diff --git a/benchmarks/bench_report.nim b/benchmarks/bench_report.nim
new file mode 100644
index 000000000..6842315e8
--- /dev/null
+++ b/benchmarks/bench_report.nim
@@ -0,0 +1,48 @@
+# Constantine
+# Copyright (c) 2018-2019 Status Research & Development GmbH
+# Copyright (c) 2020-Present Mamy Andre-Ratsimbazafy
+# Licensed and distributed under either of
+# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
+# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
+# at your option. This file may not be copied, modified, or distributed except according to those terms.
+
+import std/strutils
+
+var printedMarkdownHeaders: seq[string]
+
+func markdownEscape*(cell: string): string =
+ cell.multiReplace(
+ ("\\", "\\\\"),
+ ("|", "\\|"),
+ ("\r", ""),
+ ("\n", "
")
+ )
+
+func markdownTableHeader*(headers: openArray[string]): string =
+ result = "|"
+ for header in headers:
+ result.add " " & markdownEscape(header) & " |"
+
+func markdownTableSeparator*(columns: int): string =
+ result = "|"
+ for _ in 0 ..< columns:
+ result.add " --- |"
+
+func markdownTableRow*(cells: openArray[string]): string =
+ result = "|"
+ for cell in cells:
+ result.add " " & markdownEscape(cell) & " |"
+
+proc reportMarkdownTable*(headers, cells: openArray[string]) =
+ doAssert headers.len == cells.len
+
+ let key = headers.join("\t")
+ for printedHeader in printedMarkdownHeaders:
+ if printedHeader == key:
+ echo markdownTableRow(cells)
+ return
+
+ echo markdownTableHeader(headers)
+ echo markdownTableSeparator(headers.len)
+ printedMarkdownHeaders.add key
+ echo markdownTableRow(cells)
diff --git a/benchmarks/bench_summary_template.nim b/benchmarks/bench_summary_template.nim
index 4c32ec808..b3b35adb0 100644
--- a/benchmarks/bench_summary_template.nim
+++ b/benchmarks/bench_summary_template.nim
@@ -29,6 +29,7 @@ import
constantine/hash_to_curve/hash_to_curve,
# Helpers
helpers/prng_unsafe,
+ ./bench_report,
./bench_blueprint
export
@@ -45,9 +46,15 @@ proc report(op, domain: string, start, stop: MonoTime, startClk, stopClk: int64,
let ns = inNanoseconds((stop-start) div iters)
let throughput = 1e9 / float64(ns)
when SupportsGetTicks:
- echo &"{op:<35} {domain:<40} {throughput:>15.3f} ops/s {ns:>9} ns/op {(stopClk - startClk) div iters:>9} CPU cycles (approx)"
+ reportMarkdownTable(
+ ["Operation", "Domain", "Throughput", "Latency", "CPU cycles (approx)"],
+ [op, domain, &"{throughput:.3f} ops/s", &"{ns} ns/op", $((stopClk - startClk) div iters)]
+ )
else:
- echo &"{op:<35} {domain:<40} {throughput:>15.3f} ops/s {ns:>9} ns/op"
+ reportMarkdownTable(
+ ["Operation", "Domain", "Throughput", "Latency"],
+ [op, domain, &"{throughput:.3f} ops/s", &"{ns} ns/op"]
+ )
macro fixEllipticDisplay(T: typedesc): untyped =
# At compile-time, enums are integers and their display is buggy
diff --git a/constantine.nimble b/constantine.nimble
index 8e0202c79..f8cf17504 100644
--- a/constantine.nimble
+++ b/constantine.nimble
@@ -410,6 +410,7 @@ const testDesc: seq[tuple[path: string, useGMP: bool]] = @[
# KDF
# ----------------------------------------------------------
("tests/t_kdf_hkdf.nim", false),
+ ("tests/t_bench_report.nim", false),
# Primitives
# ----------------------------------------------------------
diff --git a/tests/t_bench_report.nim b/tests/t_bench_report.nim
new file mode 100644
index 000000000..b8db40d5d
--- /dev/null
+++ b/tests/t_bench_report.nim
@@ -0,0 +1,19 @@
+# Constantine
+# Copyright (c) 2018-2019 Status Research & Development GmbH
+# Copyright (c) 2020-Present Mamy Andre-Ratsimbazafy
+# Licensed and distributed under either of
+# * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT).
+# * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0).
+# at your option. This file may not be copied, modified, or distributed except according to those terms.
+
+import
+ std/unittest,
+ ../benchmarks/bench_report
+
+suite "Benchmark Markdown reports":
+ test "renders a Markdown table header":
+ check markdownTableHeader(["Operation", "Domain"]) == "| Operation | Domain |"
+ check markdownTableSeparator(2) == "| --- | --- |"
+
+ test "escapes Markdown table cells":
+ check markdownTableRow(["A | B", "line 1\nline 2"]) == "| A \\| B | line 1
line 2 |"