trace/plot update
This commit is contained in:
parent
d86609d8b3
commit
49fcc33a6b
21
plot.py
21
plot.py
@ -1,4 +1,5 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
@ -11,15 +12,24 @@ def plottable(log: list[tuple[float, int]]):
|
||||
return np.array([[], []])
|
||||
|
||||
|
||||
def plot():
|
||||
def plot(fn: str):
|
||||
plt.rcParams['figure.figsize'] = [16, 9]
|
||||
plt.style.use("dark_background")
|
||||
plt.subplots_adjust(left=0.03, right=0.99, top=0.99, bottom=0.05)
|
||||
plt.subplots_adjust(left=0.05, right=0.99, top=0.95, bottom=0.05)
|
||||
plt.title(fn)
|
||||
plt.xlabel('time (s)')
|
||||
plt.ylabel('concurrency (1)')
|
||||
|
||||
with open('trace/latest.json') as file:
|
||||
with open(fn) as file:
|
||||
jsonified = json.load(file)
|
||||
if (log := jsonified.get('DelayedResolver:sleep:concurrency')) is not None:
|
||||
plt.plot(*plottable(log))
|
||||
if (log := jsonified.get('ActiveBinaryTree:add:concurrency')) is not None:
|
||||
plt.plot(*plottable(log))
|
||||
if (log := jsonified.get('ActiveBinaryTree:contains:concurrency')) is not None:
|
||||
plt.plot(*plottable(log))
|
||||
if (log := jsonified.get('Stack:list:concurrency')) is not None:
|
||||
plt.plot(*plottable(log))
|
||||
if (log := jsonified.get('ActiveBinaryTree:add:entry')) is not None:
|
||||
plt.scatter(*plottable(log), c='tomato', zorder=100, s=.5)
|
||||
if (log := jsonified.get('ActiveBinaryTree:add:exit')) is not None:
|
||||
@ -28,4 +38,7 @@ def plot():
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
plot()
|
||||
plot('trace/latest.json')
|
||||
for fp in list(Path('trace').glob('*.json')):
|
||||
if fp != Path('trace/latest.json'):
|
||||
plot(str(fp))
|
||||
|
@ -32,7 +32,7 @@ class Array(RecursiveMentionable, Generic[ElementType]):
|
||||
formatted = f'('
|
||||
formatted += ''.join(
|
||||
f'{tabulate(tab + 1)}{hash_point_str}'
|
||||
for hash_point_str in gather(
|
||||
for hash_point_str in await gather(
|
||||
*(
|
||||
hash_point_format(hash_point, tab + 1) for hash_point in self.array
|
||||
)
|
||||
|
4
rainbowadn/testing/instrument/__init__.py
Normal file
4
rainbowadn/testing/instrument/__init__.py
Normal file
@ -0,0 +1,4 @@
|
||||
from .concurrency import Concurrency
|
||||
from .counter import Counter
|
||||
from .entryexit import EntryExit
|
||||
from .instrumentation import Instrumentation
|
31
rainbowadn/testing/instrument/concurrency.py
Normal file
31
rainbowadn/testing/instrument/concurrency.py
Normal file
@ -0,0 +1,31 @@
|
||||
import time
|
||||
|
||||
from .instrumentation import Instrumentation
|
||||
|
||||
__all__ = ('Concurrency',)
|
||||
|
||||
|
||||
class Concurrency(Instrumentation):
|
||||
start = time.time()
|
||||
|
||||
def __init__(self, target, methodname: str):
|
||||
assert isinstance(methodname, str)
|
||||
super().__init__(target, methodname)
|
||||
self.concurrency = 0
|
||||
self.log: list[tuple[float, int]] = []
|
||||
|
||||
def time(self) -> float:
|
||||
return time.time() - self.start
|
||||
|
||||
def point(self) -> tuple[float, int]:
|
||||
return self.time(), self.concurrency
|
||||
|
||||
async def instrument(self, method, *args, **kwargs):
|
||||
self.log.append(self.point())
|
||||
self.concurrency += 1
|
||||
self.log.append(self.point())
|
||||
result = await method(*args, **kwargs)
|
||||
self.log.append(self.point())
|
||||
self.concurrency -= 1
|
||||
self.log.append(self.point())
|
||||
return result
|
14
rainbowadn/testing/instrument/counter.py
Normal file
14
rainbowadn/testing/instrument/counter.py
Normal file
@ -0,0 +1,14 @@
|
||||
from .instrumentation import Instrumentation
|
||||
|
||||
__all__ = ('Counter',)
|
||||
|
||||
|
||||
class Counter(Instrumentation):
|
||||
def __init__(self, target, methodname: str):
|
||||
assert isinstance(methodname, str)
|
||||
super().__init__(target, methodname)
|
||||
self.counter = 0
|
||||
|
||||
def instrument(self, method, *args, **kwargs):
|
||||
self.counter += 1
|
||||
return method(*args, **kwargs)
|
21
rainbowadn/testing/instrument/entryexit.py
Normal file
21
rainbowadn/testing/instrument/entryexit.py
Normal file
@ -0,0 +1,21 @@
|
||||
from typing import Callable
|
||||
|
||||
from .instrumentation import Instrumentation
|
||||
|
||||
__all__ = ('EntryExit',)
|
||||
|
||||
|
||||
class EntryExit(Instrumentation):
|
||||
def __init__(self, target, methodname: str, point: Callable[[], tuple[float, int]]):
|
||||
assert callable(point)
|
||||
assert isinstance(methodname, str)
|
||||
super().__init__(target, methodname)
|
||||
self.point = point
|
||||
self.entry_log: list[tuple[float, int]] = []
|
||||
self.exit_log: list[tuple[float, int]] = []
|
||||
|
||||
async def instrument(self, method, *args, **kwargs):
|
||||
self.entry_log.append(self.point())
|
||||
result = await method(*args, **kwargs)
|
||||
self.exit_log.append(self.point())
|
||||
return result
|
53
rainbowadn/testing/instrument/instrumentation.py
Normal file
53
rainbowadn/testing/instrument/instrumentation.py
Normal file
@ -0,0 +1,53 @@
|
||||
import functools
|
||||
from contextlib import ExitStack
|
||||
from typing import Callable, Generic, TypeVar
|
||||
|
||||
__all__ = ('Instrumentation',)
|
||||
|
||||
IType = TypeVar('IType')
|
||||
|
||||
|
||||
class Instrumentation(Generic[IType]):
|
||||
deinstrumentation = {}
|
||||
method: Callable
|
||||
wrap: Callable
|
||||
|
||||
def __init__(self, target, methodname: str):
|
||||
assert isinstance(methodname, str)
|
||||
assert callable(getattr(target, methodname))
|
||||
self.target = target
|
||||
self.methodname = methodname
|
||||
|
||||
def instrument(self, method, *args, **kwargs):
|
||||
raise NotImplementedError
|
||||
|
||||
def __enter__(self: IType) -> IType:
|
||||
assert not hasattr(self, 'method')
|
||||
self.method = getattr(self.target, self.methodname)
|
||||
assert callable(self.method)
|
||||
|
||||
@functools.wraps(self.method)
|
||||
def wrap(*args, **kwargs):
|
||||
return self.instrument(self.method, *args, **kwargs)
|
||||
|
||||
self.wrap = wrap
|
||||
|
||||
setattr(self.target, self.methodname, self.wrap)
|
||||
|
||||
return self
|
||||
|
||||
def schedule_deinstrumentation(self):
|
||||
self.deinstrumentation[self.wrap] = self.method
|
||||
del self.wrap
|
||||
del self.method
|
||||
|
||||
def deinstrument(self):
|
||||
while (method := getattr(self.target, self.methodname)) in self.deinstrumentation:
|
||||
setattr(self.target, self.methodname, self.deinstrumentation.pop(method))
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.schedule_deinstrumentation()
|
||||
self.deinstrument()
|
||||
|
||||
def enter(self: IType, stack: ExitStack) -> IType:
|
||||
return stack.enter_context(self)
|
@ -1,107 +0,0 @@
|
||||
import functools
|
||||
import time
|
||||
from contextlib import ExitStack
|
||||
from typing import Callable, Generic, TypeVar
|
||||
|
||||
__all__ = ('Instrumentation', 'Counter', 'Concurrency', 'EntryExit',)
|
||||
|
||||
IType = TypeVar('IType')
|
||||
|
||||
|
||||
class Instrumentation(Generic[IType]):
|
||||
deinstrumentation = {}
|
||||
method: Callable
|
||||
wrap: Callable
|
||||
|
||||
def __init__(self, target, methodname: str):
|
||||
assert isinstance(methodname, str)
|
||||
assert callable(getattr(target, methodname))
|
||||
self.target = target
|
||||
self.methodname = methodname
|
||||
|
||||
def instrument(self, method, *args, **kwargs):
|
||||
raise NotImplementedError
|
||||
|
||||
def __enter__(self: IType) -> IType:
|
||||
assert not hasattr(self, 'method')
|
||||
self.method = getattr(self.target, self.methodname)
|
||||
assert callable(self.method)
|
||||
|
||||
@functools.wraps(self.method)
|
||||
def wrap(*args, **kwargs):
|
||||
return self.instrument(self.method, *args, **kwargs)
|
||||
|
||||
self.wrap = wrap
|
||||
|
||||
setattr(self.target, self.methodname, self.wrap)
|
||||
|
||||
return self
|
||||
|
||||
def schedule_deinstrumentation(self):
|
||||
self.deinstrumentation[self.wrap] = self.method
|
||||
del self.wrap
|
||||
del self.method
|
||||
|
||||
def deinstrument(self):
|
||||
while (method := getattr(self.target, self.methodname)) in self.deinstrumentation:
|
||||
setattr(self.target, self.methodname, self.deinstrumentation.pop(method))
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.schedule_deinstrumentation()
|
||||
self.deinstrument()
|
||||
|
||||
def enter(self: IType, stack: ExitStack) -> IType:
|
||||
return stack.enter_context(self)
|
||||
|
||||
|
||||
class Counter(Instrumentation):
|
||||
def __init__(self, target, methodname: str):
|
||||
assert isinstance(methodname, str)
|
||||
super().__init__(target, methodname)
|
||||
self.counter = 0
|
||||
|
||||
def instrument(self, method, *args, **kwargs):
|
||||
self.counter += 1
|
||||
return method(*args, **kwargs)
|
||||
|
||||
|
||||
class Concurrency(Instrumentation):
|
||||
start = time.time()
|
||||
|
||||
def __init__(self, target, methodname: str):
|
||||
assert isinstance(methodname, str)
|
||||
super().__init__(target, methodname)
|
||||
self.concurrency = 0
|
||||
self.log: list[tuple[float, int]] = []
|
||||
|
||||
def time(self) -> float:
|
||||
return time.time() - self.start
|
||||
|
||||
def point(self) -> tuple[float, int]:
|
||||
return self.time(), self.concurrency
|
||||
|
||||
async def instrument(self, method, *args, **kwargs):
|
||||
self.log.append(self.point())
|
||||
self.concurrency += 1
|
||||
self.log.append(self.point())
|
||||
result = await method(*args, **kwargs)
|
||||
self.log.append(self.point())
|
||||
self.concurrency -= 1
|
||||
self.log.append(self.point())
|
||||
return result
|
||||
|
||||
|
||||
class EntryExit(Instrumentation):
|
||||
def __init__(self, target, methodname: str, point: Callable[[], tuple[float, int]]):
|
||||
assert callable(point)
|
||||
assert isinstance(methodname, str)
|
||||
super().__init__(target, methodname)
|
||||
self.point = point
|
||||
self.entry_log: list[tuple[float, int]] = []
|
||||
self.exit_log: list[tuple[float, int]] = []
|
||||
|
||||
async def instrument(self, method, *args, **kwargs):
|
||||
self.entry_log.append(self.point())
|
||||
result = await method(*args, **kwargs)
|
||||
self.exit_log.append(self.point())
|
||||
return result
|
@ -16,7 +16,7 @@ from rainbowadn.encryption import *
|
||||
from rainbowadn.nullability import *
|
||||
from rainbowadn.v13 import *
|
||||
from rainbowadn.wrisbt import *
|
||||
from .instrumentation import *
|
||||
from .instrument import *
|
||||
from .resolvers import *
|
||||
|
||||
|
||||
@ -83,10 +83,10 @@ class TestAll(unittest.IsolatedAsyncioTestCase):
|
||||
stoptime = now
|
||||
return delta
|
||||
|
||||
n = 2500
|
||||
n = 5000
|
||||
keysize = 7
|
||||
with self.subTest('create empty'):
|
||||
btree: WrisbtRoot = WrisbtRoot.empty(WrisbtParametres(1, keysize))
|
||||
btree: WrisbtRoot = WrisbtRoot.empty(WrisbtParametres(5, keysize))
|
||||
measure('init')
|
||||
with self.subTest('add keys', n=n):
|
||||
for _ in range(n):
|
||||
@ -158,7 +158,7 @@ class TestAll(unittest.IsolatedAsyncioTestCase):
|
||||
|
||||
async def test_encryption(self):
|
||||
set_gather_linear()
|
||||
instrumentation = Counter(Encrypted, 'encrypt')
|
||||
encrypt_ctr = Counter(Encrypted, 'encrypt')
|
||||
with self.subTest('setup'):
|
||||
key = b'a' * 32
|
||||
with self.subTest('create empty'):
|
||||
@ -171,9 +171,9 @@ class TestAll(unittest.IsolatedAsyncioTestCase):
|
||||
print(await tree.reference.str(0))
|
||||
with self.subTest('encrypt'):
|
||||
target = tree.reference
|
||||
with instrumentation:
|
||||
with encrypt_ctr:
|
||||
target = (await Encrypted.encrypt(target, key)).decrypted
|
||||
print(instrumentation.counter)
|
||||
print(encrypt_ctr.counter)
|
||||
tree = tree.create(target)
|
||||
print(await tree.reference.str(0))
|
||||
with self.subTest('alter'):
|
||||
@ -182,13 +182,13 @@ class TestAll(unittest.IsolatedAsyncioTestCase):
|
||||
print(await tree.reference.str(0))
|
||||
with self.subTest('encrypt and migrate'):
|
||||
target = tree.reference
|
||||
with instrumentation:
|
||||
with encrypt_ctr:
|
||||
eeed = await Encrypted.encrypt(target, key)
|
||||
print(instrumentation.counter)
|
||||
print(encrypt_ctr.counter)
|
||||
print(await (await self.dr().migrate_resolved(eeed)).decrypted.str(0))
|
||||
with self.subTest('re-encrypt'):
|
||||
new_key = b'b' * 32
|
||||
target = eeed.decrypted
|
||||
with instrumentation:
|
||||
with encrypt_ctr:
|
||||
await Encrypted.encrypt(target, new_key)
|
||||
print(instrumentation.counter)
|
||||
print(encrypt_ctr.counter)
|
||||
|
53
trace.py
53
trace.py
@ -10,10 +10,11 @@ from nacl.signing import SigningKey
|
||||
|
||||
from plot import plot
|
||||
from rainbowadn.chain import *
|
||||
from rainbowadn.collection.linear import *
|
||||
from rainbowadn.collection.trees.binary import *
|
||||
from rainbowadn.core import *
|
||||
from rainbowadn.nullability import *
|
||||
from rainbowadn.testing.instrumentation import *
|
||||
from rainbowadn.testing.instrument import *
|
||||
from rainbowadn.testing.resolvers import *
|
||||
from rainbowadn.v13 import *
|
||||
|
||||
@ -33,11 +34,11 @@ def target_str(target) -> str:
|
||||
return name
|
||||
|
||||
|
||||
def jsonify(instrumentation: Instrumentation) -> dict:
|
||||
prefix = f'{target_str(instrumentation.target)}:{instrumentation.methodname}'
|
||||
match instrumentation:
|
||||
case Counter(counter=counter):
|
||||
return {f'{prefix}:counter': counter}
|
||||
def jsonify(dumped: Instrumentation) -> dict:
|
||||
prefix = f'{target_str(dumped.target)}:{dumped.methodname}'
|
||||
match dumped:
|
||||
case Counter(counter=ctr):
|
||||
return {f'{prefix}:counter': ctr}
|
||||
case Concurrency(log=log):
|
||||
return {f'{prefix}:concurrency': log}
|
||||
case EntryExit(entry_log=entry_log, exit_log=exit_log):
|
||||
@ -49,9 +50,7 @@ def jsonify(instrumentation: Instrumentation) -> dict:
|
||||
return {}
|
||||
|
||||
|
||||
async def main():
|
||||
set_gather_linear()
|
||||
bank: BankChain = BankChain.empty(ReductionChainMetaFactory().loose())
|
||||
async def mock(bank: BankChain) -> BankChain:
|
||||
key_0 = SigningKey.generate()
|
||||
transaction_0 = Transaction.make(
|
||||
[],
|
||||
@ -69,6 +68,24 @@ async def main():
|
||||
),
|
||||
]
|
||||
)
|
||||
return bank
|
||||
|
||||
|
||||
def get_instrumentations() -> list[Instrumentation]:
|
||||
sleep_cc = Concurrency(DelayedResolver, 'sleep')
|
||||
return [
|
||||
sleep_cc,
|
||||
EntryExit(ActiveBinaryTree, 'add', sleep_cc.point),
|
||||
Concurrency(ActiveBinaryTree, 'add'),
|
||||
Concurrency(ActiveBinaryTree, 'contains'),
|
||||
Concurrency(Stack, 'list'),
|
||||
]
|
||||
|
||||
|
||||
async def main():
|
||||
set_gather_linear()
|
||||
bank: BankChain = BankChain.empty(ReductionChainMetaFactory().loose())
|
||||
# bank = await mock(bank)
|
||||
for _ in range(16):
|
||||
bank = await bank.adds(
|
||||
[
|
||||
@ -87,19 +104,27 @@ async def main():
|
||||
)
|
||||
print('saved')
|
||||
set_gather_asyncio()
|
||||
with ExitStack() as stack:
|
||||
sleep_cc = Concurrency(DelayedResolver, 'sleep').enter(stack)
|
||||
avl_ex = EntryExit(ActiveBinaryTree, 'add', sleep_cc.point).enter(stack)
|
||||
with ExitStack() as estack:
|
||||
instrumentations = get_instrumentations()
|
||||
for stacked in instrumentations:
|
||||
stacked.enter(estack)
|
||||
assert_true(await bank.verify())
|
||||
print(Instrumentation.deinstrumentation)
|
||||
print('traced')
|
||||
fn = f'trace/{int(time.time())}-{os.urandom(2).hex()}.json'
|
||||
jsonified = {}
|
||||
for dumped in instrumentations:
|
||||
jsonified |= jsonify(dumped)
|
||||
with open(fn, 'w') as file:
|
||||
json.dump(
|
||||
jsonify(sleep_cc) | jsonify(avl_ex),
|
||||
jsonified,
|
||||
file
|
||||
)
|
||||
print('dumped')
|
||||
shutil.copy(fn, f'trace/latest.json')
|
||||
plot()
|
||||
print('copied')
|
||||
plot(fn)
|
||||
print('plotted')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Loading…
Reference in New Issue
Block a user