aboutsummaryrefslogtreecommitdiffhomepage
path: root/test/ec
diff options
context:
space:
mode:
authorJ08nY2021-01-30 18:30:55 +0100
committerJ08nY2021-01-30 18:40:43 +0100
commitf94d63b3b84fde4a2a9004ba0afc6693f5ba4916 (patch)
treeb5c14211b48b0e5ea4f4cc122a4e1e10986249b0 /test/ec
parent28546dad01a25ce101d6b49924f521c2ef5ffa98 (diff)
downloadpyecsca-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-xtest/ec/perf_formula.py43
-rwxr-xr-xtest/ec/perf_mod.py64
-rwxr-xr-xtest/ec/perf_mult.py41
-rw-r--r--test/ec/utils.py58
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