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. - *

- */ - private final List> results = new ArrayList<>(); - private final OpEnvironment env = OpEnvironment.build(); - - private final OpService ops; - - public static void main(final String[] args) throws IOException { - final int iterations = 10; - final int size; - if (args.length > 0) size = Integer.parseInt(args[0]); - else size = 4000; - final PerformanceBenchmark bench = new PerformanceBenchmark(size); - bench.testPerformance(iterations); - System.exit(0); - } - - /** Creates objects and measures memory usage. */ - public PerformanceBenchmark(final int imageSize) { - Context ctx = new Context(OpService.class); - ops = ctx.getService(OpService.class); - - width = height = imageSize; - System.out.println(); - System.out.println("===== " + width + " x " + height + " ====="); - - final List memUsage = new ArrayList<>(); - memUsage.add(getMemUsage()); - rawData = createRawData(width, height); - memUsage.add(getMemUsage()); - scijavaOpsData = rawData.clone(); - memUsage.add(getMemUsage()); - imageJOpsData = rawData.clone(); - memUsage.add(getMemUsage()); - - reportMemoryUsage(memUsage); - } - - public void testPerformance(final int iterationCount) throws IOException { - // initialize results map - results.clear(); - for (int i = 0; i < iterationCount; i++) { - final Map entry = new HashMap<>(); - results.add(entry); - } - testCheapPerformance(iterationCount); - if (SAVE_RESULTS_TO_DISK) saveResults("cheap"); - testExpensivePerformance(iterationCount); - if (SAVE_RESULTS_TO_DISK) saveResults("expensive"); - } - - /** - * Saves benchmark results to the given CSV file on disk. - *

- * The motivation is to produce two charts: - *

- *
    - *
  1. performance by iteration number (on each size of image) - *
      - *
    • one line graph per method
    • - *
    • X axis = iteration number
    • - *
    • Y axis = time needed
    • - *
    - *
  2. - *
  3. average performance by image size (for first iteration, and 10th) - *
      - *
    • one line graph per method
    • - *
    • X axis = size of image
    • - *
    • Y axis = time needed
    • - *
    - *
  4. - *
- *

- * The CSV file produced enables graph #1 very easily. For graph #2, results - * from several files must be combined. - *

- */ - public void saveResults(final String prefix) throws IOException { - final StringBuilder sb = new StringBuilder(); - - // write header - final Map firstEntry = results.get(0); - final String[] methods = firstEntry.keySet().toArray(new String[0]); - Arrays.sort(methods); - sb.append("Iteration"); - for (final String method : methods) { - sb.append("\t"); - sb.append(method); - } - sb.append("\n"); - - // write data - for (int iter = 0; iter < results.size(); iter++) { - final Map entry = results.get(iter); - sb.append(iter + 1); - for (final String method : methods) { - sb.append("\t"); - sb.append(entry.get(method)); - } - sb.append("\n"); - } - - // write to disk - final String path = "results-" + prefix + "-" + width + "x" + height + - ".csv"; - final PrintWriter out = new PrintWriter(new FileWriter(path)); - out.print(sb); - out.close(); - } - - // -- Helper methods -- - - /** Measures performance of a cheap operation (image inversion). */ - private void testCheapPerformance(final int iterationCount) { - System.out.println(); - System.out.println("-- TIME PERFORMANCE - CHEAP OPERATION --"); - for (int i = 0; i < iterationCount; i++) { - System.gc(); - System.out.println("Iteration #" + (i + 1) + "/" + iterationCount + ":"); - final List times = new ArrayList<>(); - times.add(System.currentTimeMillis()); - invertRaw(rawData); - times.add(System.currentTimeMillis()); - invertUsingSciJavaOps(scijavaOpsData); - times.add(System.currentTimeMillis()); - invertUsingImageJOps(imageJOpsData); - times.add(System.currentTimeMillis()); - logTimePerformance(i, times); - } - } - - /** Measures performance of a computationally more expensive operation. */ - private void testExpensivePerformance(final int iterationCount) { - System.out.println(); - System.out.println("-- TIME PERFORMANCE - EXPENSIVE OPERATION --"); - for (int i = 0; i < iterationCount; i++) { - System.gc(); - System.out.println("Iteration #" + (i + 1) + "/" + iterationCount + ":"); - final List times = new ArrayList<>(); - times.add(System.currentTimeMillis()); - randomizeRaw(rawData); - times.add(System.currentTimeMillis()); - randomizeUsingSciJavaOps(scijavaOpsData); - times.add(System.currentTimeMillis()); - randomizeUsingImageJOps(imageJOpsData); - times.add(System.currentTimeMillis()); - logTimePerformance(i, times); - } - } - - private long getMemUsage() { - final Runtime r = Runtime.getRuntime(); - System.gc(); - System.gc(); - return r.totalMemory() - r.freeMemory(); - } - - private void reportMemoryUsage(final List memUsage) { - final long rawMem = computeDifference(memUsage); - final long sjMem = computeDifference(memUsage); - final long ijMem = computeDifference(memUsage); - System.out.println(); - System.out.println("-- MEMORY OVERHEAD --"); - System.out.println(METHOD_RAW + ": " + rawMem + " bytes"); - System.out.println(METHOD_SCIJAVA_OPS + ": " + sjMem + " bytes"); - System.out.println(METHOD_IMAGEJ_OPS + ": " + ijMem + " bytes"); - } - - private void logTimePerformance(final int iter, final List times) { - final long rawTime = computeDifference(times); - final long sjTime = computeDifference(times); - final long ijTime = computeDifference(times); - - final Map entry = results.get(iter); - entry.put(METHOD_RAW, rawTime); - entry.put(METHOD_SCIJAVA_OPS, sjTime); - entry.put(METHOD_IMAGEJ_OPS, ijTime); - - reportTime(METHOD_RAW, rawTime, rawTime, sjTime); - reportTime(METHOD_SCIJAVA_OPS, sjTime, rawTime, sjTime); - reportTime(METHOD_IMAGEJ_OPS, ijTime, rawTime, sjTime); - } - - private long computeDifference(final List list) { - final long mem = list.remove(0); - return list.get(0) - mem; - } - - private void reportTime(final String label, final long time, - final long... otherTimes) - { - final StringBuilder sb = new StringBuilder(); - sb.append("\t"); - sb.append(label); - sb.append(": "); - sb.append(time); - sb.append(" ms"); - for (final long otherTime : otherTimes) { - sb.append(", "); - sb.append(time / (float) otherTime); - } - System.out.println(sb); - } - - // -- Creation methods -- - - private byte[] createRawData(final int w, final int h) { - final int size = w * h; - final int max = w + h; - final byte[] data = new byte[size]; - int index = 0; - for (int y = 0; y < h; y++) { - for (int x = 0; x < w; x++) { - data[index++] = (byte) (255 * (x + y) / max); - } - } - return data; - } - - // -- Inversion methods -- - - /** - * @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; - } - } - - private void invertUsingSciJavaOps(final byte[] data) { - env.unary("benchmark.invert").input(data).mutate(); - } - - private void invertUsingImageJOps(final byte[] data) { - ops.run("image.invert", new Object[] { data }); - } - - // -- Randomization methods -- - - /** - * @param data the data to invert - * @implNote op name="benchmark.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; - } - } - - private void randomizeUsingSciJavaOps(final byte[] data) { - env.unary("benchmark.randomize").input(data).mutate(); - } - - private void randomizeUsingImageJOps(final byte[] data) { - ops.run("math.randomUniform", new Object[] { data }); - } - - private static double expensiveOperation(final int value) { - return 255 * Math.random() * Math.sin(value / 255.0); - } - -} diff --git a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/RandomOp.java b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/RandomOp.java deleted file mode 100644 index 7af84b4b3..000000000 --- a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/RandomOp.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.Math.RandomUniform.class) -public class RandomOp extends AbstractUnaryInplaceOp implements - Ops.Math.RandomUniform -{ - - @Override - public void mutate(byte[] o) { - PerformanceBenchmark.randomizeRaw(o); - } -} diff --git a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/frameworks/BenchmarkFrameworks.java b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/frameworks/BenchmarkFrameworks.java new file mode 100644 index 000000000..6c64eb757 --- /dev/null +++ b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/frameworks/BenchmarkFrameworks.java @@ -0,0 +1,83 @@ + +package org.scijava.ops.benchmarks.frameworks; + +import net.imagej.ops.Ops; +import net.imagej.ops.special.inplace.AbstractUnaryInplaceOp; +import org.openjdk.jmh.annotations.*; +import org.scijava.ops.api.Hints; +import org.scijava.plugin.Plugin; + +/** + * Tests relative performance of ImageJ Ops and SciJava Ops. + * + * @author Gabriel Selzer + */ +public class BenchmarkFrameworks { + + private static final Hints HINTS = new Hints("cache.IGNORE"); + + /** + * Baseline benchmark - static code execution + * + * @param state the state between benchmarks + */ + @Fork(value = 1, warmups = 2) + @Warmup(iterations = 2) + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void runStatic(FrameworksState state) { + invertRaw(state.in); + } + + /** + * SciJava Ops benchmark + * + * @param state the state between benchmarks + */ + @Fork(value = 1, warmups = 2) + @Warmup(iterations = 2) + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void sciJavaOps(final FrameworksState state) { + state.env.unary("benchmark.invert", HINTS).input(state.in).mutate(); + } + + /** + * ImageJ Ops benchmark + * + * @param state the state between benchmarks + */ + @Fork(value = 1, warmups = 2) + @Warmup(iterations = 2) + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void imageJOps(final FrameworksState state) { + state.ops.run("image.invert", new Object[] { state.in }); + } + + // -- Ops -- // + + /** + * @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; + } + } + + @Plugin(type = Ops.Image.Invert.class) + public static class InvertOp extends AbstractUnaryInplaceOp implements + Ops.Image.Invert + { + + @Override + public void mutate(byte[] o) { + invertRaw(o); + } + } + +} diff --git a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/frameworks/FrameworksState.java b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/frameworks/FrameworksState.java new file mode 100644 index 000000000..bb8594931 --- /dev/null +++ b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/frameworks/FrameworksState.java @@ -0,0 +1,54 @@ + +package org.scijava.ops.benchmarks.frameworks; + +import net.imagej.ops.OpService; +import org.openjdk.jmh.annotations.*; +import org.scijava.Context; +import org.scijava.annotations.Index; +import org.scijava.annotations.IndexItem; +import org.scijava.ops.api.OpEnvironment; +import org.scijava.plugin.Plugin; + +/** + * {@link State} used in SciJava Ops framework benchmarks + * + * @author Gabriel Selzer + */ +@State(Scope.Benchmark) +public class FrameworksState { + + public OpEnvironment env; + public OpService ops; + public byte[] in; + + private Context ctx; + + @Setup(Level.Invocation) + public void setUpInvocation() { + // Set up ImageJ Ops + ctx = new Context(OpService.class); + ops = ctx.getService(OpService.class); + // Set up SciJava Ops + env = OpEnvironment.build(); + // Create input data + in = createRawData(4000, 4000); + } + + @TearDown(Level.Invocation) + public void tearDownInvocation() { + ctx.dispose(); + } + + private byte[] createRawData(final int w, final int h) { + final int size = w * h; + final int max = w + h; + final byte[] data = new byte[size]; + int index = 0; + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + data[index++] = (byte) (255 * (x + y) / max); + } + } + return data; + } +} diff --git a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/BenchmarkCaching.java b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/BenchmarkCaching.java new file mode 100644 index 000000000..3cb6b26de --- /dev/null +++ b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/BenchmarkCaching.java @@ -0,0 +1,52 @@ + +package org.scijava.ops.benchmarks.matching; + +import net.imglib2.img.array.ArrayImgs; +import org.openjdk.jmh.annotations.*; +import org.scijava.ops.api.Hints; + +import static org.scijava.ops.benchmarks.matching.MatchingOpCollection.op; + +/** + * Benchmark showcasing the effects of Op caching + * + * @author Gabriel Selzer + */ +public class BenchmarkCaching { + + private static final Hints CACHE_HIT_HINTS = new Hints(); + private static final Hints CACHE_MISS_HINTS = new Hints("cache.IGNORE"); + + @Fork(value = 1, warmups = 2) + @Warmup(iterations = 2) + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void runStatic(MatchingState state) { + var out = ArrayImgs.doubles(state.in.dimensionsAsLongArray()); + op(state.in, 1.0, out); + } + + @Fork(value = 1, warmups = 2) + @Warmup(iterations = 2) + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void runOp(final MatchingState state) { + var out = ArrayImgs.doubles(state.in.dimensionsAsLongArray()); + state.env.binary("benchmark.match", CACHE_MISS_HINTS) // + .input(state.in, 1.0) // + .output(out) // + .compute(); + } + + @Fork(value = 1, warmups = 2) + @Warmup(iterations = 2) + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void runOpCached(final MatchingState state) { + var out = ArrayImgs.doubles(state.in.dimensionsAsLongArray()); + state.env.binary("benchmark.match", CACHE_HIT_HINTS) // + .input(state.in, 1.0) // + .output(out) // + .compute(); + } +} diff --git a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/BenchmarkConversion.java b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/BenchmarkConversion.java new file mode 100644 index 000000000..6cc6b540b --- /dev/null +++ b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/BenchmarkConversion.java @@ -0,0 +1,78 @@ + +package org.scijava.ops.benchmarks.matching; + +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.converter.Converters; +import net.imglib2.converter.readwrite.SamplerConverter; +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.img.basictypeaccess.DoubleAccess; +import net.imglib2.type.numeric.integer.ByteType; +import net.imglib2.type.numeric.real.DoubleType; +import org.openjdk.jmh.annotations.*; +import org.scijava.ops.api.Hints; + +import static org.scijava.ops.benchmarks.matching.MatchingOpCollection.op; + +/** + * Benchmark showcasing the performance of Op parameter conversion + * + * @author Gabriel Selzer + */ +public class BenchmarkConversion { + + private static final Hints HINTS = new Hints("cache.IGNORE"); + + private static final SamplerConverter CONVERTER // + = sampler -> new DoubleType(new DoubleAccess() { + + @Override + public double getValue(int index) { + return sampler.get().getRealDouble(); + } + + @Override + public void setValue(int index, double value) { + sampler.get().setReal(value); + } + }); + + @Fork(value = 1, warmups = 2) + @Warmup(iterations = 2) + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void runStatic(MatchingState state) { + // manual conversion of simple input + var in = Converters.convert( // + (RandomAccessibleInterval) state.simpleIn, // + CONVERTER // + ); + // manual creation of simple output + var out = ArrayImgs.doubles(in.dimensionsAsLongArray()); + + // invoke Op statically + op(in, 1.0, out); + } + + @Fork(value = 1, warmups = 2) + @Warmup(iterations = 2) + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void runOpConverted(final MatchingState state) { + var out = ArrayImgs.bytes(state.simpleIn.dimensionsAsLongArray()); + state.env.binary("benchmark.match", HINTS) // + .input(state.simpleIn, (byte) 1) // + .output(out) // + .compute(); + } + + @Fork(value = 1, warmups = 2) + @Warmup(iterations = 2) + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void runOpConvertedAdapted(final MatchingState state) { + state.env.binary("benchmark.match", HINTS) // + .input(state.simpleIn, (byte) 1.0) // + .apply(); + } + +} diff --git a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/BenchmarkMatching.java b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/BenchmarkMatching.java new file mode 100644 index 000000000..6e084070b --- /dev/null +++ b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/BenchmarkMatching.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2014, Oracle America, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * 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. + * + * * Neither the name of Oracle nor the names of its contributors may be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * 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 HOLDER 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. + */ + +package org.scijava.ops.benchmarks.matching; + +import net.imglib2.img.array.ArrayImgs; +import org.openjdk.jmh.annotations.*; +import org.scijava.ops.api.Hints; + +/** + * JMH Benchmarks comparing the performance of basic Op invocations + * + * @author Gabriel Selzer + */ +public class BenchmarkMatching { + + private static final Hints HINTS = new Hints("cache.IGNORE"); + + @Fork(value = 1, warmups = 2) + @Warmup(iterations = 2) + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void runStatic(MatchingState state) { + var out = ArrayImgs.doubles(state.in.dimensionsAsLongArray()); + MatchingOpCollection.op(state.in, 1.0, out); + } + + @Fork(value = 1, warmups = 2) + @Warmup(iterations = 2) + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void runOp(final MatchingState state) { + var out = ArrayImgs.doubles(state.in.dimensionsAsLongArray()); + state.env.binary("benchmark.match", HINTS) // + .input(state.in, 1.0) // + .output(out) // + .compute(); + } + + @Fork(value = 1, warmups = 2) + @Warmup(iterations = 2) + @Benchmark + @BenchmarkMode(Mode.AverageTime) + public void runOpAdapted(final MatchingState state) { + var tmp = state.env.binary("benchmark.match", HINTS) // + .input(state.in, 1.0) // + .apply(); + } +} diff --git a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/MatchingOpCollection.java b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/MatchingOpCollection.java new file mode 100644 index 000000000..2a7c8780c --- /dev/null +++ b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/MatchingOpCollection.java @@ -0,0 +1,79 @@ + +package org.scijava.ops.benchmarks.matching; + +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.converter.Converters; +import net.imglib2.img.basictypeaccess.ByteAccess; +import net.imglib2.img.basictypeaccess.DoubleAccess; +import net.imglib2.loops.LoopBuilder; +import net.imglib2.type.numeric.integer.ByteType; +import net.imglib2.type.numeric.real.DoubleType; + +/** + * Class containing Ops used in benchmarking + * + * @author Gabriel Selzer + */ +public class MatchingOpCollection { + + /** + * @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)); + } + + /** + * @param in the {@link RandomAccessibleInterval} containing {@link ByteType}s + * @return a {@link RandomAccessibleInterval} wrapping {@code in}. + * @implNote op name="engine.convert", type=Function, priority='1000.' + */ + public static RandomAccessibleInterval toDoubleType( + RandomAccessibleInterval in) + { + return Converters.convert(in, sampler -> new DoubleType(new DoubleAccess() { + + @Override + public double getValue(int index) { + return sampler.get().getRealDouble(); + } + + @Override + public void setValue(int index, double value) { + sampler.get().setReal(value); + } + })); + } + + /** + * @param in the {@link RandomAccessibleInterval} containing + * {@link DoubleType}s + * @return a {@link RandomAccessibleInterval} wrapping {@code in}. + * @implNote op name="engine.convert", type=Function, priority='1000.' + */ + public static RandomAccessibleInterval toByteType( + RandomAccessibleInterval in) + { + return Converters.convert(in, sampler -> new ByteType(new ByteAccess() { + + @Override + public byte getValue(int index) { + return (byte) sampler.get().getRealDouble(); + } + + @Override + public void setValue(int index, byte value) { + sampler.get().setReal(value); + } + })); + } + +} diff --git a/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/MatchingState.java b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/MatchingState.java new file mode 100644 index 000000000..2297f7860 --- /dev/null +++ b/scijava-ops-benchmarks/src/main/java/org/scijava/ops/benchmarks/matching/MatchingState.java @@ -0,0 +1,32 @@ + +package org.scijava.ops.benchmarks.matching; + +import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.type.numeric.integer.ByteType; +import net.imglib2.type.numeric.real.DoubleType; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.scijava.ops.api.OpEnvironment; + +/** + * {@link State} used in SciJava Ops benchmarks + * + * @author Gabriel Selzer + */ +@State(Scope.Benchmark) +public class MatchingState { + + public OpEnvironment env; + public Img in; + public Img simpleIn; + + @Setup(Level.Invocation) + public void setUpInvocation() { + env = OpEnvironment.build(); + in = ArrayImgs.doubles(1000, 1000); + simpleIn = ArrayImgs.bytes(1000, 1000); + } +} diff --git a/scijava-ops-benchmarks/src/main/scripts/benchmark.sh b/scijava-ops-benchmarks/src/main/scripts/benchmark.sh deleted file mode 100755 index 4b68a44af..000000000 --- a/scijava-ops-benchmarks/src/main/scripts/benchmark.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/sh - -### -# #%L -# ImgLib2: a general-purpose, multidimensional image processing library. -# %% -# Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, -# John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, -# Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, -# Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, -# Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, -# Jean-Yves Tinevez and Michael Zinsmaier. -# %% -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as -# published by the Free Software Foundation, either version 2 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public -# License along with this program. If not, see -# . -# #L% -### - -# This simple script executes the imglib performance benchmark on -# images with varying numbers of pixels. Results are written to -# CSV in the current directory. - -DIR="$(dirname "$0")" -TARGET="$DIR/../../../target" - -CP=\ -$TARGET'/dependency/*':\ -$TARGET/classes: - -JAVA=java -MEM=512m -MAIN_CLASS=org.scijava.ops.benchmarks.PerformanceBenchmark - -# copy dependent JARs first -cd "$DIR/../../.." -mvn -DskipTests package dependency:copy-dependencies -cd - - -# 1 million -$JAVA -mx$MEM -cp "$CP" $MAIN_CLASS 1000 -# 4 million -$JAVA -mx$MEM -cp "$CP" $MAIN_CLASS 2000 -# 7 million -$JAVA -mx$MEM -cp "$CP" $MAIN_CLASS 2646 -# 10 million -$JAVA -mx$MEM -cp "$CP" $MAIN_CLASS 3162 -# 13 million -$JAVA -mx$MEM -cp "$CP" $MAIN_CLASS 3606 -# 16 million -$JAVA -mx$MEM -cp "$CP" $MAIN_CLASS 4000 -# 19 million -$JAVA -mx$MEM -cp "$CP" $MAIN_CLASS 4359 -# 22 million -$JAVA -mx$MEM -cp "$CP" $MAIN_CLASS 4690 -# 25 million -$JAVA -mx$MEM -cp "$CP" $MAIN_CLASS 5000 - -python "$DIR/chart-gen.py" > copyme.txt diff --git a/scijava-ops-benchmarks/src/main/scripts/chart-gen.py b/scijava-ops-benchmarks/src/main/scripts/chart-gen.py deleted file mode 100644 index 387097dfd..000000000 --- a/scijava-ops-benchmarks/src/main/scripts/chart-gen.py +++ /dev/null @@ -1,125 +0,0 @@ -### -# #%L -# ImgLib2: a general-purpose, multidimensional image processing library. -# %% -# Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, -# John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, -# Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, -# Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, -# Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, -# Jean-Yves Tinevez and Michael Zinsmaier. -# %% -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as -# published by the Free Software Foundation, either version 2 of the -# License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public -# License along with this program. If not, see -# . -# #L% -### - -# Transforms CSV data into HTML that uses pChart4mw charts. - -# Charts produced: -# - Iteration x Time for minimum resolution image, by method -# - Iteration x Time for maximum resolution image, by method -# - Resolution x Time at first iteration, by method -# - Resolution x Time at last iteration, by method - -# Total charts: 8 (4 for "cheap" and 4 for "expensive") - -import math - -# NB: Presumably there is a slick way to avoid having two very similar -# methods (generateIterationChart and generateResolutionChart) iterating -# over different list indices, but my Python knowledge is weak. - -# Iteration x Time at a given resolution, by method -def generateIterationChart(name, data, methods, resolution_index, keywords): - resolutions = range(1, 26, 3) - iteration_count = len(data[0]) - title = 'Iteration x Time (ms) at ' + str(resolutions[resolution_index]) + ' Mpx' - # Header - print(f'plot("{name}IterationVsTime{resolutions[resolution_index]}", "{title}", "Iteration",') - print(f'\t"Iteration,{",".join(methods)}\\n" +') - # Data - for i in range(0, iteration_count): - suffix = '\\n" +' if i < iteration_count - 1 else '");' - print(f'\t"{i},{",".join(str(v) for v in data[resolution_index][i])}{suffix}') - print() - -# Resolution x Time at a given iteration, by method -def generateResolutionChart(name, data, methods, iteration_index, keywords): - resolution_count = len(data) - title = 'Resolution x Time (ms) at iteration #' + str(iteration_index + 1) - # Header - print(f'plot("{name}ResolutionVsTime{iteration_index + 1}", "{title}", "Mpx",') - print(f'\t"Mpx,{",".join(methods)}\\n" +') - # Data - for r in range(resolution_count): - suffix = '\\n" +' if r < resolution_count - 1 else '");' - print(f'\t"{r},{",".join(str(v) for v in data[r][iteration_index])}{suffix}') - print() - -# reads data from CSV files into 3D array dimensioned: -# [resolution_count][iteration_count][method_count] -def process(prefix): - methods = [] - - # loop over image resolutions - data = [] - for p in range(1, 26, 3): - # compute filename - res = round(math.sqrt(1000000 * p)) - s_res = str(int(res)) - path_prefix = 'results-' + prefix + '-' + s_res + 'x' + s_res - in_path = path_prefix + '.csv' - - # read data file - with open(in_path, 'r') as f: - lines = f.readlines() - - # loop over iterations - header = True - data0 = [] - for line in lines: - items = line.rstrip().split('\t') - items.pop(0) - if header: - header = False - methods = items - else: - # loop over methods - data1 = [] - for item in items: - data1.append(int(item)) - data0.append(data1) - data.append(data0) - - resolution_count = len(data) - iteration_count = len(data[0]) - - w = 300 - h = 250 - l_size = 'size=' + str(w) + 'x' + str(h) - r_size = 'size=' + str(w + 135) + 'x' + str(h) - - # Iteration x Time for minimum resolution image, by method - generateIterationChart(prefix, data, methods, 0, l_size + ' plots') - # Iteration x Time for maximum resolution image, by method - generateIterationChart(prefix, data, methods, resolution_count - 1, r_size + ' plots legend') - # Resolution x Time at first iteration, by method - generateResolutionChart(prefix, data, methods, 0, l_size + ' angle=90 cubic plots') - # Resolution x Time at last iteration, by method - generateResolutionChart(prefix, data, methods, iteration_count - 1, r_size + ' angle=90 cubic plots legend') - -print("") -process('cheap') -process('expensive') \ No newline at end of file diff --git a/scijava-ops-benchmarks/src/test/java/org/scijava/ops/benchmarks/matching/BenchmarkingOpsTest.java b/scijava-ops-benchmarks/src/test/java/org/scijava/ops/benchmarks/matching/BenchmarkingOpsTest.java new file mode 100644 index 000000000..a793c7808 --- /dev/null +++ b/scijava-ops-benchmarks/src/test/java/org/scijava/ops/benchmarks/matching/BenchmarkingOpsTest.java @@ -0,0 +1,34 @@ + +package org.scijava.ops.benchmarks.matching; + +import net.imglib2.img.array.ArrayImgs; +import org.junit.jupiter.api.Test; +import org.scijava.ops.api.OpEnvironment; + +/** + * A simple test ensuring that the Ops used in benchmarks get matched. + * + * @author Gabriel Selzer + */ +public class BenchmarkingOpsTest { + + @Test + public void testImageConversion() { + OpEnvironment env = OpEnvironment.build(); + var simpleIn = ArrayImgs.bytes(1000, 1000); + var out = ArrayImgs.bytes(simpleIn.dimensionsAsLongArray()); + env.binary("benchmark.match") // + .input(simpleIn, (byte) 1) // + .output(out) // + .compute(); + } + + @Test + public void testImageConversionAdaptation() { + OpEnvironment env = OpEnvironment.build(); + var simpleIn = ArrayImgs.bytes(1000, 1000); + env.binary("benchmark.match") // + .input(simpleIn, 1.0) // + .apply(); + } +}