trace/plot update

This commit is contained in:
AF 2022-06-24 00:26:43 +03:00
parent d86609d8b3
commit 49fcc33a6b
10 changed files with 190 additions and 136 deletions

21
plot.py
View File

@ -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))

View File

@ -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
)

View File

@ -0,0 +1,4 @@
from .concurrency import Concurrency
from .counter import Counter
from .entryexit import EntryExit
from .instrumentation import Instrumentation

View 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

View 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)

View 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

View 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)

View File

@ -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

View File

@ -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)

View File

@ -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__':