Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save dustinhartlyn/798c6d0514e03146306524911b497435 to your computer and use it in GitHub Desktop.

Select an option

Save dustinhartlyn/798c6d0514e03146306524911b497435 to your computer and use it in GitHub Desktop.
Benchmark for FreeCAD PR: analytic line/line intersection in WireJoiner.
"""
Benchmark for FreeCAD PR: analytic line/line intersection in WireJoiner.
It builds a sketch of crossing straight lines (N horizontal + N vertical, so
N*N interior crossings) and times a recompute. That recompute runs the shape
build, which is where WireJoiner::splitEdges() does its work, and a grid of
crossing lines is the case that path is most expensive for.
How to run
----------
Linux / macOS:
freecadcmd gist_bench_wirejoiner.py
(results print to the console)
Windows:
FreeCADCmd's stdout is detached, so set an output file and read it:
set BENCH_OUT=C:\\path\\to\\results.txt
FreeCADCmd.exe gist_bench_wirejoiner.py
type C:\\path\\to\\results.txt
How I measured
--------------
Two builds of the same commit, release (-O2), same machine, differing only in
WireJoiner.cpp. Baseline was FreeCAD main; the other build added the change.
Median of 9 runs per size. Note this times a whole recompute, so splitEdges'
own share of the improvement is larger than the recompute-level number.
"""
import os
import sys
import time
import statistics
import FreeCAD as App
import Part
import Sketcher
_out_path = os.environ.get("BENCH_OUT")
_out_file = open(_out_path, "w") if _out_path else None
def emit(line):
print(line)
if _out_file:
_out_file.write(line + "\n")
_out_file.flush()
def V(x, y):
return App.Vector(x, y, 0)
def build_grid(n, span=100.0):
doc = App.newDocument("WJ%d" % n)
sk = doc.addObject("Sketcher::SketchObject", "S")
geo = []
step = span / (n + 1)
for i in range(1, n + 1):
geo.append(Part.LineSegment(V(0.0, i * step), V(span, i * step)))
for i in range(1, n + 1):
geo.append(Part.LineSegment(V(i * step, 0.0), V(i * step, span)))
sk.addGeometry(geo, False)
return doc, sk
def median_ms(fn, reps):
ts = []
for _ in range(reps):
t0 = time.perf_counter()
fn()
ts.append((time.perf_counter() - t0) * 1000.0)
return statistics.median(ts), min(ts), max(ts)
emit("FreeCAD " + " ".join(App.Version()[:4]))
emit("")
emit("%-6s %-10s %-12s %-12s %-12s" % ("N", "edges", "median_ms", "min_ms", "max_ms"))
emit("-" * 56)
for n in [10, 15, 20, 25]:
doc, sk = build_grid(n)
doc.recompute() # warm
def touch_recompute():
sk.touch()
doc.recompute()
med, lo, hi = median_ms(touch_recompute, 9)
emit("%-6d %-10d %-12.1f %-12.1f %-12.1f" % (n, 2 * n, med, lo, hi))
App.closeDocument(doc.Name)
emit("")
emit("DONE")
if _out_file:
_out_file.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment