Tidied up load dist code.

This commit is contained in:
Michael Whittaker 2021-01-29 00:01:47 -08:00
parent cb8c5d0e6c
commit 31e875f957
4 changed files with 110 additions and 52 deletions

50
plot_load_distribution.py Normal file
View file

@ -0,0 +1,50 @@
from quorums import *
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
def main():
a = Node('a', capacity=100)
b = Node('b', capacity=200)
c = Node('c', capacity=100)
d = Node('d', capacity=200)
e = Node('e', capacity=100)
nodes = [a, b, c, d, e]
quorum_systems = {
'majority': QuorumSystem(reads=majority([a, b, c, d, e])),
'crumbling_walls': QuorumSystem(reads=a*b + c*d*e),
'paths': QuorumSystem(reads=a*b + a*c*e + d*e + d*c*b),
}
for name, qs in quorum_systems.items():
d = {0.0: 1, 0.1: 1, 0.2: 1, 0.3: 1, 0.4: 1, 0.5: 1,
0.6: 1, 0.7: 1, 0.8: 1, 0.9: 1, 1.0: 1}
fig, axes = plt.subplots(3, 4, figsize=(6 * 4, 4 * 3), sharey='all')
axes_iter = (axes[row][col] for row in range(3) for col in range(4))
for fr in d.keys():
sigma = qs.strategy(read_fraction=fr)
ax = next(axes_iter)
plot_load_distribution_on(ax, sigma, nodes)
ax.set_title(f'Optimized For Read Fraction = {fr}')
ax.set_xlabel('Read Fraction')
ax.legend()
sigma = qs.strategy(read_fraction=d)
ax = next(axes_iter)
plot_load_distribution_on(ax, sigma, nodes)
ax.set_title('Optimized For Uniform Read Fraction')
ax.set_xlabel('Read Fraction')
ax.legend()
axes[0][0].set_ylabel('Load')
axes[1][0].set_ylabel('Load')
axes[2][0].set_ylabel('Load')
fig.tight_layout()
fig.savefig(f'{name}.pdf')
if __name__ == '__main__':
main()

View file

@ -7,4 +7,6 @@ from .viz import (
plot_node_utilization_on, plot_node_utilization_on,
plot_node_throughput, plot_node_throughput,
plot_node_throughput_on, plot_node_throughput_on,
plot_load_distribution,
plot_load_distribution_on,
) )

View file

@ -6,9 +6,6 @@ from .geometry import Point, Segment
from typing import Dict, Generic, List, Optional, Set, Tuple, TypeVar from typing import Dict, Generic, List, Optional, Set, Tuple, TypeVar
import collections import collections
import itertools import itertools
import math
import matplotlib
import matplotlib.pyplot as plt
import numpy as np import numpy as np
@ -87,54 +84,6 @@ class Strategy(Generic[T]):
-> float: -> float:
return 1 / self.load(read_fraction, write_fraction) return 1 / self.load(read_fraction, write_fraction)
def _group(self, segments: List[Tuple[Segment, T]]) -> Dict[Segment, List[T]]:
groups: Dict[Segment, List[T]] = collections.defaultdict(list)
for segment, x in segments:
match_found = False
for other, xs in groups.items():
if segment.approximately_equal(other):
xs.append(x)
match_found = True
break
if not match_found:
groups[segment].append(x)
return groups
def plot_load_distribution_on(self,
ax: plt.Axes,
nodes: Optional[List[Node[T]]] = None) \
-> None:
nodes = nodes or list(self.nodes)
# We want to plot every node's load distribution. Multiple nodes might
# have the same load distribution, so we group the nodes by their
# distribution. The grouping is a little annoying because two floats
# might not be exactly equal but pretty close.
groups = self._group([
(Segment(Point(0, self.node_load(node, read_fraction=0)),
Point(1, self.node_load(node, read_fraction=1))), node.x)
for node in nodes
])
# Compute and plot the max of all segments. We increase the line
# slightly so it doesn't overlap with the other lines.
path = geometry.max_of_segments(list(groups.keys()))
ax.plot([p[0] for p in path],
[p[1] for p in path],
label='load',
linewidth=4)
for segment, xs in groups.items():
ax.plot([segment.l.x, segment.r.x],
[segment.l.y, segment.r.y],
'--',
label=','.join(str(x) for x in xs),
linewidth=2,
alpha=0.75)
def _node_load(self, x: T, fr: float) -> float: def _node_load(self, x: T, fr: float) -> float:
""" """
_node_load returns the load on x given a fixed read fraction fr. _node_load returns the load on x given a fixed read fraction fr.

View file

@ -1,8 +1,11 @@
from . import distribution from . import distribution
from . import geometry
from .distribution import Distribution from .distribution import Distribution
from .expr import Node from .expr import Node
from .geometry import Point, Segment
from .strategy import Strategy from .strategy import Strategy
from typing import List, Optional, Set, TypeVar from typing import Dict, List, Optional, Set, Tuple, TypeVar
import collections
import matplotlib import matplotlib
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
@ -146,3 +149,57 @@ def _plot_node_load_on(ax: plt.Axes,
write_capacities, matplotlib.cm.get_cmap('Blues')) write_capacities, matplotlib.cm.get_cmap('Blues'))
ax.set_xticks(x_ticks) ax.set_xticks(x_ticks)
ax.set_xticklabels(str(x) for x in x_list) ax.set_xticklabels(str(x) for x in x_list)
def plot_load_distribution(filename: str,
strategy: Strategy[T],
nodes: Optional[List[Node[T]]] = None):
fig, ax = plt.subplots()
plot_load_distribution_on(ax, strategy, nodes)
ax.set_xlabel('Read Fraction')
ax.set_ylabel('Load')
fig.tight_layout()
fig.savefig(filename)
def _group(segments: Dict[T, Segment]) -> Dict[Segment, List[T]]:
groups: Dict[Segment, List[T]] = collections.defaultdict(list)
for x, segment in segments.items():
matches = (s for s in groups if segment.approximately_equal(s))
groups[next(matches, segment)].append(x)
return groups
def plot_load_distribution_on(ax: plt.Axes,
strategy: Strategy[T],
nodes: Optional[List[Node[T]]] = None):
nodes = nodes or list(strategy.nodes)
# We want to plot every node's load distribution. Multiple nodes might
# have the same load distribution, so we group the nodes by their
# distribution. The grouping is a little annoying because two floats
# might not be exactly equal but pretty close.
groups = _group({
node.x: Segment(
Point(0, strategy.node_load(node, read_fraction=0)),
Point(1, strategy.node_load(node, read_fraction=1))
)
for node in nodes
})
# Compute and plot the max of all segments. We plot the load first so that
# it lies underneath the node loads.
path = geometry.max_of_segments(list(groups.keys()))
ax.plot([p[0] for p in path],
[p[1] for p in path],
label='load',
linewidth=4)
# We plot the node loads second so that they appear above the load.
for segment, xs in groups.items():
ax.plot([segment.l.x, segment.r.x],
[segment.l.y, segment.r.y],
'--',
label=','.join(str(x) for x in xs),
linewidth=2,
alpha=0.75)