diff options
| author | J08nY | 2021-01-30 18:30:55 +0100 |
|---|---|---|
| committer | J08nY | 2021-01-30 18:40:43 +0100 |
| commit | f94d63b3b84fde4a2a9004ba0afc6693f5ba4916 (patch) | |
| tree | b5c14211b48b0e5ea4f4cc122a4e1e10986249b0 /test/ec | |
| parent | 28546dad01a25ce101d6b49924f521c2ef5ffa98 (diff) | |
| download | pyecsca-f94d63b3b84fde4a2a9004ba0afc6693f5ba4916.tar.gz pyecsca-f94d63b3b84fde4a2a9004ba0afc6693f5ba4916.tar.zst pyecsca-f94d63b3b84fde4a2a9004ba0afc6693f5ba4916.zip | |
Add performance monitoring and a few improvements to Mod.
Diffstat (limited to 'test/ec')
| -rwxr-xr-x | test/ec/perf_formula.py | 43 | ||||
| -rwxr-xr-x | test/ec/perf_mod.py | 64 | ||||
| -rwxr-xr-x | test/ec/perf_mult.py | 41 | ||||
| -rw-r--r-- | test/ec/utils.py | 58 |
4 files changed, 206 insertions, 0 deletions
diff --git a/test/ec/perf_formula.py b/test/ec/perf_formula.py new file mode 100755 index 0000000..9e651ae --- /dev/null +++ b/test/ec/perf_formula.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +import click + +from pyecsca.ec.mod import has_gmp +from pyecsca.ec.params import get_params +from pyecsca.misc.cfg import TemporaryConfig +from utils import Profiler + + +@click.command() +@click.option("-p", "--profiler", type=click.Choice(("py", "c")), default="py") +@click.option("-m", "--mod", type=click.Choice(("python", "gmp")), default="gmp" if has_gmp else "python") +@click.option("-o", "--operations", type=click.INT, default=5000) +@click.option("-d", "--directory", type=click.Path(file_okay=False, dir_okay=True), default=None, envvar="DIR") +def main(profiler, mod, operations, directory): + with TemporaryConfig() as cfg: + cfg.ec.mod_implementation = mod + p256 = get_params("secg", "secp256r1", "projective") + coords = p256.curve.coordinate_model + add = coords.formulas["add-2016-rcb"] + dbl = coords.formulas["dbl-2016-rcb"] + click.echo(f"Profiling {operations} {p256.curve.prime.bit_length()}-bit doubling formula executions...") + one_point = p256.generator + with Profiler(profiler, directory, f"formula_dbl2016rcb_p256_{operations}_{mod}"): + for _ in range(operations): + one_point = dbl(p256.curve.prime, one_point, **p256.curve.parameters)[0] + click.echo(f"Profiling {operations} {p256.curve.prime.bit_length()}-bit addition formula executions...") + other_point = p256.generator + with Profiler(profiler, directory, f"formula_add2016rcb_p256_{operations}_{mod}"): + for _ in range(operations): + one_point = add(p256.curve.prime, one_point, other_point, **p256.curve.parameters)[0] + ed25519 = get_params("other", "Ed25519", "extended") + ecoords = ed25519.curve.coordinate_model + dblg = ecoords.formulas["mdbl-2008-hwcd"] + click.echo(f"Profiling {operations} {ed25519.curve.prime.bit_length()}-bit doubling formula executions (with assumption)...") + eone_point = ed25519.generator + with Profiler(profiler, directory, f"formula_mdbl2008hwcd_ed25519_{operations}_{mod}"): + for _ in range(operations): + dblg(ed25519.curve.prime, eone_point, **ed25519.curve.parameters) + + +if __name__ == "__main__": + main() diff --git a/test/ec/perf_mod.py b/test/ec/perf_mod.py new file mode 100755 index 0000000..37cf41b --- /dev/null +++ b/test/ec/perf_mod.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +import click + +from pyecsca.ec.mod import Mod, has_gmp +from pyecsca.misc.cfg import TemporaryConfig +from utils import Profiler + + +@click.command() +@click.option("-p", "--profiler", type=click.Choice(("py", "c")), default="py") +@click.option("-m", "--mod", type=click.Choice(("python", "gmp")), default="gmp" if has_gmp else "python") +@click.option("-o", "--operations", type=click.INT, default=100000) +@click.option("-d", "--directory", type=click.Path(file_okay=False, dir_okay=True), default=None, envvar="DIR") +def main(profiler, mod, operations, directory): + with TemporaryConfig() as cfg: + cfg.ec.mod_implementation = mod + n = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff + a = Mod(0x11111111111111111111111111111111, n) + b = Mod(0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb, n) + click.echo(f"Profiling {operations} {n.bit_length()}-bit modular inverse...") + with Profiler(profiler, directory, f"mod_256b_inverse_{operations}_{mod}"): + for _ in range(operations): + a.inverse() + click.echo(f"Profiling {operations} {n.bit_length()}-bit modular square root...") + with Profiler(profiler, directory, f"mod_256b_sqrt_{operations}_{mod}"): + for _ in range(operations): + a.sqrt() + click.echo(f"Profiling {operations} {n.bit_length()}-bit modular multiply...") + c = a + with Profiler(profiler, directory, f"mod_256b_multiply_{operations}_{mod}"): + for _ in range(operations): + c = c * b + click.echo(f"Profiling {operations} {n.bit_length()}-bit constant modular multiply...") + c = a + with Profiler(profiler, directory, f"mod_256b_constmultiply_{operations}_{mod}"): + for _ in range(operations): + c = c * 48006 + click.echo(f"Profiling {operations} {n.bit_length()}-bit modular square...") + c = a + with Profiler(profiler, directory, f"mod_256b_square_{operations}_{mod}"): + for _ in range(operations): + c = c**2 + click.echo(f"Profiling {operations} {n.bit_length()}-bit modular add...") + c = a + with Profiler(profiler, directory, f"mod_256b_add_{operations}_{mod}"): + for _ in range(operations): + c = c + b + click.echo(f"Profiling {operations} {n.bit_length()}-bit modular subtract...") + c = a + with Profiler(profiler, directory, f"mod_256b_subtract_{operations}_{mod}"): + for _ in range(operations): + c = c - b + click.echo(f"Profiling {operations} {n.bit_length()}-bit modular quadratic residue checks...") + with Profiler(profiler, directory, f"mod_256b_isresidue_{operations}_{mod}"): + for _ in range(operations): + a.is_residue() + click.echo(f"Profiling {operations} {n.bit_length()}-bit modular random...") + with Profiler(profiler, directory, f"mod_256b_random_{operations}_{mod}"): + for _ in range(operations): + Mod.random(n) + + +if __name__ == "__main__": + main() diff --git a/test/ec/perf_mult.py b/test/ec/perf_mult.py new file mode 100755 index 0000000..2ec82b0 --- /dev/null +++ b/test/ec/perf_mult.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +import click + +from pyecsca.ec.mod import has_gmp +from pyecsca.ec.mult import LTRMultiplier +from pyecsca.ec.params import get_params +from pyecsca.misc.cfg import TemporaryConfig +from utils import Profiler + + +@click.command() +@click.option("-p", "--profiler", type=click.Choice(("py", "c")), default="py") +@click.option("-m", "--mod", type=click.Choice(("python", "gmp")), default="gmp" if has_gmp else "python") +@click.option("-o", "--operations", type=click.INT, default=50) +@click.option("-d", "--directory", type=click.Path(file_okay=False, dir_okay=True), default=None, envvar="DIR") +def main(profiler, mod, operations, directory): + with TemporaryConfig() as cfg: + cfg.ec.mod_implementation = mod + p256 = get_params("secg", "secp256r1", "projective") + coords = p256.curve.coordinate_model + add = coords.formulas["add-2016-rcb"] + dbl = coords.formulas["dbl-2016-rcb"] + mult = LTRMultiplier(add, dbl) + click.echo(f"Profiling {operations} {p256.curve.prime.bit_length()}-bit scalar multiplication executions...") + one_point = p256.generator + with Profiler(profiler, directory, f"mult_ltr_rcb_p256_{operations}_{mod}"): + for _ in range(operations): + mult.init(p256, one_point) + one_point = mult.multiply(0x71a55e0c1abb3a0e069419e0f837bc195f1b9545e69fc51e53c4d48d7fea3b1a) + # ed25519 = get_params("other", "Ed25519", "extended") + # ecoords = ed25519.curve.coordinate_model + # dblg = ecoords.formulas["mdbl-2008-hwcd"] + # click.echo(f"Profiling {operations} {ed25519.curve.prime.bit_length()}-bit doubling formula executions (with assumption)...") + # eone_point = ed25519.generator + # with Profiler(profiler) as pr: + # for _ in range(operations): + # dblg(ed25519.curve.prime, eone_point, **ed25519.curve.parameters) + + +if __name__ == "__main__": + main() diff --git a/test/ec/utils.py b/test/ec/utils.py index 67a9cc0..6429ac9 100644 --- a/test/ec/utils.py +++ b/test/ec/utils.py @@ -1,5 +1,12 @@ +import pstats +import sys + +from pathlib import Path +from subprocess import run, PIPE, DEVNULL from itertools import product from functools import reduce +from pyinstrument import Profiler as PyProfiler +from cProfile import Profile as cProfiler def slow(func): @@ -10,3 +17,54 @@ def slow(func): def cartesian(*items): for cart in product(*items): yield reduce(lambda x, y: x + y, cart) + + +class Profiler(object): + def __init__(self, prof_type, output_directory, benchmark_name): + self._prof = PyProfiler() if prof_type == "py" else cProfiler() + self._prof_type = prof_type + self._root_frame = None + self._state = None + self._output_directory = output_directory + self._benchmark_name = benchmark_name + + def __enter__(self): + self._prof.__enter__() + self._state = "in" + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self._prof.__exit__(exc_type, exc_val, exc_tb) + if self._prof_type == "py": + self._root_frame = self._prof.last_session.root_frame() + self._state = "out" + self.output() + self.save() + + def save(self): + if self._state != "out": + raise ValueError + if self._output_directory is None or self._benchmark_name is None: + return + git_commit = run(["git", "rev-parse", "--short", "HEAD"], stdout=PIPE, stderr=DEVNULL).stdout.strip().decode() + git_dirty = run(["git", "diff", "--quiet"], stdout=DEVNULL, stderr=DEVNULL).returncode != 0 + version = git_commit + ("-dirty" if git_dirty else "") + output_path = Path(self._output_directory) / (self._benchmark_name + ".csv") + with output_path.open("a") as f: + f.write(f"{version},{'.'.join(map(str, sys.version_info[:3]))},{self.get_time()}\n") + + def output(self): + if self._state != "out": + raise ValueError + if self._prof_type == "py": + print(self._prof.output_text(unicode=True, color=True)) + else: + self._prof.print_stats("cumtime") + + def get_time(self): + if self._state != "out": + raise ValueError + if self._prof_type == "py": + return self._root_frame.time() + else: + return pstats.Stats(self._prof).total_tt |
