From af553fa6b1b0b547c47820b7c9fb62ffc3020b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nurzhan=20Sak=C3=A9n?= Date: Thu, 2 Nov 2023 21:25:41 +0400 Subject: [PATCH] Inline util functions and iterator methods, fix slow inclusive range iteration, add benchmarks, release 0.1.2 --- BENCHMARKS.md | 161 ++++++++++++++++++++++++++++++++ Cargo.toml | 13 ++- README.md | 10 +- benches/bresenham_comparison.rs | 153 ++++++++++++++++++++++++++++++ src/func.rs | 2 +- src/iter.rs | 16 +++- src/util.rs | 11 ++- 7 files changed, 355 insertions(+), 11 deletions(-) create mode 100644 BENCHMARKS.md create mode 100644 benches/bresenham_comparison.rs diff --git a/BENCHMARKS.md b/BENCHMARKS.md new file mode 100644 index 0000000..20a62af --- /dev/null +++ b/BENCHMARKS.md @@ -0,0 +1,161 @@ +# Benchmarks + +## Table of Contents + +- [Benchmark Results](#benchmark-results) + - [160x160_inside_vert](#160x160_inside_vert) + - [160x160_inside_hor](#160x160_inside_hor) + - [160x160_inside_gentle](#160x160_inside_gentle) + - [160x160_inside_steep](#160x160_inside_steep) + - [160x160_outside](#160x160_outside) + - [1280x1280_inside_vert](#1280x1280_inside_vert) + - [1280x1280_inside_hor](#1280x1280_inside_hor) + - [1280x1280_inside_gentle](#1280x1280_inside_gentle) + - [1280x1280_inside_steep](#1280x1280_inside_steep) + - [1280x1280_outside](#1280x1280_outside) + - [5120x5120_inside_vert](#5120x5120_inside_vert) + - [5120x5120_inside_hor](#5120x5120_inside_hor) + - [5120x5120_inside_gentle](#5120x5120_inside_gentle) + - [5120x5120_inside_steep](#5120x5120_inside_steep) + - [5120x5120_outside](#5120x5120_outside) + +## Benchmark Results + +### 160x160_inside_vert + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:--------------------------|:---------------------------------|:---------------------------------|:---------------------------------|:--------------------------------- | +| **`1`** | `206.62 ns` (✅ **1.00x**) | `205.03 ns` (✅ **1.01x faster**) | `62.82 ns` (🚀 **3.29x faster**) | `71.83 ns` (🚀 **2.88x faster**) | `71.74 ns` (🚀 **2.88x faster**) | +| **`1024`** | `212.41 us` (✅ **1.00x**) | `210.76 us` (✅ **1.01x faster**) | `64.71 us` (🚀 **3.28x faster**) | `74.10 us` (🚀 **2.87x faster**) | `73.30 us` (🚀 **2.90x faster**) | +| **`8192`** | `1.70 ms` (✅ **1.00x**) | `1.69 ms` (✅ **1.01x faster**) | `523.70 us` (🚀 **3.24x faster**) | `595.99 us` (🚀 **2.85x faster**) | `588.16 us` (🚀 **2.89x faster**) | +| **`32768`** | `6.86 ms` (✅ **1.00x**) | `6.82 ms` (✅ **1.01x faster**) | `2.07 ms` (🚀 **3.31x faster**) | `2.37 ms` (🚀 **2.89x faster**) | `2.35 ms` (🚀 **2.92x faster**) | + +### 160x160_inside_hor + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:--------------------------|:---------------------------------|:---------------------------------|:---------------------------------|:--------------------------------- | +| **`1`** | `206.54 ns` (✅ **1.00x**) | `208.54 ns` (✅ **1.01x slower**) | `64.29 ns` (🚀 **3.21x faster**) | `70.78 ns` (🚀 **2.92x faster**) | `70.85 ns` (🚀 **2.92x faster**) | +| **`1024`** | `212.85 us` (✅ **1.00x**) | `214.42 us` (✅ **1.01x slower**) | `66.03 us` (🚀 **3.22x faster**) | `72.22 us` (🚀 **2.95x faster**) | `72.90 us` (🚀 **2.92x faster**) | +| **`8192`** | `1.71 ms` (✅ **1.00x**) | `1.72 ms` (✅ **1.00x slower**) | `520.47 us` (🚀 **3.28x faster**) | `583.05 us` (🚀 **2.93x faster**) | `582.82 us` (🚀 **2.93x faster**) | +| **`32768`** | `6.79 ms` (✅ **1.00x**) | `6.84 ms` (✅ **1.01x slower**) | `2.07 ms` (🚀 **3.29x faster**) | `2.31 ms` (🚀 **2.94x faster**) | `2.32 ms` (🚀 **2.92x faster**) | + +### 160x160_inside_gentle + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:--------------------------|:---------------------------------|:---------------------------------|:---------------------------------|:--------------------------------- | +| **`1`** | `210.43 ns` (✅ **1.00x**) | `207.39 ns` (✅ **1.01x faster**) | `157.35 ns` (✅ **1.34x faster**) | `158.41 ns` (✅ **1.33x faster**) | `161.99 ns` (✅ **1.30x faster**) | +| **`1024`** | `214.75 us` (✅ **1.00x**) | `207.98 us` (✅ **1.03x faster**) | `160.44 us` (✅ **1.34x faster**) | `161.97 us` (✅ **1.33x faster**) | `165.89 us` (✅ **1.29x faster**) | +| **`8192`** | `1.72 ms` (✅ **1.00x**) | `1.66 ms` (✅ **1.03x faster**) | `1.28 ms` (✅ **1.34x faster**) | `1.30 ms` (✅ **1.33x faster**) | `1.33 ms` (✅ **1.29x faster**) | +| **`32768`** | `6.87 ms` (✅ **1.00x**) | `6.65 ms` (✅ **1.03x faster**) | `5.14 ms` (✅ **1.34x faster**) | `5.19 ms` (✅ **1.33x faster**) | `5.30 ms` (✅ **1.30x faster**) | + +### 160x160_inside_steep + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:--------------------------|:---------------------------------|:---------------------------------|:---------------------------------|:--------------------------------- | +| **`1`** | `206.37 ns` (✅ **1.00x**) | `217.45 ns` (✅ **1.05x slower**) | `171.48 ns` (✅ **1.20x faster**) | `156.90 ns` (✅ **1.32x faster**) | `160.01 ns` (✅ **1.29x faster**) | +| **`1024`** | `211.43 us` (✅ **1.00x**) | `222.79 us` (✅ **1.05x slower**) | `175.53 us` (✅ **1.20x faster**) | `160.38 us` (✅ **1.32x faster**) | `163.79 us` (✅ **1.29x faster**) | +| **`8192`** | `1.69 ms` (✅ **1.00x**) | `1.78 ms` (✅ **1.05x slower**) | `1.41 ms` (✅ **1.20x faster**) | `1.28 ms` (✅ **1.32x faster**) | `1.31 ms` (✅ **1.29x faster**) | +| **`32768`** | `6.76 ms` (✅ **1.00x**) | `7.13 ms` (✅ **1.05x slower**) | `5.62 ms` (✅ **1.20x faster**) | `5.13 ms` (✅ **1.32x faster**) | `5.25 ms` (✅ **1.29x faster**) | + +### 160x160_outside + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:--------------------------|:---------------------------------|:----------------------------------|:----------------------------------|:---------------------------------- | +| **`1`** | `206.08 ns` (✅ **1.00x**) | `204.10 ns` (✅ **1.01x faster**) | `4.99 ns` (🚀 **41.27x faster**) | `6.08 ns` (🚀 **33.92x faster**) | `6.26 ns` (🚀 **32.90x faster**) | +| **`1024`** | `211.36 us` (✅ **1.00x**) | `209.86 us` (✅ **1.01x faster**) | `4.55 us` (🚀 **46.44x faster**) | `6.08 us` (🚀 **34.79x faster**) | `6.02 us` (🚀 **35.09x faster**) | +| **`8192`** | `1.69 ms` (✅ **1.00x**) | `1.68 ms` (✅ **1.01x faster**) | `36.34 us` (🚀 **46.57x faster**) | `48.75 us` (🚀 **34.71x faster**) | `48.21 us` (🚀 **35.09x faster**) | +| **`32768`** | `6.77 ms` (✅ **1.00x**) | `6.71 ms` (✅ **1.01x faster**) | `145.64 us` (🚀 **46.49x faster**) | `195.52 us` (🚀 **34.63x faster**) | `193.11 us` (🚀 **35.07x faster**) | + +### 1280x1280_inside_vert + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:-------------------------|:--------------------------------|:---------------------------------|:---------------------------------|:--------------------------------- | +| **`1`** | `1.64 us` (✅ **1.00x**) | `1.62 us` (✅ **1.01x faster**) | `419.94 ns` (🚀 **3.91x faster**) | `430.34 ns` (🚀 **3.81x faster**) | `431.27 ns` (🚀 **3.80x faster**) | +| **`1024`** | `1.69 ms` (✅ **1.00x**) | `1.67 ms` (✅ **1.01x faster**) | `428.86 us` (🚀 **3.94x faster**) | `441.79 us` (🚀 **3.82x faster**) | `440.58 us` (🚀 **3.84x faster**) | +| **`8192`** | `13.54 ms` (✅ **1.00x**) | `13.32 ms` (✅ **1.02x faster**) | `3.43 ms` (🚀 **3.95x faster**) | `3.54 ms` (🚀 **3.83x faster**) | `3.53 ms` (🚀 **3.84x faster**) | +| **`32768`** | `54.08 ms` (✅ **1.00x**) | `53.32 ms` (✅ **1.01x faster**) | `13.75 ms` (🚀 **3.93x faster**) | `14.15 ms` (🚀 **3.82x faster**) | `14.10 ms` (🚀 **3.83x faster**) | + +### 1280x1280_inside_hor + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:-------------------------|:--------------------------------|:---------------------------------|:---------------------------------|:--------------------------------- | +| **`1`** | `1.66 us` (✅ **1.00x**) | `1.64 us` (✅ **1.01x faster**) | `419.82 ns` (🚀 **3.95x faster**) | `429.73 ns` (🚀 **3.86x faster**) | `428.72 ns` (🚀 **3.86x faster**) | +| **`1024`** | `1.70 ms` (✅ **1.00x**) | `1.67 ms` (✅ **1.01x faster**) | `429.17 us` (🚀 **3.96x faster**) | `440.99 us` (🚀 **3.85x faster**) | `439.01 us` (🚀 **3.87x faster**) | +| **`8192`** | `13.59 ms` (✅ **1.00x**) | `13.38 ms` (✅ **1.02x faster**) | `3.43 ms` (🚀 **3.96x faster**) | `3.52 ms` (🚀 **3.86x faster**) | `3.51 ms` (🚀 **3.87x faster**) | +| **`32768`** | `54.37 ms` (✅ **1.00x**) | `53.54 ms` (✅ **1.02x faster**) | `13.72 ms` (🚀 **3.96x faster**) | `14.09 ms` (🚀 **3.86x faster**) | `14.03 ms` (🚀 **3.88x faster**) | + +### 1280x1280_inside_gentle + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:-------------------------|:--------------------------------|:--------------------------------|:--------------------------------|:-------------------------------- | +| **`1`** | `1.67 us` (✅ **1.00x**) | `1.61 us` (✅ **1.03x faster**) | `1.28 us` (✅ **1.30x faster**) | `1.26 us` (✅ **1.33x faster**) | `1.30 us` (✅ **1.29x faster**) | +| **`1024`** | `1.71 ms` (✅ **1.00x**) | `1.63 ms` (✅ **1.05x faster**) | `1.32 ms` (✅ **1.30x faster**) | `1.29 ms` (✅ **1.33x faster**) | `1.33 ms` (✅ **1.29x faster**) | +| **`8192`** | `13.71 ms` (✅ **1.00x**) | `13.06 ms` (✅ **1.05x faster**) | `10.52 ms` (✅ **1.30x faster**) | `10.33 ms` (✅ **1.33x faster**) | `10.65 ms` (✅ **1.29x faster**) | +| **`32768`** | `54.74 ms` (✅ **1.00x**) | `52.24 ms` (✅ **1.05x faster**) | `42.05 ms` (✅ **1.30x faster**) | `41.32 ms` (✅ **1.32x faster**) | `42.62 ms` (✅ **1.28x faster**) | + +### 1280x1280_inside_steep + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:-------------------------|:--------------------------------|:--------------------------------|:--------------------------------|:-------------------------------- | +| **`1`** | `1.64 us` (✅ **1.00x**) | `1.73 us` (✅ **1.05x slower**) | `1.41 us` (✅ **1.16x faster**) | `1.25 us` (✅ **1.32x faster**) | `1.28 us` (✅ **1.29x faster**) | +| **`1024`** | `1.69 ms` (✅ **1.00x**) | `1.77 ms` (✅ **1.05x slower**) | `1.45 ms` (✅ **1.16x faster**) | `1.28 ms` (✅ **1.32x faster**) | `1.32 ms` (✅ **1.28x faster**) | +| **`8192`** | `13.47 ms` (✅ **1.00x**) | `14.20 ms` (✅ **1.05x slower**) | `11.59 ms` (✅ **1.16x faster**) | `10.21 ms` (✅ **1.32x faster**) | `10.48 ms` (✅ **1.29x faster**) | +| **`32768`** | `53.93 ms` (✅ **1.00x**) | `56.79 ms` (✅ **1.05x slower**) | `46.33 ms` (✅ **1.16x faster**) | `40.86 ms` (✅ **1.32x faster**) | `41.84 ms` (✅ **1.29x faster**) | + +### 1280x1280_outside + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:-------------------------|:--------------------------------|:-----------------------------------|:-----------------------------------|:----------------------------------- | +| **`1`** | `1.64 us` (✅ **1.00x**) | `1.61 us` (✅ **1.02x faster**) | `5.00 ns` (🚀 **328.60x faster**) | `6.08 ns` (🚀 **270.08x faster**) | `6.26 ns` (🚀 **262.50x faster**) | +| **`1024`** | `1.69 ms` (✅ **1.00x**) | `1.65 ms` (✅ **1.02x faster**) | `4.55 us` (🚀 **370.53x faster**) | `6.06 us` (🚀 **278.01x faster**) | `6.04 us` (🚀 **279.21x faster**) | +| **`8192`** | `13.51 ms` (✅ **1.00x**) | `13.25 ms` (✅ **1.02x faster**) | `36.34 us` (🚀 **371.66x faster**) | `48.67 us` (🚀 **277.51x faster**) | `48.24 us` (🚀 **279.98x faster**) | +| **`32768`** | `54.02 ms` (✅ **1.00x**) | `52.92 ms` (✅ **1.02x faster**) | `145.42 us` (🚀 **371.45x faster**) | `194.69 us` (🚀 **277.45x faster**) | `193.21 us` (🚀 **279.57x faster**) | + +### 5120x5120_inside_vert + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:--------------------------|:---------------------------------|:--------------------------------|:--------------------------------|:-------------------------------- | +| **`1`** | `6.46 us` (✅ **1.00x**) | `6.35 us` (✅ **1.02x faster**) | `1.65 us` (🚀 **3.92x faster**) | `1.67 us` (🚀 **3.87x faster**) | `1.66 us` (🚀 **3.89x faster**) | +| **`1024`** | `6.65 ms` (✅ **1.00x**) | `6.55 ms` (✅ **1.02x faster**) | `1.69 ms` (🚀 **3.94x faster**) | `1.72 ms` (🚀 **3.88x faster**) | `1.71 ms` (🚀 **3.90x faster**) | +| **`8192`** | `53.28 ms` (✅ **1.00x**) | `52.42 ms` (✅ **1.02x faster**) | `13.51 ms` (🚀 **3.94x faster**) | `13.79 ms` (🚀 **3.86x faster**) | `13.62 ms` (🚀 **3.91x faster**) | +| **`32768`** | `213.19 ms` (✅ **1.00x**) | `210.61 ms` (✅ **1.01x faster**) | `53.83 ms` (🚀 **3.96x faster**) | `54.83 ms` (🚀 **3.89x faster**) | `54.42 ms` (🚀 **3.92x faster**) | + +### 5120x5120_inside_hor + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:--------------------------|:---------------------------------|:--------------------------------|:--------------------------------|:-------------------------------- | +| **`1`** | `6.50 us` (✅ **1.00x**) | `6.41 us` (✅ **1.01x faster**) | `1.64 us` (🚀 **3.96x faster**) | `1.67 us` (🚀 **3.89x faster**) | `1.65 us` (🚀 **3.94x faster**) | +| **`1024`** | `6.67 ms` (✅ **1.00x**) | `6.56 ms` (✅ **1.02x faster**) | `1.68 ms` (🚀 **3.97x faster**) | `1.70 ms` (🚀 **3.93x faster**) | `1.69 ms` (🚀 **3.95x faster**) | +| **`8192`** | `53.32 ms` (✅ **1.00x**) | `52.46 ms` (✅ **1.02x faster**) | `13.43 ms` (🚀 **3.97x faster**) | `13.57 ms` (🚀 **3.93x faster**) | `13.51 ms` (🚀 **3.95x faster**) | +| **`32768`** | `217.12 ms` (✅ **1.00x**) | `211.33 ms` (✅ **1.03x faster**) | `54.16 ms` (🚀 **4.01x faster**) | `55.01 ms` (🚀 **3.95x faster**) | `55.24 ms` (🚀 **3.93x faster**) | + +### 5120x5120_inside_gentle + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:--------------------------|:---------------------------------|:---------------------------------|:---------------------------------|:--------------------------------- | +| **`1`** | `6.62 us` (✅ **1.00x**) | `6.42 us` (✅ **1.03x faster**) | `5.07 us` (✅ **1.31x faster**) | `4.99 us` (✅ **1.33x faster**) | `5.15 us` (✅ **1.28x faster**) | +| **`1024`** | `6.79 ms` (✅ **1.00x**) | `6.43 ms` (✅ **1.06x faster**) | `5.18 ms` (✅ **1.31x faster**) | `5.07 ms` (✅ **1.34x faster**) | `5.24 ms` (✅ **1.30x faster**) | +| **`8192`** | `54.03 ms` (✅ **1.00x**) | `51.33 ms` (✅ **1.05x faster**) | `41.87 ms` (✅ **1.29x faster**) | `41.04 ms` (✅ **1.32x faster**) | `41.91 ms` (✅ **1.29x faster**) | +| **`32768`** | `216.14 ms` (✅ **1.00x**) | `205.47 ms` (✅ **1.05x faster**) | `166.00 ms` (✅ **1.30x faster**) | `162.36 ms` (✅ **1.33x faster**) | `167.61 ms` (✅ **1.29x faster**) | + +### 5120x5120_inside_steep + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:--------------------------|:---------------------------------|:---------------------------------|:---------------------------------|:--------------------------------- | +| **`1`** | `6.49 us` (✅ **1.00x**) | `6.84 us` (✅ **1.05x slower**) | `5.57 us` (✅ **1.16x faster**) | `4.89 us` (✅ **1.33x faster**) | `5.01 us` (✅ **1.29x faster**) | +| **`1024`** | `6.64 ms` (✅ **1.00x**) | `7.01 ms` (✅ **1.05x slower**) | `5.73 ms` (✅ **1.16x faster**) | `5.01 ms` (✅ **1.33x faster**) | `5.14 ms` (✅ **1.29x faster**) | +| **`8192`** | `53.15 ms` (✅ **1.00x**) | `55.98 ms` (✅ **1.05x slower**) | `45.81 ms` (✅ **1.16x faster**) | `40.09 ms` (✅ **1.33x faster**) | `41.11 ms` (✅ **1.29x faster**) | +| **`32768`** | `212.37 ms` (✅ **1.00x**) | `223.94 ms` (✅ **1.05x slower**) | `183.01 ms` (✅ **1.16x faster**) | `160.38 ms` (✅ **1.32x faster**) | `164.41 ms` (✅ **1.29x faster**) | + +### 5120x5120_outside + +| | `bresenham` | `line_drawing` | `clipline(fn)` | `Clipline(iter)` | `Clipline(match-iter)` | +|:------------|:--------------------------|:---------------------------------|:------------------------------------|:------------------------------------|:------------------------------------ | +| **`1`** | `6.47 us` (✅ **1.00x**) | `6.32 us` (✅ **1.02x faster**) | `5.00 ns` (🚀 **1293.48x faster**) | `6.07 ns` (🚀 **1064.55x faster**) | `6.26 ns` (🚀 **1033.39x faster**) | +| **`1024`** | `6.66 ms` (✅ **1.00x**) | `6.52 ms` (✅ **1.02x faster**) | `4.55 us` (🚀 **1464.19x faster**) | `6.07 us` (🚀 **1097.84x faster**) | `6.01 us` (🚀 **1107.50x faster**) | +| **`8192`** | `53.24 ms` (✅ **1.00x**) | `52.07 ms` (✅ **1.02x faster**) | `36.39 us` (🚀 **1463.08x faster**) | `48.51 us` (🚀 **1097.39x faster**) | `48.03 us` (🚀 **1108.44x faster**) | +| **`32768`** | `212.83 ms` (✅ **1.00x**) | `208.42 ms` (✅ **1.02x faster**) | `145.39 us` (🚀 **1463.83x faster**) | `193.97 us` (🚀 **1097.27x faster**) | `193.91 us` (🚀 **1097.59x faster**) | + +--- +Made with [criterion-table](https://github.com/nu11ptr/criterion-table) + diff --git a/Cargo.toml b/Cargo.toml index ec91122..59ed70d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clipline" -version = "0.1.1" +version = "0.1.2" authors = ["Nurzhan Sakén "] edition = "2021" description = "Efficient scan conversion (rasterization) of line segments with clipping to a rectangular window." @@ -17,4 +17,13 @@ include = [ [features] default = ["func", "iter"] iter = [] -func = [] \ No newline at end of file +func = [] + +[dev-dependencies] +criterion = { version = "0.5.1"} +bresenham = "0.1.1" +line_drawing = "1.0.0" + +[[bench]] +name = "bresenham_comparison" +harness = false diff --git a/README.md b/README.md index 69a9c20..aa8ce0e 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,14 @@ clipping algorithms like [Cohen-Sutherland](https://en.wikipedia.org/wiki/Cohen% ![`clipline` in action](img/clip_anim.gif) +## Benchmarks + +[Benchmarks are available here](BENCHMARKS.md). I used [criterion.rs](https://github.com/bheisler/criterion.rs) to +compare +`clipline` to two +popular line drawing crates – `bresenham` and `line_drawing`, on a variety of +clipping window sizes, line orientations and counts. + ## Installation To use `clipline`, add it to your `Cargo.toml` file: @@ -52,7 +60,7 @@ for (x, y) in Clipline::new(line, clip_rect).unwrap() { draw_pixel(x, y); } -// C. Iterate over each `Clipline` case directly (faster, recommended) +// C. Iterate over each `Clipline` case directly match Clipline::new(line, clip_rect).unwrap() { Vlipline(pixels) => pixels.for_each(|(x, y)| draw_pixel(x, y)), Hlipline(pixels) => pixels.for_each(|(x, y)| draw_pixel(x, y)), diff --git a/benches/bresenham_comparison.rs b/benches/bresenham_comparison.rs new file mode 100644 index 0000000..e2c432d --- /dev/null +++ b/benches/bresenham_comparison.rs @@ -0,0 +1,153 @@ +use bresenham::Bresenham as BresenhamA; +use clipline::{clipline, Clipline}; +use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; +use line_drawing::Bresenham as BresenhamB; + +type Point = (isize, isize); +type Line = (Point, Point); +type Rect = (Point, Point); + +fn draw_pixel_checked(p: Point, clip: Rect) { + let (x, y) = p; + let ((wx1, wy1), (wx2, wy2)) = clip; + if black_box(x >= wx1 && x < wx2 && y >= wy1 && y < wy2) { + black_box((x, y)); + } +} + +fn draw_pixel_unchecked(p: Point) { + black_box(p); +} + +fn line_a(line: Line, clip: Rect) { + let (p1, p2) = line; + for p in BresenhamA::new(black_box(p1), black_box(p2)) { + draw_pixel_checked(p, clip) + } +} + +fn line_b(line: Line, clip: Rect) { + let (p1, p2) = line; + for p in BresenhamB::new(black_box(p1), black_box(p2)) { + draw_pixel_checked(p, clip) + } +} + +fn clipline_a(line: Line, clip: Rect) { + clipline(black_box(line), black_box(clip), |x, y| { + draw_pixel_unchecked((x, y)) + }); +} + +fn clipline_b(line: Line, clip: Rect) { + if let Some(clipline) = Clipline::new(black_box(line), black_box(clip)) { + for p in clipline { + draw_pixel_unchecked(p) + } + } +} + +fn clipline_c(line: Line, clip: Rect) { + if let Some(clipline) = Clipline::new(black_box(line), black_box(clip)) { + match clipline { + Clipline::Vlipline(ln) => ln.for_each(draw_pixel_unchecked), + Clipline::Hlipline(ln) => ln.for_each(draw_pixel_unchecked), + Clipline::Gentleham(ln) => ln.for_each(draw_pixel_unchecked), + Clipline::Steepnham(ln) => ln.for_each(draw_pixel_unchecked), + } + } +} + +fn bench_lines(c: &mut Criterion) { + let cases = [1, 8, 32].iter().flat_map(|mult| { + let (w, h) = (160 * mult, 160 * mult); + [ + ( + format!("{w}x{h}_inside_vert"), + (w, h), + ((w / 2, 0), (w / 2, h - 1)), + ), + ( + format!("{w}x{h}_inside_hor"), + (w, h), + ((0, h / 2), (w - 1, h / 2)), + ), + ( + format!("{w}x{h}_inside_gentle"), + (w, h), + ((0, h - 1), (w / 2, 0)), + ), + ( + format!("{w}x{h}_inside_steep"), + (w, h), + ((0, h - 1), (w - 1, h / 2)), + ), + (format!("{w}x{h}_outside"), (w, h), ((0, h), (w - 1, 2 * h))), + ] + }); + for (case_name, clip_size, line) in cases { + let mut group = c.benchmark_group(case_name); + for num_lines in [1, 1024, 8192, 32768] { + group.throughput(Throughput::Elements(num_lines)); + group.bench_with_input( + BenchmarkId::new("bresenham", num_lines), + &num_lines, + |b, &num_lines| { + b.iter(|| { + for _ in 0..num_lines { + line_a(line, ((0, 0), clip_size)); + } + }); + }, + ); + group.bench_with_input( + BenchmarkId::new("line_drawing", num_lines), + &num_lines, + |b, &num_lines| { + b.iter(|| { + for _ in 0..num_lines { + line_b(line, ((0, 0), clip_size)); + } + }); + }, + ); + group.bench_with_input( + BenchmarkId::new("clipline(fn)", num_lines), + &num_lines, + |b, &num_lines| { + b.iter(|| { + for _ in 0..num_lines { + clipline_a(line, ((0, 0), clip_size)); + } + }); + }, + ); + group.bench_with_input( + BenchmarkId::new("Clipline(iter)", num_lines), + &num_lines, + |b, &num_lines| { + b.iter(|| { + for _ in 0..num_lines { + clipline_b(line, ((0, 0), clip_size)); + } + }); + }, + ); + group.bench_with_input( + BenchmarkId::new("Clipline(match-iter)", num_lines), + &num_lines, + |b, &num_lines| { + b.iter(|| { + for _ in 0..num_lines { + clipline_c(line, ((0, 0), clip_size)); + } + }); + }, + ); + } + group.finish() + } +} + +criterion_group!(benches, bench_lines); +criterion_main!(benches); diff --git a/src/func.rs b/src/func.rs index 908b932..766df97 100644 --- a/src/func.rs +++ b/src/func.rs @@ -118,7 +118,7 @@ where mod tests { use super::*; - fn draw_pixel(_x: isize, _y: isize) {} + const fn draw_pixel(_x: isize, _y: isize) {} #[test] fn test_line_outside_clip() { diff --git a/src/iter.rs b/src/iter.rs index 6fe724d..4981d35 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -317,7 +317,8 @@ impl Hlipline { impl Bresenham { #[allow(clippy::too_many_arguments)] - fn new( + #[inline(always)] + const fn new( tx: isize, ty: isize, dx2: isize, @@ -343,12 +344,13 @@ impl Bresenham { impl Iterator for Clipline { type Item = Point; + #[inline] fn next(&mut self) -> Option { match self { - Clipline::Vlipline(iter) => iter.next(), - Clipline::Hlipline(iter) => iter.next(), - Clipline::Gentleham(iter) => iter.next(), - Clipline::Steepnham(iter) => iter.next(), + Self::Vlipline(iter) => iter.next(), + Self::Hlipline(iter) => iter.next(), + Self::Gentleham(iter) => iter.next(), + Self::Steepnham(iter) => iter.next(), } } } @@ -356,6 +358,7 @@ impl Iterator for Clipline { impl Iterator for Vlipline { type Item = Point; + #[inline] fn next(&mut self) -> Option { if self.y1 * self.sy > self.y2 * self.sy { return None; @@ -369,6 +372,7 @@ impl Iterator for Vlipline { impl Iterator for Hlipline { type Item = Point; + #[inline] fn next(&mut self) -> Option { if self.x1 * self.sx > self.x2 * self.sx { return None; @@ -382,6 +386,7 @@ impl Iterator for Hlipline { impl Iterator for Gentleham { type Item = Point; + #[inline] fn next(&mut self) -> Option { let Self(b) = self; if b.xd == b.term { @@ -396,6 +401,7 @@ impl Iterator for Gentleham { impl Iterator for Steepnham { type Item = Point; + #[inline] fn next(&mut self) -> Option { let Self(b) = self; if b.yd == b.term { diff --git a/src/util.rs b/src/util.rs index 6233bc0..b5ce35b 100644 --- a/src/util.rs +++ b/src/util.rs @@ -3,6 +3,7 @@ use core::cmp::{max, min}; pub type Point = (isize, isize); /// Standardizes the line segment (such that `x1 < x2 && y1 < y2`). +#[inline(always)] pub fn standardize( xy1: isize, xy2: isize, @@ -17,6 +18,7 @@ pub fn standardize( } #[allow(clippy::too_many_arguments)] +#[inline(always)] pub fn vertical_line( x: isize, y1: isize, @@ -35,13 +37,14 @@ pub fn vertical_line( return None; } let (cy1, cy2) = (max(y1, wy1), min(y2, wy2)); - for y in cy1..=cy2 { + for y in cy1..(cy2 + 1) { pixel_op(x, y); } Some((cy1, cy2)) } #[allow(clippy::too_many_arguments)] +#[inline(always)] pub fn horizontal_line( y: isize, x1: isize, @@ -62,13 +65,14 @@ pub fn horizontal_line( let (cx1, cx2) = (max(x1, wx1), min(x2, wx2)); // in practice it's better to fill the whole row in one operation, // but to keep the API simple we do it pixel-wise - for x in cx1..=cx2 { + for x in cx1..(cx2 + 1) { pixel_op(x, y); } Some((cx1, cx2)) } #[allow(clippy::too_many_arguments)] +#[inline(always)] pub fn clip_rect_entry( xy1: isize, yx1: isize, @@ -124,6 +128,7 @@ pub fn clip_rect_entry( } #[allow(clippy::too_many_arguments)] +#[inline(always)] pub fn clip_rect_exit( xy1: isize, xy2: isize, @@ -148,6 +153,7 @@ pub fn clip_rect_exit( } #[allow(clippy::too_many_arguments)] +#[inline(always)] pub fn destandardize( mut term: isize, mut xyd: isize, @@ -162,6 +168,7 @@ pub fn destandardize( (xyd, yxd, term) } +#[inline(always)] pub fn bresenham_step( mut err: isize, mut xyd: isize,