diff --git a/docs/conf.py b/docs/conf.py
index 727517896..b1f6ca184 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,7 +18,11 @@
"sphinx.ext.viewcode",
"sphinx_search.extension",
"sphinx_tabs.tabs",
+ "sphinx_charts.charts",
"myst_nb",
+ # NB: Needed for newer sphinx versions - see
+ # https://github.com/thclark/sphinx-charts/issues/23
+ "sphinxcontrib.jquery"
]
diff --git a/docs/ops/bin/benchmark.sh b/docs/ops/bin/benchmark.sh
new file mode 100755
index 000000000..5a6d0d47b
--- /dev/null
+++ b/docs/ops/bin/benchmark.sh
@@ -0,0 +1,25 @@
+#!/bin/bash
+
+conda init
+
+# Get the path to the script
+SCRIPT_PATH=$(dirname "$(realpath -s "$0")")
+DOCS_OPS_PATH="$SCRIPT_PATH/.."
+INC_PATH="$DOCS_OPS_PATH/../../"
+BENCHMARKS_PATH="$INC_PATH/scijava-ops-benchmarks"
+
+BENCH_OUT_FILE=scijava-ops-benchmarks_results.txt
+
+cd "$INC_PATH"
+mvn clean install -pl scijava-ops-benchmarks -am
+
+cd "$BENCHMARKS_PATH"
+mvn dependency:copy-dependencies
+
+cd "$DOCS_OPS_PATH"
+conda env create -f "environment.yml"
+java -cp "$BENCHMARKS_PATH/target/scijava-ops-benchmarks-0-SNAPSHOT.jar:$BENCHMARKS_PATH/target/dependency/*" org.openjdk.jmh.Main -o $BENCH_OUT_FILE
+
+source activate ops-docs
+python graph_results.py
+source deactivate
diff --git a/docs/ops/doc/Benchmarks.md b/docs/ops/doc/Benchmarks.md
deleted file mode 100644
index fddc2c396..000000000
--- a/docs/ops/doc/Benchmarks.md
+++ /dev/null
@@ -1,269 +0,0 @@
-# SciJava Ops Benchmarks
-
-This page describes a quantitative analysis of the SciJava Ops framework, and is heavily inspired by a similar comparison of [ImgLib2](https://imagej.net/libs/imglib2/benchmarks).
-
-For this analysis, we compare SciJava Ops against:
-1) Raw code execution
-2) [ImageJ Ops](https://imagej.net/libs/imagej-ops/index)
-
-Some of the charts shown plot execution time as a function of the number of executions - this allows us to show the impact of both the just-in-time (JIT) compiler and the framework's ability to improve performance on repeated function calls.
-
-Other charts plot execution time as a function of input size, which allows analysis of framework overhead.
-
-## Hardware and Software
-
-This analysis was performed with the following hardware:
-* 2017 Dell Inspiron 15 7000 Gaming
-* Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz
-* 16 GB 2400 MHz DDR4 RAM
-
-The following software components were used:
-* Ubuntu 22.04.3 LTS
-* OpenJDK Runtime Environment (build 11.0.21) with OpenJDK 64-Bit Server VM (build 11.0.21, mixed mode, sharing)
-* SciJava Ops Engine version `0.0-SNAPSHOT`
-* ImageJ Ops version `2.0.0`
-
-## "Cheap" Operations
-
-Following the precedent of ImgLib2, we first analyze the performance of each execution method in performing a simple byte inversion, which can be performed very quickly. This operation lends itself to the analysis of framework overhead.
-* The `Raw` execution method simply calls a static method
-* The `SciJava Ops` execution method discovers the static method through the `@implNote` Op declaration and calls the functionality through an `OpEnvironment`
-* The `ImageJ Ops` execution method discovers the static method through an `@Plugin` wrapper class and calls the functionality through an `OpService`
-
-```java
-/**
- * @param data the data to invert
- * @implNote op name="invert",type=Inplace1
- */
-public static void invertRaw(final byte[] data) {
- for (int i = 0; i < data.length; i++) {
- final int value = data[i] & 0xff;
- final int result = 255 - value;
- data[i] = (byte) result;
- }
-}
-```
-
-### Repetition Results
-
-We first note that the JIT improves performance benefits starting from the second iteration on all tested methods.
-
-For the very first execution, both SciJava Ops and ImageJ Ops incur decreased performance due to the overhead of discovering and matching the required functionality. The overhead for SciJava Ops is approximately half as much as the overhead of ImageJ Ops.
-
-Starting from the second execution, SciJava Ops rivals the performance of Raw execution, as it caches the results of prior matching calls. ImageJ Ops does not provide similar functionality.
-
-
-
-
-
-
-### Scaling Results
-
-For the first iteration, we see a consistent hierarchy, where Raw execution outperforms SciJava Ops, and SciJava Ops outperforms ImageJ Ops. This hierarchy stems from matching overhead.
-
-For the final iteration, we see that SciJava Ops consistently performs equivalently to Raw execution, while ImageJ Ops maintains worse performance from matching overhead.
-
-
-
-
-
-
-## "Expensive" Operations
-
-We now analyze the performance of each execution method in performing randomization. This operation is much more intensive, and allows us to ensure that the computation dominates any framework overhead.
-* The `Raw` execution method simply calls a static method
-* The `SciJava Ops` execution method discovers the static method through the `@implNote` Op declaration and calls the functionality through an `OpEnvironment`
-* The `ImageJ Ops` execution method discovers the static method through an `@Plugin` wrapper class and calls the functionality through an `OpService`
-
-```java
-private static double expensiveOperation(final int value) {
- return 255 * Math.random() * Math.sin(value / 255.0);
-}
-
-/**
- * @param data the data to invert
- * @implNote op name="randomize",type=Inplace1
- */
-public static void randomizeRaw(final byte[] data) {
- for (int i = 0; i < data.length; i++) {
- final int value = data[i] & 0xff;
- final double result = expensiveOperation(value);
- data[i] = (byte) result;
- }
-}
-```
-
-### Repetition Results
-
-For this more expensive operation, we see that computation does indeed dominate any framework overhead for both SciJava Ops and ImageJ Ops. At the smallest input size, we see that ImageJ Ops still shows noticeable matching overhead at every iteration, and SciJava Ops shows noticeable overhead for only the first few iterations.
-
-
-
-
-
-
-### Scaling Results
-
-As computation dominates overhead, we see no noticeable difference between the three execution methods.
-
-
-
-
-
-
-## Reproducing these results
-
-To reproduce these results, take the following steps:
-1) Assuming you have a supported Python 3.x installed, you can run `src/main/scripts.benchmark.sh` within the SciJava Ops Benchmarks module. This will generate a file `copyme.txt` in your current directory
-2) Copy the contents of `copyme.txt` into the HTML at the bottom of this page, starting at the tag ``
-3) Rebuild the document and view the graphs in a browser
-
-
-
-
-
-
-
-
-
diff --git a/docs/ops/doc/Benchmarks.rst b/docs/ops/doc/Benchmarks.rst
new file mode 100644
index 000000000..528190c8e
--- /dev/null
+++ b/docs/ops/doc/Benchmarks.rst
@@ -0,0 +1,128 @@
+SciJava Ops Benchmarks
+======================
+
+This page describes a quantitative analysis of the SciJava Ops framework, and is heavily inspired by a similar comparison of `ImgLib2 `_.
+
+Hardware and Software
+---------------------
+
+This analysis was performed with the following hardware:
+
+* 2021 Dell OptiPlex 5090 Small Form Factor
+* Intel(R) Core(TM) i7-10700 CPU @ 2.90GHz
+* 64 GB 3200 MHz DDR4 RAM
+
+The following software components were used:
+
+* Ubuntu 22.04.3 LTS
+* OpenJDK Runtime Environment (build 11.0.21) with OpenJDK 64-Bit Server VM (build 11.0.21, mixed mode, sharing)
+* SciJava Incubator commit `57dbbd25 `_
+* ImageJ Ops version ``2.0.0``
+
+All benchmarks are executed using the `Java Microbenchmark Harness `_, using the following parameters:
+
+* Forked JVM
+* 2 warmup executions
+* 2 10-second iterations per warm-up execution
+* 1 measurement execution
+* 5 10-second iterations per measurement execution
+
+Op Matching
+-----------
+
+We first analyze the performance of executing the following static method:
+
+.. code-block:: java
+
+ /**
+ * @param in the data to input to our function
+ * @param d the value to add to each element in the input
+ * @param out the preallocated storage buffer
+ * @implNote op name="benchmark.match",type=Computer
+ */
+ public static void op( //
+ final RandomAccessibleInterval in, //
+ final Double d, //
+ final RandomAccessibleInterval out //
+ ) {
+ LoopBuilder.setImages(in, out)
+ .multiThreaded()
+ .forEachPixel((i, o) -> o.set(i.get() + d));
+ }
+
+We first benchmark the base penalty of executing this method using SciJava Ops, compared to direct execution of the static method. Notably, as this method requires a preallocated output buffer, we must either create it ourselves, *or* allow SciJava Ops to create it for us using an Op adaptation. Thus, we test the benchmark the following three scenarios:
+
+* Output buffer creation + static method invocation
+* Output buffer creation + SciJava Ops invocation
+* SciJava Ops invocation using Op adaptation
+
+The results are shown in **Figure 1**. We find Op execution through the SciJava Ops framework adds a few milliseconds of additional overhead. A few additional milliseconds of overhead are observed when SciJava Ops is additionally tasked with creating an output buffer.
+
+.. chart:: ../images/BenchmarkMatching.json
+
+ **Figure 1:** Algorithm execution performance (lower is better)
+
+Note that the avove requests are benchmarked without assistance from the Op cache, i.e. they are designed to model the full matching process. As repeated Op requests will utilize the Op cache, we benchmark cached Op retrieval separately, with results shown in **Figure 2**. These benchmarks suggest Op caching helps avoid the additional overhead of Op adaptation as its performance approaches that of normal Op execution.
+
+.. chart:: ../images/BenchmarkCaching.json
+
+ **Figure 2:** Algorithm execution performance with Op caching (lower is better)
+
+Finally, we benchmark the overhead of SciJava Ops parameter conversion. Suppose we instead wish to operate upon a ``RandomAccessibleInterval`` - we must convert it to call our Op. We consider the following procedures:
+
+* Image conversion + output buffer creation + static method invocation
+* output buffer creation + SciJava Ops invocation using Op conversion
+* SciJava Ops invocation using Op conversion and Op adaptation
+
+The results are shown in **Figure 3**; note the Op cache is **not** enabled. We observe overheads on the order of 10 milliseconds to perform Op conversion with and without Op adaptation.
+
+.. chart:: ../images/BenchmarkConversion.json
+
+ **Figure 3:** Algorithm execution performance with Op conversion (lower is better)
+
+Framework Comparison
+--------------------
+
+To validate our development efforts atop the original `ImageJ Ops `_ framework, we benchmark executions of the following method:
+
+.. code-block:: java
+
+ /**
+ * @param data the data to invert
+ * @implNote op name="benchmark.invert",type=Inplace1
+ */
+ public static void invertRaw(final byte[] data) {
+ for (int i = 0; i < data.length; i++) {
+ final int value = data[i] & 0xff;
+ final int result = 255 - value;
+ data[i] = (byte) result;
+ }
+ }
+
+We then benchmark the performance of executing this code using the following pathways:
+
+* Static method invocation
+* SciJava Ops invocation
+* ImageJ Ops invocation (using a ``Class`` wrapper to make the method discoverable within ImageJ Ops)
+
+The results are shown in **Figure 4**. When algorithm matching dominates execution time, the SciJava Ops matching framework provides significant improvement in matching performance in comparison with the original ImageJ Ops framework.
+
+.. chart:: ../images/BenchmarkFrameworks.json
+
+ **Figure 4:** Algorithm execution performance by Framework (lower is better)
+
+Reproducing these Results
+-------------------------
+
+1. Create a local copy of the SciJava Ops incubator from the `GitHub repository `_
+2. Ensure you have package manager `Mamba `_ installed.
+3. Run the script `docs/ops/bin/benchmark.sh`, which will:
+ * Create the mamba Environment
+ * Build the benchmarking code
+ * Execute all JMH benchmarks
+ * Build `plotly `_ figures for each benchmark
+ * Distill each figure into JSON, stored in the correct place
+
+4. View the benchmark results, either by:
+ * Viewing the final lines of the JMH output file ``docs/ops/scijava-ops-benchmarks_results.txt``, **or**
+ * Locally building the documentation by navigating to ``docs``, executing ``make clean html && python -m http.server`` and navigating to this page.
diff --git a/docs/ops/environment.yml b/docs/ops/environment.yml
index d388bfec8..73eb1d456 100644
--- a/docs/ops/environment.yml
+++ b/docs/ops/environment.yml
@@ -3,9 +3,18 @@ channels:
- conda-forge
- defaults
dependencies:
+ - myst-nb
+ - openjdk=11
+ - plotly
- sphinx
- sphinx_rtd_theme
+
- pip
- pip:
+ # NB: Needed for newer sphinx versions - see
+ # https://github.com/thclark/sphinx-charts/issues/23
+ - sphinxcontrib-jquery
- sphinx-multiproject
- readthedocs-sphinx-search
+ - sphinx-tabs
+ - sphinx_charts
diff --git a/docs/ops/graph_results.py b/docs/ops/graph_results.py
new file mode 100644
index 000000000..705280289
--- /dev/null
+++ b/docs/ops/graph_results.py
@@ -0,0 +1,88 @@
+import json
+
+import plotly.graph_objects as go
+import plotly.io as io
+
+# This script parses JMH benchmarking results into charts developed using plot.ly (https://plotly.com/)
+# It currently develops one boxplot PER class, with each JMH benchmark method represented as a separate boxplot.
+# It expects JMH benchmark results be dumped to a file "scijava-ops-benchmark_results.txt", within its directory.
+
+# If you'd like to add a title to the plotly charts, add an entry to the following dict.
+#
+# The key should be the simple name of the class containing the JMH benchmark
+# and the value should be the title of the chart
+figure_titles = {
+ "BenchmarkFrameworks" : "Algorithm Execution Performance by Framework",
+ "BenchmarkCaching" : "Caching Effects on Op Matching Performance",
+ "BenchmarkConversion": "Parameter Conversion Performance",
+ "BenchmarkMatching": "Basic Op Matching Performance",
+}
+
+# If you'd like to alias a particular test in the chart categories, add an entry to the following dict.
+#
+# The key should be the JMH benchmark method name, and the value should be the alias
+benchmark_categories = {
+ "imageJOps" : "ImageJ Ops",
+ "sciJavaOps": "SciJava Ops",
+ "runStatic" : "Static Method",
+ "runOp" : "Op Execution",
+ "runOpCached": "Op Execution (cached)",
+ "runOpConverted": "Op Execution (converted)",
+ "runOpConvertedAdapted": "Op Execution (converted + adapted)",
+ "runOpAdapted": "Op Execution (adapted)",
+}
+
+# Read in the benchmark results
+with open("scijava-ops-benchmarks_results.txt") as f:
+ lines = f.readlines()
+
+# Keep only the lines containing our desired results
+for i in range(len(lines) - 1, 0, -1):
+ if (lines[i].startswith("Benchmark ")):
+ lines = lines[i+1:]
+ break
+
+# Build a map of results by benchmark class
+benchmark_classes = {}
+for line in lines:
+ words = line.split()
+ test = words[0]
+ last_period = test.rfind('.')
+ cls = test[:last_period]
+ test = test[last_period+1:]
+
+ if cls not in benchmark_classes:
+ benchmark_classes[cls] = {}
+
+ benchmark_classes[cls][test] = words[1:]
+
+# For each class, build a chart and dump it to JSON
+for cls, data in benchmark_classes.items():
+ period_pos = cls.rfind(".")
+ if period_pos > -1:
+ cls = cls[period_pos+1:]
+ x = []
+ y = []
+ error_y = []
+
+ # Add each benchmark in the class
+ for method, line in data.items():
+ method = benchmark_categories.get(method, method)
+ x.append(method)
+ y.append(float(line[2]))
+ error_y.append(float(line[4]))
+ # Create a bar chart
+ fig = go.Figure()
+ fig.add_bar(
+ x=x,
+ y=y,
+ error_y=dict(type='data', array=error_y),
+ )
+ fig.update_layout(
+ title_text=figure_titles.get(cls, "TODO: Add title"),
+ yaxis_title="Performance (s/op)"
+ )
+
+ # Convert to JSON and dump
+ with open(f"images/{cls}.json", "w") as f:
+ f.write(io.to_json(fig))
diff --git a/docs/ops/images/BenchmarkCaching.json b/docs/ops/images/BenchmarkCaching.json
new file mode 100644
index 000000000..64621bcd3
--- /dev/null
+++ b/docs/ops/images/BenchmarkCaching.json
@@ -0,0 +1 @@
+{"data":[{"error_y":{"array":[0.001,0.001,0.001],"type":"data"},"x":["Op Execution","Op Execution (cached)","Static Method"],"y":[0.003,0.003,0.001],"type":"bar"}],"layout":{"template":{"data":{"histogram2dcontour":[{"type":"histogram2dcontour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"choropleth":[{"type":"choropleth","colorbar":{"outlinewidth":0,"ticks":""}}],"histogram2d":[{"type":"histogram2d","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmap":[{"type":"heatmap","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmapgl":[{"type":"heatmapgl","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"contourcarpet":[{"type":"contourcarpet","colorbar":{"outlinewidth":0,"ticks":""}}],"contour":[{"type":"contour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"surface":[{"type":"surface","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"mesh3d":[{"type":"mesh3d","colorbar":{"outlinewidth":0,"ticks":""}}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"parcoords":[{"type":"parcoords","line":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolargl":[{"type":"scatterpolargl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"scattergeo":[{"type":"scattergeo","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolar":[{"type":"scatterpolar","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"scattergl":[{"type":"scattergl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatter3d":[{"type":"scatter3d","line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattermapbox":[{"type":"scattermapbox","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterternary":[{"type":"scatterternary","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattercarpet":[{"type":"scattercarpet","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}],"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"pie":[{"automargin":true,"type":"pie"}]},"layout":{"autotypenumbers":"strict","colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"hovermode":"closest","hoverlabel":{"align":"left"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"bgcolor":"#E5ECF6","angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"ternary":{"bgcolor":"#E5ECF6","aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]]},"xaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"yaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"geo":{"bgcolor":"white","landcolor":"#E5ECF6","subunitcolor":"white","showland":true,"showlakes":true,"lakecolor":"white"},"title":{"x":0.05},"mapbox":{"style":"light"}}},"title":{"text":"Caching Effects on Op Matching Performance"},"yaxis":{"title":{"text":"Performance (s\u002fop)"}}}}
\ No newline at end of file
diff --git a/docs/ops/images/BenchmarkConversion.json b/docs/ops/images/BenchmarkConversion.json
new file mode 100644
index 000000000..a266e7e0c
--- /dev/null
+++ b/docs/ops/images/BenchmarkConversion.json
@@ -0,0 +1 @@
+{"data":[{"error_y":{"array":[0.002,0.001,0.001],"type":"data"},"x":["Op Execution (converted)","Op Execution (converted + adapted)","Static Method"],"y":[0.015,0.007,0.001],"type":"bar"}],"layout":{"template":{"data":{"histogram2dcontour":[{"type":"histogram2dcontour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"choropleth":[{"type":"choropleth","colorbar":{"outlinewidth":0,"ticks":""}}],"histogram2d":[{"type":"histogram2d","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmap":[{"type":"heatmap","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmapgl":[{"type":"heatmapgl","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"contourcarpet":[{"type":"contourcarpet","colorbar":{"outlinewidth":0,"ticks":""}}],"contour":[{"type":"contour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"surface":[{"type":"surface","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"mesh3d":[{"type":"mesh3d","colorbar":{"outlinewidth":0,"ticks":""}}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"parcoords":[{"type":"parcoords","line":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolargl":[{"type":"scatterpolargl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"scattergeo":[{"type":"scattergeo","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolar":[{"type":"scatterpolar","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"scattergl":[{"type":"scattergl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatter3d":[{"type":"scatter3d","line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattermapbox":[{"type":"scattermapbox","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterternary":[{"type":"scatterternary","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattercarpet":[{"type":"scattercarpet","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}],"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"pie":[{"automargin":true,"type":"pie"}]},"layout":{"autotypenumbers":"strict","colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"hovermode":"closest","hoverlabel":{"align":"left"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"bgcolor":"#E5ECF6","angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"ternary":{"bgcolor":"#E5ECF6","aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]]},"xaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"yaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"geo":{"bgcolor":"white","landcolor":"#E5ECF6","subunitcolor":"white","showland":true,"showlakes":true,"lakecolor":"white"},"title":{"x":0.05},"mapbox":{"style":"light"}}},"title":{"text":"Parameter Conversion Performance"},"yaxis":{"title":{"text":"Performance (s\u002fop)"}}}}
\ No newline at end of file
diff --git a/docs/ops/images/BenchmarkFrameworks.json b/docs/ops/images/BenchmarkFrameworks.json
new file mode 100644
index 000000000..7608fbb1b
--- /dev/null
+++ b/docs/ops/images/BenchmarkFrameworks.json
@@ -0,0 +1 @@
+{"data":[{"error_y":{"array":[0.002,0.001,0.001],"type":"data"},"x":["ImageJ Ops","Static Method","SciJava Ops"],"y":[0.012,0.001,0.002],"type":"bar"}],"layout":{"template":{"data":{"histogram2dcontour":[{"type":"histogram2dcontour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"choropleth":[{"type":"choropleth","colorbar":{"outlinewidth":0,"ticks":""}}],"histogram2d":[{"type":"histogram2d","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmap":[{"type":"heatmap","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmapgl":[{"type":"heatmapgl","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"contourcarpet":[{"type":"contourcarpet","colorbar":{"outlinewidth":0,"ticks":""}}],"contour":[{"type":"contour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"surface":[{"type":"surface","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"mesh3d":[{"type":"mesh3d","colorbar":{"outlinewidth":0,"ticks":""}}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"parcoords":[{"type":"parcoords","line":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolargl":[{"type":"scatterpolargl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"scattergeo":[{"type":"scattergeo","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolar":[{"type":"scatterpolar","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"scattergl":[{"type":"scattergl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatter3d":[{"type":"scatter3d","line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattermapbox":[{"type":"scattermapbox","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterternary":[{"type":"scatterternary","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattercarpet":[{"type":"scattercarpet","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}],"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"pie":[{"automargin":true,"type":"pie"}]},"layout":{"autotypenumbers":"strict","colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"hovermode":"closest","hoverlabel":{"align":"left"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"bgcolor":"#E5ECF6","angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"ternary":{"bgcolor":"#E5ECF6","aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]]},"xaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"yaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"geo":{"bgcolor":"white","landcolor":"#E5ECF6","subunitcolor":"white","showland":true,"showlakes":true,"lakecolor":"white"},"title":{"x":0.05},"mapbox":{"style":"light"}}},"title":{"text":"Algorithm Execution Performance by Framework"},"yaxis":{"title":{"text":"Performance (s\u002fop)"}}}}
\ No newline at end of file
diff --git a/docs/ops/images/BenchmarkMatching.json b/docs/ops/images/BenchmarkMatching.json
new file mode 100644
index 000000000..174bf12e5
--- /dev/null
+++ b/docs/ops/images/BenchmarkMatching.json
@@ -0,0 +1 @@
+{"data":[{"error_y":{"array":[0.001,0.001,0.001],"type":"data"},"x":["Op Execution","Op Execution (adapted)","Static Method"],"y":[0.003,0.004,0.001],"type":"bar"}],"layout":{"template":{"data":{"histogram2dcontour":[{"type":"histogram2dcontour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"choropleth":[{"type":"choropleth","colorbar":{"outlinewidth":0,"ticks":""}}],"histogram2d":[{"type":"histogram2d","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmap":[{"type":"heatmap","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"heatmapgl":[{"type":"heatmapgl","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"contourcarpet":[{"type":"contourcarpet","colorbar":{"outlinewidth":0,"ticks":""}}],"contour":[{"type":"contour","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"surface":[{"type":"surface","colorbar":{"outlinewidth":0,"ticks":""},"colorscale":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]]}],"mesh3d":[{"type":"mesh3d","colorbar":{"outlinewidth":0,"ticks":""}}],"scatter":[{"fillpattern":{"fillmode":"overlay","size":10,"solidity":0.2},"type":"scatter"}],"parcoords":[{"type":"parcoords","line":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolargl":[{"type":"scatterpolargl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"bar":[{"error_x":{"color":"#2a3f5f"},"error_y":{"color":"#2a3f5f"},"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"bar"}],"scattergeo":[{"type":"scattergeo","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterpolar":[{"type":"scatterpolar","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"histogram":[{"marker":{"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"histogram"}],"scattergl":[{"type":"scattergl","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatter3d":[{"type":"scatter3d","line":{"colorbar":{"outlinewidth":0,"ticks":""}},"marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattermapbox":[{"type":"scattermapbox","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scatterternary":[{"type":"scatterternary","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"scattercarpet":[{"type":"scattercarpet","marker":{"colorbar":{"outlinewidth":0,"ticks":""}}}],"carpet":[{"aaxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"baxis":{"endlinecolor":"#2a3f5f","gridcolor":"white","linecolor":"white","minorgridcolor":"white","startlinecolor":"#2a3f5f"},"type":"carpet"}],"table":[{"cells":{"fill":{"color":"#EBF0F8"},"line":{"color":"white"}},"header":{"fill":{"color":"#C8D4E3"},"line":{"color":"white"}},"type":"table"}],"barpolar":[{"marker":{"line":{"color":"#E5ECF6","width":0.5},"pattern":{"fillmode":"overlay","size":10,"solidity":0.2}},"type":"barpolar"}],"pie":[{"automargin":true,"type":"pie"}]},"layout":{"autotypenumbers":"strict","colorway":["#636efa","#EF553B","#00cc96","#ab63fa","#FFA15A","#19d3f3","#FF6692","#B6E880","#FF97FF","#FECB52"],"font":{"color":"#2a3f5f"},"hovermode":"closest","hoverlabel":{"align":"left"},"paper_bgcolor":"white","plot_bgcolor":"#E5ECF6","polar":{"bgcolor":"#E5ECF6","angularaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"radialaxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"ternary":{"bgcolor":"#E5ECF6","aaxis":{"gridcolor":"white","linecolor":"white","ticks":""},"baxis":{"gridcolor":"white","linecolor":"white","ticks":""},"caxis":{"gridcolor":"white","linecolor":"white","ticks":""}},"coloraxis":{"colorbar":{"outlinewidth":0,"ticks":""}},"colorscale":{"sequential":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"sequentialminus":[[0.0,"#0d0887"],[0.1111111111111111,"#46039f"],[0.2222222222222222,"#7201a8"],[0.3333333333333333,"#9c179e"],[0.4444444444444444,"#bd3786"],[0.5555555555555556,"#d8576b"],[0.6666666666666666,"#ed7953"],[0.7777777777777778,"#fb9f3a"],[0.8888888888888888,"#fdca26"],[1.0,"#f0f921"]],"diverging":[[0,"#8e0152"],[0.1,"#c51b7d"],[0.2,"#de77ae"],[0.3,"#f1b6da"],[0.4,"#fde0ef"],[0.5,"#f7f7f7"],[0.6,"#e6f5d0"],[0.7,"#b8e186"],[0.8,"#7fbc41"],[0.9,"#4d9221"],[1,"#276419"]]},"xaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"yaxis":{"gridcolor":"white","linecolor":"white","ticks":"","title":{"standoff":15},"zerolinecolor":"white","automargin":true,"zerolinewidth":2},"scene":{"xaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"yaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2},"zaxis":{"backgroundcolor":"#E5ECF6","gridcolor":"white","linecolor":"white","showbackground":true,"ticks":"","zerolinecolor":"white","gridwidth":2}},"shapedefaults":{"line":{"color":"#2a3f5f"}},"annotationdefaults":{"arrowcolor":"#2a3f5f","arrowhead":0,"arrowwidth":1},"geo":{"bgcolor":"white","landcolor":"#E5ECF6","subunitcolor":"white","showland":true,"showlakes":true,"lakecolor":"white"},"title":{"x":0.05},"mapbox":{"style":"light"}}},"title":{"text":"Basic Op Matching Performance"},"yaxis":{"title":{"text":"Performance (s\u002fop)"}}}}
\ No newline at end of file
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 4b5d9000a..1d6ab664d 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,5 +1,6 @@
sphinx-multiproject
sphinx_rtd_theme
sphinx-tabs
+sphinx_charts
myst-nb
readthedocs-sphinx-search
diff --git a/scijava-ops-benchmarks/README.md b/scijava-ops-benchmarks/README.md
index 006d98cc4..102f5472b 100644
--- a/scijava-ops-benchmarks/README.md
+++ b/scijava-ops-benchmarks/README.md
@@ -1,3 +1,24 @@
-# SciJava Ops Benchmarks: benchmarking utilities for the SciJava Ops Engine library
+# SciJava Ops Benchmarks: A set of benchmarks for the Scijava Ops framework
-TODO
+This module contains benchmark code used to assess the performance of the SciJava Ops framework.
+
+# Executing the Benchmarks
+
+The following lines can be used to build and execute the benchmarks from the base `scijava-ops-benchmarks` directory on the command line:
+
+```bash
+# Build the benchmarks module
+cd ..
+mvn clean install -pl scijava-ops-benchmarks -am
+
+# Copy dependencies into target folder
+cd scijava-ops-benchmarks
+
+# Execute the benchmarks
+mvn dependency:copy-dependencies
+java -cp "target/scijava-ops-benchmarks-0-SNAPSHOT.jar:target/dependency/*" org.openjdk.jmh.Main
+```
+
+# Adding a new benchmark
+
+The best way to create a new benchmark is to create a new Java class within `src/main/java/org/scijava/benchmarks`. Within this new class, you can add new methods (annotated with `@Benchmark`) which will be automatically invoked when benchmarks are executed using the commands above.
diff --git a/scijava-ops-benchmarks/bin/generate.groovy b/scijava-ops-benchmarks/bin/generate.groovy
deleted file mode 100755
index 3e26050d9..000000000
--- a/scijava-ops-benchmarks/bin/generate.groovy
+++ /dev/null
@@ -1,238 +0,0 @@
-#!/usr/bin/env groovy
-
-/*
- * #%L
- * SciJava Operations: a framework for reusable algorithms.
- * %%
- * Copyright (C) 2018 SciJava developers.
- * %%
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- * #L%
- */
-
-debug = System.getenv('DEBUG')
-def debug(msg) {
- if (debug) System.err.println("[DEBUG] $msg")
-}
-
-@Grab('org.apache.velocity:velocity:1.7')
-import org.apache.velocity.app.VelocityEngine
-
-// TODO: Get path to Groovy script and make these dirs relative to that.
-templateDirectory = 'templates'
-outputDirectory = 'src'
-
-knownFiles = new java.util.HashSet();
-
-/* Gets the last modified timestamp for the given file. */
-def timestamp(dir, file) {
- if (file == null) return Long.MAX_VALUE;
- file = new java.io.File(dir, file);
- knownFiles.add(file);
- return file.lastModified();
-}
-
-/* Processes a template using Apache Velocity. */
-def processTemplate(engine, context, templateFile, outFilename) {
- debug("processTemplate('$engine', '$context', '$templateFile', '$outFilename')")
-
- if (outFilename == null) return; // nothing to do
-
- // create output directory if it does not already exist
- outFile = new java.io.File(outputDirectory, outFilename);
- knownFiles.add(outFile);
- if (outFile.getParentFile() != null) outFile.getParentFile().mkdirs();
-
- // apply the template and write out the result
- t = engine.getTemplate(templateFile);
- writer = new StringWriter();
- t.merge(context, writer);
- out = new PrintWriter(outFile, "UTF-8");
- out.print(writer.toString());
- out.close();
-}
-
-/* Evaluates a string using Groovy. */
-def parseValue(sh, translationsFile, key, expression) {
- try {
- result = sh.evaluate(expression)
- sh.setVariable(key, result)
- return result
- }
- catch (groovy.lang.GroovyRuntimeException e) {
- print("[WARNING] $translationsFile: " +
- "key '$key' has unparseable value: " + e.getMessage());
- }
-}
-
-/* Reads a translations File */
-def readTranslation(engine, globalContext, reader, templateSubdirectory, templateFile, translationsFile, isInclude){
- sh = new groovy.lang.GroovyShell();
- for (;;) {
- // read the line
- line = reader.readLine();
-
- if (line == null) break;
- // check if the line starts a new section
- if (line.startsWith("[") && line.endsWith("]")) {
- // if we are parsing a .include file, return when we hit any sections
- if(isInclude){
- println("[WARNING] $translationsFile: Section definition in .include file. Ending processing of $translationsFile");
- return context;
- }
- // write out the previous file
- processTemplate(engine, context, templateFile, outputFilename);
-
- // start a new file
- outputFilename = line.substring(1, line.length() - 1);
- if (!templateDirectory.equals(templateSubdirectory)) {
- subPath = templateSubdirectory.substring(templateDirectory.length() + 1);
- outputFilename = "$subPath/$outputFilename";
- }
- context = new org.apache.velocity.VelocityContext(globalContext);
- continue;
- }
-
- // ignore blank lines
- trimmedLine = line.trim();
- if (trimmedLine.isEmpty()) continue;
-
- // ignore comments
- if (trimmedLine.startsWith("#")) continue;
-
- // include any global files
- if (trimmedLine.startsWith(".include")){
- includeFile = line.substring(9);
- if(includeFile.startsWith("templates")){
- includeSubdirectory = includeFile.substring(0, includeFile.lastIndexOf("/"))
- includeFile = includeFile.substring(includeFile.lastIndexOf("/"))
- }
- else{
- includeSubdirectory = templateSubdirectory
- }
- globalReader = new java.io.BufferedReader(new java.io.FileReader("$includeSubdirectory/$includeFile"));
- encapsulatedContext = new org.apache.velocity.VelocityContext(context)
- context = readTranslation(engine, encapsulatedContext, globalReader, templateSubdirectory, templateFile, includeFile, true)
- continue;
- }
-
- if (!line.contains('=')) {
- print("[WARNING] $translationsFile: Ignoring spurious line: $line");
- continue;
- }
-
- int idx = line.indexOf('=');
- key = line.substring(0, idx).trim();
- value = line.substring(idx + 1);
-
- if (value.trim().equals('```')) {
- // multi-line value
- builder = new StringBuilder();
- for (;;) {
- line = reader.readLine();
- if (line == null) {
- throw new RuntimeException("Unfinished value: " + builder.toString());
- }
- if (line.equals('```')) {
- break;
- }
- if (builder.length() > 0) {
- builder.append("\n");
- }
- builder.append(line);
- }
- value = builder.toString();
- }
-
- context.put(key, parseValue(sh, translationsFile, key, value));
- }
-
- return context;
-}
-
-/*
- * Translates a template into many files in the outputDirectory,
- * given a translations file in INI style; e.g.:
- *
- * [filename1]
- * variable1 = value1
- * variable2 = value2
- * ...
- * [filename2]
- * variable1 = value3
- * variable2 = value4
- * ...
- */
-def translate(templateSubdirectory, templateFile, translationsFile) {
- debug("translate('$templateSubdirectory', '$templateFile', '$translationsFile')")
-
- // initialize the Velocity engine
- engine = new org.apache.velocity.app.VelocityEngine();
- p = new java.util.Properties();
- // fail if template uses an invalid expression; e.g., an undefined variable
- p.setProperty("runtime.references.strict", "true");
- // tell Velocity where the templates are located
- p.setProperty("file.resource.loader.path", "$templateSubdirectory");
- // tell Velocity to log to stderr rather than to a velocity.log file
- p.setProperty(org.apache.velocity.runtime.RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
- "org.apache.velocity.runtime.log.SystemLogChute");
- engine.init(p);
-
- // read translation lines
- outputFilename = null;
- context = globalContext = new org.apache.velocity.VelocityContext();
- reader = new java.io.BufferedReader(new java.io.FileReader("$templateSubdirectory/$translationsFile"));
-
- readTranslation(engine, context, reader, templateSubdirectory, templateFile, translationsFile, false);
-
- reader.close();
-
- // process the template
- processTemplate(engine, context, templateFile, outputFilename);
-}
-
-/* Recursively translates all templates in the given directory. */
-def translateDirectory(templateSubdirectory) {
- debug("translateDirectory('$templateSubdirectory')")
-
- for (file in new java.io.File(templateSubdirectory).listFiles()) {
- if (file.isDirectory()) {
- // process subdirectories recursively
- translateDirectory(file.getPath());
- }
- else {
- // process Velocity template files only
- name = file.getName();
- if (!name.endsWith('.vm')) continue;
- prefix = name.substring(0, name.lastIndexOf('.'));
- translate(templateSubdirectory, name, prefix + '.list');
- }
- }
-}
-
-try {
- translateDirectory(templateDirectory);
-}
-catch (Throwable t) {
- t.printStackTrace(System.err);
- throw t;
-}
diff --git a/scijava-ops-benchmarks/pom.xml b/scijava-ops-benchmarks/pom.xml
index 46400d7c5..531289eb5 100644
--- a/scijava-ops-benchmarks/pom.xml
+++ b/scijava-ops-benchmarks/pom.xml
@@ -1,165 +1,191 @@
- 4.0.0
+ 4.0.0
-
- org.scijava
- scijava-incubator
- 0-SNAPSHOT
- ..
-
+
+ org.scijava
+ scijava-incubator
+ 0-SNAPSHOT
+ ..
+
- scijava-ops-benchmarks
+ scijava-ops-benchmarks
- SciJava Ops Benchmarks
- SciJava Operations Benchmarks: A set of benchmarking tests for the SciJava Ops Engine library
- https://github.com/scijava/scijava-ops-benchmarks
- 2023
-
- SciJava
- https://scijava.org/
-
-
-
- Simplified BSD License
- repo
-
-
+ SciJava Ops Benchmarks
+ SciJava Operations Benchmarks: A set of benchmarks for the SciJava Ops framework
+ https://github.com/scijava/scijava-ops-benchmarks
+ 2023
+
+ SciJava
+ https://scijava.org/
+
+
+
+ Simplified BSD License
+ repo
+
+
-
-
- ctrueden
- Curtis Rueden
- https://imagej.net/people/ctrueden
-
- founder
- lead
- reviewer
- support
- maintainer
-
-
-
- gselzer
- Gabriel Selzer
-
- founder
- developer
- debugger
- reviewer
- support
-
-
-
-
-
- Christian Dietz
- https://imagej.net/people/dietzc
-
- founder
-
-
- dietzc
-
-
-
- David Kolb
-
- founder
-
-
- Treiblesschorle
-
-
-
+
+
+ ctrueden
+ Curtis Rueden
+ https://imagej.net/people/ctrueden
+
+ founder
+ lead
+ reviewer
+ support
+ maintainer
+
+
+
+ gselzer
+ Gabriel Selzer
+
+ founder
+ developer
+ debugger
+ reviewer
+ support
+
+
+
+
+
+ David Kolb
+
+ founder
+
+
+ Treiblesschorle
+
+
+
-
-
- Image.sc Forum
- https://forum.image.sc/tag/scijava
-
-
-
-
-
- maven-compiler-plugin
-
-
-
- org.scijava
- scijava-ops-indexer
- ${project.version}
-
-
- org.scijava
- scijava-common
- ${scijava-common.version}
-
-
- true
- true
-
- -Aparse.ops="${scijava.parse.ops}"
- -Aop.version="${project.version}"
-
-
-
-
-
+
+
+ Image.sc Forum
+ https://forum.image.sc/tag/scijava
+
+
-
- scm:git:git://github.com/scijava/incubator
- scm:git:git@github.com:scijava/incubator
- HEAD
- https://github.com/scijava/incubator
-
-
- GitHub Issues
- https://github.com/scijava/scijava/issues
-
-
- GitHub Actions
- https://github.com/scijava/incubator/actions
-
+
+ scm:git:git://github.com/scijava/incubator
+ scm:git:git@github.com:scijava/incubator
+ HEAD
+ https://github.com/scijava/incubator
+
+
+ GitHub Issues
+ https://github.com/scijava/scijava/issues
+
+
+ GitHub Actions
+ https://github.com/scijava/incubator/actions
+
-
- org.scijava.ops.benchmarks.Main
- org.scijava.ops.benchmarks
+
+ org.scijava.ops.benchmarks.jmh
- bsd_2
- SciJava developers.
- ${scijava.allowedDuplicateClasses},com.github.therapi.runtimejavadoc.repack.com.eclipsesource.json.*
- ${scijava-ops-benchmarks.allowedDuplicateClasses}
-
+ bsd_2
+ SciJava developers.
-
-
- scijava.public
- https://maven.scijava.org/content/groups/public
-
-
+ ${scijava.allowedDuplicateClasses}
+ ${scijava-ops-benchmarks.allowedDuplicateClasses}
+
+
+
+
+ scijava.public
+ https://maven.scijava.org/content/groups/public
+
+
+
+
+
+
+ maven-compiler-plugin
+
+
+
+ org.scijava
+ scijava-ops-indexer
+ ${project.version}
+
+
+ org.scijava
+ scijava-common
+ ${scijava-common.version}
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ ${jmh.version}
+
+
+ true
+ true
+
+ -Aparse.ops="${scijava.parse.ops}"
+ -Aop.version="${project.version}"
+
+
+
+
+
+
+
+
+ net.imagej
+ imagej-ops
+
+
+
+ org.scijava
+ scijava-common
+
+
+ org.openjdk.jmh
+ jmh-core
+ ${jmh.version}
+
+ compile
+
+
+ org.openjdk.jmh
+ jmh-generator-annprocess
+ ${jmh.version}
+ provided
+
+
+ org.scijava
+ scijava-ops-api
+ ${project.version}
+
+
+ org.scijava
+ scijava-ops-engine
+ ${project.version}
+ runtime
+
+
+ org.scijava
+ scijava-ops-image
+ ${project.version}
+ runtime
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
-
-
- net.imagej
- imagej-ops
-
-
-
- org.scijava
- scijava-common
-
-
- org.scijava
- scijava-ops-api
- ${project.version}
-
-
- org.scijava
- scijava-ops-engine
- ${project.version}
- runtime
-
-
-
diff --git a/scijava-ops-benchmarks/src/main/java/module-info.java b/scijava-ops-benchmarks/src/main/java/module-info.java
deleted file mode 100644
index 6c1b00079..000000000
--- a/scijava-ops-benchmarks/src/main/java/module-info.java
+++ /dev/null
@@ -1,8 +0,0 @@
-open module org.scijava.ops.benchmarks {
- exports org.scijava.ops.benchmarks;
-
- requires net.imagej;
- requires net.imagej.ops;
- requires org.scijava;
- requires org.scijava.ops.api;
-}
\ No newline at end of file
diff --git a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/InvertOp.java b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/InvertOp.java
deleted file mode 100644
index 761863972..000000000
--- a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/InvertOp.java
+++ /dev/null
@@ -1,17 +0,0 @@
-
-package org.scijava.ops.benchmarks;
-
-import net.imagej.ops.Ops;
-import net.imagej.ops.special.inplace.AbstractUnaryInplaceOp;
-import org.scijava.plugin.Plugin;
-
-@Plugin(type = Ops.Image.Invert.class)
-public class InvertOp extends AbstractUnaryInplaceOp implements
- Ops.Image.Invert
-{
-
- @Override
- public void mutate(byte[] o) {
- PerformanceBenchmark.invertRaw(o);
- }
-}
diff --git a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/PerformanceBenchmark.java b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/PerformanceBenchmark.java
deleted file mode 100644
index 704b5ead5..000000000
--- a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/PerformanceBenchmark.java
+++ /dev/null
@@ -1,307 +0,0 @@
-
-package org.scijava.ops.benchmarks;
-
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import net.imagej.ops.OpService;
-import org.scijava.Context;
-import org.scijava.ops.api.OpEnvironment;
-
-/**
- * Tests performance of uint8 image operations with raw byte array, ImageJ 1.x,
- * and ImgLib2 libraries.
- *
- * @author Curtis Rueden
- */
-public class PerformanceBenchmark {
-
- private static final boolean SAVE_RESULTS_TO_DISK = true;
-
- private static final String METHOD_RAW = "Raw";
- private static final String METHOD_SCIJAVA_OPS = "SciJava Ops";
- private static final String METHOD_IMAGEJ_OPS = "ImageJ Ops";
- private final int width, height;
- private final byte[] rawData;
- private final byte[] scijavaOpsData;
- private final byte[] imageJOpsData;
-
- /**
- * List of timing results.
- *
- * Each element of the list represents an iteration. Each entry maps the
- * method name to the time measured.
- *