fuzzgpu 0.1.7__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/Cargo.lock +2 -2
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/PKG-INFO +95 -29
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/README.md +94 -28
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/Cargo.toml +1 -1
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/benches/bench.rs +60 -16
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/damerau.rs +604 -158
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/fuzz.rs +385 -32
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/gpu.rs +224 -80
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/jaro.rs +925 -249
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/levenshtein.rs +1453 -436
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/lib.rs +23 -16
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/needleman.rs +731 -188
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/simd.rs +464 -132
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/tests/differential.rs +130 -58
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/tests/kernel_registration.rs +18 -14
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-python/Cargo.toml +2 -2
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-python/src/lib.rs +269 -48
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-wasm/Cargo.toml +1 -1
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/pyproject.toml +1 -1
- fuzzgpu-0.2.0/python/fuzzgpu/distance/DamerauLevenshtein.py +145 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/distance/Jaro.py +3 -3
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/distance/JaroWinkler.py +3 -3
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/fuzz.py +54 -3
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/process.py +21 -14
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/tests/test_api_signatures.py +20 -7
- fuzzgpu-0.2.0/tests/test_gpu_health.py +102 -0
- fuzzgpu-0.2.0/tests/test_gpu_threshold_env.py +159 -0
- fuzzgpu-0.1.7/python/fuzzgpu/distance/DamerauLevenshtein.py +0 -43
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/Cargo.toml +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/LICENSE +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/assets/logo.png +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/shaders/damerau.wgsl +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/shaders/damerau_matrix.wgsl +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/shaders/jaro.wgsl +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/shaders/jaro_matrix.wgsl +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/shaders/levenshtein.wgsl +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/shaders/levenshtein_cdist_myers.wgsl +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/shaders/levenshtein_matrix.wgsl +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/shaders/levenshtein_myers.wgsl +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/shaders/levenshtein_short.wgsl +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/shaders/needleman_affine.wgsl +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/src/shaders/needleman_wavefront.wgsl +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/tests/differential.proptest-regressions +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-core/tests/fixtures/broken.wgsl +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-wasm/src/lib.rs +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-wasm/tests/differential_harness.js +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/crates/fuzzgpu-wasm/tests/js_api.test.cjs +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/__init__.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/__init__.pyi +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/distance/Hamming.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/distance/Indel.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/distance/LCSseq.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/distance/Levenshtein.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/distance/OSA.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/distance/Postfix.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/distance/Prefix.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/distance/__init__.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/distance/__init__.pyi +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/distance/_common.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/fuzz.pyi +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/python/fuzzgpu/process.pyi +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/tests/test_basic.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/tests/test_concurrency.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/tests/test_edge_cases.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/tests/test_invariants.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/tests/test_out_buffers.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/tests/test_rapidfuzz_compat.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/tests/test_stress.py +0 -0
- {fuzzgpu-0.1.7 → fuzzgpu-0.2.0}/tests/wasm_python_differential.py +0 -0
|
@@ -372,7 +372,7 @@ dependencies = [
|
|
|
372
372
|
|
|
373
373
|
[[package]]
|
|
374
374
|
name = "fuzzgpu-core"
|
|
375
|
-
version = "0.
|
|
375
|
+
version = "0.2.0"
|
|
376
376
|
dependencies = [
|
|
377
377
|
"bytemuck",
|
|
378
378
|
"criterion",
|
|
@@ -386,7 +386,7 @@ dependencies = [
|
|
|
386
386
|
|
|
387
387
|
[[package]]
|
|
388
388
|
name = "fuzzgpu-python"
|
|
389
|
-
version = "0.
|
|
389
|
+
version = "0.2.0"
|
|
390
390
|
dependencies = [
|
|
391
391
|
"fuzzgpu-core",
|
|
392
392
|
"log",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fuzzgpu
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -37,7 +37,7 @@ Project-URL: Repository, https://github.com/kuntal-devrat/fuzzgpu
|
|
|
37
37
|
|
|
38
38
|
*Cross-platform GPU compute via WebGPU (`wgpu`) & Multi-Core CPU parallelism with Rayon. Zero CUDA dependencies.*
|
|
39
39
|
|
|
40
|
-
[](https://pypi.org/project/fuzzgpu/)
|
|
41
41
|
[](https://opensource.org/licenses/MIT)
|
|
42
42
|
[](https://www.rust-lang.org)
|
|
43
43
|
[](https://github.com/kuntal-devrat/fuzzgpu)
|
|
@@ -60,28 +60,60 @@ No NVIDIA CUDA drivers or complex toolkits required.
|
|
|
60
60
|
|
|
61
61
|
---
|
|
62
62
|
|
|
63
|
-
## What's New in v0.
|
|
63
|
+
## What's New in v0.2.0
|
|
64
|
+
|
|
65
|
+
### Production hardening
|
|
66
|
+
- **Kernel `get()` panics eliminated** — all four GPU kernels (`GpuLevenshteinKernel`,
|
|
67
|
+
`GpuJaroKernel`, `GpuNeedlemanAffineKernel`, `GpuDamerauKernel`) previously called
|
|
68
|
+
`.unwrap()` on `OnceLock::get()` after initialization, which could panic the Python
|
|
69
|
+
interpreter under rare concurrent races. Replaced with `.ok_or_else(...)` returning
|
|
70
|
+
a proper `FuzzGpuError::NoDevice`.
|
|
71
|
+
- **`debug_assert!` → real release guards** — `MyersPattern::new`, `jaro_bitpar`, `jaro_4way`
|
|
72
|
+
were guarded only by `debug_assert!`. In release builds, calling them with inputs outside
|
|
73
|
+
their contract (non-ASCII or > 64 bytes) would silently produce wrong results. Promoted to
|
|
74
|
+
proper `assert!` with descriptive messages that surface immediately in both debug and release.
|
|
75
|
+
|
|
76
|
+
### New API surface
|
|
77
|
+
- **`fuzz.cdist`** — pairwise score matrix, mirrors `rapidfuzz.fuzz.cdist`. Delegates to
|
|
78
|
+
`process.cdist` with `ratio` as the default scorer.
|
|
79
|
+
- **`DamerauLevenshtein.editops` / `.opcodes`** — full Lowrance-Wagner traceback returning
|
|
80
|
+
`Editops` / `Opcodes` (insert/delete/replace), completing parity with rapidfuzz's alignment
|
|
81
|
+
API for this module.
|
|
82
|
+
- **`fuzz.__all__`** now includes `partial_ratio_alignment` and `cdist` (were missing).
|
|
83
|
+
|
|
84
|
+
### API correctness fixes
|
|
85
|
+
- **`Jaro.similarity` / `JaroWinkler.similarity` `score_cutoff`** — changed default from
|
|
86
|
+
`0.0` to `None`, matching rapidfuzz's semantics (`0.0` treated scores of exactly 0.0 as
|
|
87
|
+
filtered, which was wrong).
|
|
88
|
+
- **`ratio_batch(workers=)`** — was silently ignored (`del workers`). Now wires up a
|
|
89
|
+
`ThreadPoolExecutor` for the processor path; the no-processor path continues to use Rayon
|
|
90
|
+
internally (ignoring `workers` is correct there — Rayon already uses all cores).
|
|
91
|
+
- **`process.cdist` fast path** — rewrote to use `fuzz_ratio_batch` row-by-row (each row
|
|
92
|
+
runs under Rayon across all cores) instead of a dead `raw = _native.fuzz_ratio_batch`
|
|
93
|
+
assignment followed by the same loop. Removed the dead `raw:` type-hint-only line.
|
|
94
|
+
- **`process.cdist` silent swallow** — `except Exception: pass` replaced with
|
|
95
|
+
`warnings.warn(...)` so unexpected fast-path failures are visible instead of silently
|
|
96
|
+
producing slow results.
|
|
97
|
+
|
|
98
|
+
### Includes all v0.1.7 fixes
|
|
99
|
+
All fixes from v0.1.7 are included — see the v0.1.7 changelog below.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
<details>
|
|
104
|
+
<summary><b>Previous (v0.1.7)</b></summary>
|
|
64
105
|
|
|
65
106
|
### Bug fixes (Windows DX12 / Jaro shader)
|
|
66
107
|
- **Jaro GPU shader FXC crash fixed** — `jaro.wgsl` and `jaro_matrix.wgsl` used dynamic vector
|
|
67
|
-
component writes (`v[j >> 5u] = ...`) in `bit_set()`.
|
|
68
|
-
|
|
69
|
-
with `X3550`/`X3511` on every Windows DX12 runner — all 11 Jaro GPU tests were panicking.
|
|
70
|
-
Fixed by rewriting `bit_set` with `select()`-based static construction, which compiles
|
|
71
|
-
identically on all backends (Vulkan, Metal, DX12).
|
|
108
|
+
component writes (`v[j >> 5u] = ...`) in `bit_set()`. Fixed by rewriting `bit_set` with
|
|
109
|
+
`select()`-based static construction (Vulkan, Metal, DX12 all compile identically).
|
|
72
110
|
|
|
73
111
|
### Bug fixes (arity mismatch — wasm & fuzz crates)
|
|
74
|
-
- **`fuzzgpu-wasm`** —
|
|
75
|
-
|
|
76
|
-
the
|
|
77
|
-
`0.0` as the cutoff (no cutoff — identical to the previous behaviour).
|
|
78
|
-
- **`fuzzgpu-fuzz`** — same four callsites in `fuzz/src/lib.rs` had the same arity mismatch.
|
|
79
|
-
Fixed identically.
|
|
80
|
-
|
|
81
|
-
### Includes all v0.1.6 features
|
|
82
|
-
All features from v0.1.6 are included — see the v0.1.6 changelog below.
|
|
112
|
+
- **`fuzzgpu-wasm`** and **`fuzzgpu-fuzz`** — 4 × `E0061` arity mismatch for
|
|
113
|
+
`partial_ratio`/`token_sort_ratio`/`token_set_ratio`/`wratio`. Fixed by passing `0.0`
|
|
114
|
+
as the cutoff.
|
|
83
115
|
|
|
84
|
-
|
|
116
|
+
</details>
|
|
85
117
|
|
|
86
118
|
<details>
|
|
87
119
|
<summary><b>Previous (v0.1.6)</b></summary>
|
|
@@ -131,27 +163,61 @@ The full Python layer is now byte-identical to rapidfuzz 3.14.5 over a 169,744-p
|
|
|
131
163
|
|
|
132
164
|
## Benchmark Results
|
|
133
165
|
|
|
134
|
-
*Hardware: Intel(R) Iris(R) Xe Graphics (Vulkan) + Intel Core i7 (Rayon
|
|
135
|
-
*Versions: fuzzgpu 0.
|
|
166
|
+
*Hardware: Intel(R) Iris(R) Xe Graphics — integrated GPU (Vulkan) + Intel Core i7 (Rayon, all cores)*
|
|
167
|
+
*Versions: fuzzgpu 0.2.0 · rapidfuzz 3.14.5 · python-Levenshtein 0.27.4*
|
|
136
168
|
*Median of 7 runs after warmup. Reproduce: `python benchmarks/bench_compare.py`*
|
|
137
169
|
|
|
170
|
+
> **GPU class note:** These numbers are from an **integrated GPU** (iGPU), which shares memory
|
|
171
|
+
> bandwidth with the CPU and has a ~1 ms dispatch round-trip. On a **discrete GPU** (dGPU) the
|
|
172
|
+
> GPU columns improve significantly — expect 3–10× better GPU throughput and GPU routing kicking
|
|
173
|
+
> in at much smaller batch sizes (threshold drops from ~500 pairs to ~64 pairs automatically).
|
|
174
|
+
> Concurrency gains (multiple Python threads, after the wgpu fix ships) add a further 2–4×
|
|
175
|
+
> on top for server workloads regardless of GPU class.
|
|
176
|
+
|
|
138
177
|
### Levenshtein Batch (1 query × N candidates, 10-char strings)
|
|
139
178
|
| Batch Size | `fuzzgpu` (GPU) | `fuzzgpu` (CPU) | `rapidfuzz` | vs RF (GPU) | vs RF (CPU) |
|
|
140
179
|
| :--- | :---: | :---: | :---: | :---: | :---: |
|
|
141
|
-
| **100** | 0.
|
|
142
|
-
| **1,000** | 0.
|
|
143
|
-
| **10,000** |
|
|
144
|
-
| **50,000** |
|
|
180
|
+
| **100** | 0.04 ms | 0.00 ms | 0.01 ms | 0.24× | 1.90× |
|
|
181
|
+
| **1,000** | 0.50 ms | 0.04 ms | 0.07 ms | 0.13× | 1.89× |
|
|
182
|
+
| **10,000** | 1.53 ms | 0.40 ms | 0.64 ms | 0.42× | 1.61× |
|
|
183
|
+
| **50,000** | 6.87 ms | 3.36 ms | 4.46 ms | 0.65× | 1.33× |
|
|
145
184
|
|
|
146
185
|
### Damerau-Levenshtein Batch (unrestricted Lowrance-Wagner)
|
|
147
186
|
| Batch Size | `fuzzgpu` (GPU) | `fuzzgpu` (CPU) | `rapidfuzz` | vs RF (GPU) | vs RF (CPU) |
|
|
148
187
|
| :--- | :---: | :---: | :---: | :---: | :---: |
|
|
149
|
-
| **
|
|
150
|
-
| **
|
|
151
|
-
| **
|
|
188
|
+
| **100** | 0.04 ms | 0.04 ms | 0.10 ms | 2.68× | 2.63× |
|
|
189
|
+
| **1,000** | 0.21 ms | 0.20 ms | 0.98 ms | 4.71× | 4.89× |
|
|
190
|
+
| **10,000** | 1.39 ms | 1.49 ms | 9.93 ms | 7.14× | 6.67× |
|
|
191
|
+
| **50,000** | 10.19 ms | 9.91 ms | 66.57 ms | 6.54× | 6.72× |
|
|
152
192
|
|
|
153
193
|
> **Note:** rapidfuzz's `DamerauLevenshtein` uses Optimal String Alignment (OSA). fuzzgpu implements the **unrestricted** Lowrance-Wagner (1975) algorithm which allows non-adjacent transpositions. For example: `damerau("ca", "abc") == 2` (fuzzgpu) vs `3` (rapidfuzz OSA). Use `fuzzgpu.distance.OSA` for OSA-compatible semantics.
|
|
154
194
|
|
|
195
|
+
### Jaro-Winkler Batch (p = 0.1)
|
|
196
|
+
| Batch Size | `fuzzgpu` (GPU) | `fuzzgpu` (CPU) | `rapidfuzz` | vs RF (GPU) | vs RF (CPU) |
|
|
197
|
+
| :--- | :---: | :---: | :---: | :---: | :---: |
|
|
198
|
+
| **100** | 0.02 ms | 0.01 ms | 0.02 ms | 0.87× | 1.14× |
|
|
199
|
+
| **1,000** | 0.18 ms | 0.13 ms | 0.12 ms | 0.70× | 0.94× |
|
|
200
|
+
| **10,000** | 1.01 ms | 0.83 ms | 0.97 ms | 0.96× | 1.18× |
|
|
201
|
+
| **50,000** | 5.61 ms | 3.25 ms | 6.69 ms | 1.19× | 2.06× |
|
|
202
|
+
|
|
203
|
+
### Needleman-Wunsch Affine Batch (match=1, mismatch=-1, gap_open=-2, gap_extend=-1)
|
|
204
|
+
| Batch Size | `fuzzgpu` (GPU) | `fuzzgpu` (CPU) | `rapidfuzz` |
|
|
205
|
+
| :--- | :---: | :---: | :---: |
|
|
206
|
+
| **100** | 0.07 ms | 0.05 ms | — |
|
|
207
|
+
| **1,000** | 1.65 ms | 0.43 ms | — |
|
|
208
|
+
| **10,000** | 12.49 ms | 3.91 ms | — |
|
|
209
|
+
| **50,000** | 47.86 ms | 26.95 ms | — |
|
|
210
|
+
|
|
211
|
+
> rapidfuzz has no Needleman-Wunsch API — no comparison available.
|
|
212
|
+
|
|
213
|
+
### Levenshtein Cross-Product Matrix (`cdist`)
|
|
214
|
+
| Matrix Size | Total Pairs | `fuzzgpu` (GPU) | `fuzzgpu` (CPU) | `rapidfuzz` | python-Levenshtein | vs RF (GPU) | vs RF (CPU) |
|
|
215
|
+
| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
|
216
|
+
| **10 × 10** | 100 | 0.05 ms | 0.02 ms | 0.01 ms | 0.06 ms | 0.18× | 0.56× |
|
|
217
|
+
| **50 × 50** | 2,500 | 0.58 ms | 0.11 ms | 0.09 ms | 2.17 ms | 0.16× | 0.81× |
|
|
218
|
+
| **100 × 100** | 10,000 | 0.67 ms | 0.25 ms | 0.18 ms | 5.75 ms | 0.27× | 0.70× |
|
|
219
|
+
| **200 × 200** | 40,000 | 1.19 ms | 0.81 ms | 0.61 ms | 22.31 ms | 0.51× | 0.75× |
|
|
220
|
+
|
|
155
221
|
---
|
|
156
222
|
|
|
157
223
|
## Installation
|
|
@@ -163,7 +229,7 @@ pip install fuzzgpu
|
|
|
163
229
|
```toml
|
|
164
230
|
# Rust
|
|
165
231
|
[dependencies]
|
|
166
|
-
fuzzgpu-core = "0.
|
|
232
|
+
fuzzgpu-core = "0.2.0"
|
|
167
233
|
```
|
|
168
234
|
|
|
169
235
|
---
|
|
@@ -259,7 +325,7 @@ fuzzgpu.set_cpu_only(True) # force CPU-only mode
|
|
|
259
325
|
|
|
260
326
|
```toml
|
|
261
327
|
[dependencies]
|
|
262
|
-
fuzzgpu-core = "0.
|
|
328
|
+
fuzzgpu-core = "0.2.0" # GPU + CPU fallback
|
|
263
329
|
# fuzzgpu-core = { version = "0.1.7", default-features = false } # CPU-only
|
|
264
330
|
```
|
|
265
331
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
*Cross-platform GPU compute via WebGPU (`wgpu`) & Multi-Core CPU parallelism with Rayon. Zero CUDA dependencies.*
|
|
10
10
|
|
|
11
|
-
[](https://pypi.org/project/fuzzgpu/)
|
|
12
12
|
[](https://opensource.org/licenses/MIT)
|
|
13
13
|
[](https://www.rust-lang.org)
|
|
14
14
|
[](https://github.com/kuntal-devrat/fuzzgpu)
|
|
@@ -31,28 +31,60 @@ No NVIDIA CUDA drivers or complex toolkits required.
|
|
|
31
31
|
|
|
32
32
|
---
|
|
33
33
|
|
|
34
|
-
## What's New in v0.
|
|
34
|
+
## What's New in v0.2.0
|
|
35
|
+
|
|
36
|
+
### Production hardening
|
|
37
|
+
- **Kernel `get()` panics eliminated** — all four GPU kernels (`GpuLevenshteinKernel`,
|
|
38
|
+
`GpuJaroKernel`, `GpuNeedlemanAffineKernel`, `GpuDamerauKernel`) previously called
|
|
39
|
+
`.unwrap()` on `OnceLock::get()` after initialization, which could panic the Python
|
|
40
|
+
interpreter under rare concurrent races. Replaced with `.ok_or_else(...)` returning
|
|
41
|
+
a proper `FuzzGpuError::NoDevice`.
|
|
42
|
+
- **`debug_assert!` → real release guards** — `MyersPattern::new`, `jaro_bitpar`, `jaro_4way`
|
|
43
|
+
were guarded only by `debug_assert!`. In release builds, calling them with inputs outside
|
|
44
|
+
their contract (non-ASCII or > 64 bytes) would silently produce wrong results. Promoted to
|
|
45
|
+
proper `assert!` with descriptive messages that surface immediately in both debug and release.
|
|
46
|
+
|
|
47
|
+
### New API surface
|
|
48
|
+
- **`fuzz.cdist`** — pairwise score matrix, mirrors `rapidfuzz.fuzz.cdist`. Delegates to
|
|
49
|
+
`process.cdist` with `ratio` as the default scorer.
|
|
50
|
+
- **`DamerauLevenshtein.editops` / `.opcodes`** — full Lowrance-Wagner traceback returning
|
|
51
|
+
`Editops` / `Opcodes` (insert/delete/replace), completing parity with rapidfuzz's alignment
|
|
52
|
+
API for this module.
|
|
53
|
+
- **`fuzz.__all__`** now includes `partial_ratio_alignment` and `cdist` (were missing).
|
|
54
|
+
|
|
55
|
+
### API correctness fixes
|
|
56
|
+
- **`Jaro.similarity` / `JaroWinkler.similarity` `score_cutoff`** — changed default from
|
|
57
|
+
`0.0` to `None`, matching rapidfuzz's semantics (`0.0` treated scores of exactly 0.0 as
|
|
58
|
+
filtered, which was wrong).
|
|
59
|
+
- **`ratio_batch(workers=)`** — was silently ignored (`del workers`). Now wires up a
|
|
60
|
+
`ThreadPoolExecutor` for the processor path; the no-processor path continues to use Rayon
|
|
61
|
+
internally (ignoring `workers` is correct there — Rayon already uses all cores).
|
|
62
|
+
- **`process.cdist` fast path** — rewrote to use `fuzz_ratio_batch` row-by-row (each row
|
|
63
|
+
runs under Rayon across all cores) instead of a dead `raw = _native.fuzz_ratio_batch`
|
|
64
|
+
assignment followed by the same loop. Removed the dead `raw:` type-hint-only line.
|
|
65
|
+
- **`process.cdist` silent swallow** — `except Exception: pass` replaced with
|
|
66
|
+
`warnings.warn(...)` so unexpected fast-path failures are visible instead of silently
|
|
67
|
+
producing slow results.
|
|
68
|
+
|
|
69
|
+
### Includes all v0.1.7 fixes
|
|
70
|
+
All fixes from v0.1.7 are included — see the v0.1.7 changelog below.
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
<details>
|
|
75
|
+
<summary><b>Previous (v0.1.7)</b></summary>
|
|
35
76
|
|
|
36
77
|
### Bug fixes (Windows DX12 / Jaro shader)
|
|
37
78
|
- **Jaro GPU shader FXC crash fixed** — `jaro.wgsl` and `jaro_matrix.wgsl` used dynamic vector
|
|
38
|
-
component writes (`v[j >> 5u] = ...`) in `bit_set()`.
|
|
39
|
-
|
|
40
|
-
with `X3550`/`X3511` on every Windows DX12 runner — all 11 Jaro GPU tests were panicking.
|
|
41
|
-
Fixed by rewriting `bit_set` with `select()`-based static construction, which compiles
|
|
42
|
-
identically on all backends (Vulkan, Metal, DX12).
|
|
79
|
+
component writes (`v[j >> 5u] = ...`) in `bit_set()`. Fixed by rewriting `bit_set` with
|
|
80
|
+
`select()`-based static construction (Vulkan, Metal, DX12 all compile identically).
|
|
43
81
|
|
|
44
82
|
### Bug fixes (arity mismatch — wasm & fuzz crates)
|
|
45
|
-
- **`fuzzgpu-wasm`** —
|
|
46
|
-
|
|
47
|
-
the
|
|
48
|
-
`0.0` as the cutoff (no cutoff — identical to the previous behaviour).
|
|
49
|
-
- **`fuzzgpu-fuzz`** — same four callsites in `fuzz/src/lib.rs` had the same arity mismatch.
|
|
50
|
-
Fixed identically.
|
|
51
|
-
|
|
52
|
-
### Includes all v0.1.6 features
|
|
53
|
-
All features from v0.1.6 are included — see the v0.1.6 changelog below.
|
|
83
|
+
- **`fuzzgpu-wasm`** and **`fuzzgpu-fuzz`** — 4 × `E0061` arity mismatch for
|
|
84
|
+
`partial_ratio`/`token_sort_ratio`/`token_set_ratio`/`wratio`. Fixed by passing `0.0`
|
|
85
|
+
as the cutoff.
|
|
54
86
|
|
|
55
|
-
|
|
87
|
+
</details>
|
|
56
88
|
|
|
57
89
|
<details>
|
|
58
90
|
<summary><b>Previous (v0.1.6)</b></summary>
|
|
@@ -102,27 +134,61 @@ The full Python layer is now byte-identical to rapidfuzz 3.14.5 over a 169,744-p
|
|
|
102
134
|
|
|
103
135
|
## Benchmark Results
|
|
104
136
|
|
|
105
|
-
*Hardware: Intel(R) Iris(R) Xe Graphics (Vulkan) + Intel Core i7 (Rayon
|
|
106
|
-
*Versions: fuzzgpu 0.
|
|
137
|
+
*Hardware: Intel(R) Iris(R) Xe Graphics — integrated GPU (Vulkan) + Intel Core i7 (Rayon, all cores)*
|
|
138
|
+
*Versions: fuzzgpu 0.2.0 · rapidfuzz 3.14.5 · python-Levenshtein 0.27.4*
|
|
107
139
|
*Median of 7 runs after warmup. Reproduce: `python benchmarks/bench_compare.py`*
|
|
108
140
|
|
|
141
|
+
> **GPU class note:** These numbers are from an **integrated GPU** (iGPU), which shares memory
|
|
142
|
+
> bandwidth with the CPU and has a ~1 ms dispatch round-trip. On a **discrete GPU** (dGPU) the
|
|
143
|
+
> GPU columns improve significantly — expect 3–10× better GPU throughput and GPU routing kicking
|
|
144
|
+
> in at much smaller batch sizes (threshold drops from ~500 pairs to ~64 pairs automatically).
|
|
145
|
+
> Concurrency gains (multiple Python threads, after the wgpu fix ships) add a further 2–4×
|
|
146
|
+
> on top for server workloads regardless of GPU class.
|
|
147
|
+
|
|
109
148
|
### Levenshtein Batch (1 query × N candidates, 10-char strings)
|
|
110
149
|
| Batch Size | `fuzzgpu` (GPU) | `fuzzgpu` (CPU) | `rapidfuzz` | vs RF (GPU) | vs RF (CPU) |
|
|
111
150
|
| :--- | :---: | :---: | :---: | :---: | :---: |
|
|
112
|
-
| **100** | 0.
|
|
113
|
-
| **1,000** | 0.
|
|
114
|
-
| **10,000** |
|
|
115
|
-
| **50,000** |
|
|
151
|
+
| **100** | 0.04 ms | 0.00 ms | 0.01 ms | 0.24× | 1.90× |
|
|
152
|
+
| **1,000** | 0.50 ms | 0.04 ms | 0.07 ms | 0.13× | 1.89× |
|
|
153
|
+
| **10,000** | 1.53 ms | 0.40 ms | 0.64 ms | 0.42× | 1.61× |
|
|
154
|
+
| **50,000** | 6.87 ms | 3.36 ms | 4.46 ms | 0.65× | 1.33× |
|
|
116
155
|
|
|
117
156
|
### Damerau-Levenshtein Batch (unrestricted Lowrance-Wagner)
|
|
118
157
|
| Batch Size | `fuzzgpu` (GPU) | `fuzzgpu` (CPU) | `rapidfuzz` | vs RF (GPU) | vs RF (CPU) |
|
|
119
158
|
| :--- | :---: | :---: | :---: | :---: | :---: |
|
|
120
|
-
| **
|
|
121
|
-
| **
|
|
122
|
-
| **
|
|
159
|
+
| **100** | 0.04 ms | 0.04 ms | 0.10 ms | 2.68× | 2.63× |
|
|
160
|
+
| **1,000** | 0.21 ms | 0.20 ms | 0.98 ms | 4.71× | 4.89× |
|
|
161
|
+
| **10,000** | 1.39 ms | 1.49 ms | 9.93 ms | 7.14× | 6.67× |
|
|
162
|
+
| **50,000** | 10.19 ms | 9.91 ms | 66.57 ms | 6.54× | 6.72× |
|
|
123
163
|
|
|
124
164
|
> **Note:** rapidfuzz's `DamerauLevenshtein` uses Optimal String Alignment (OSA). fuzzgpu implements the **unrestricted** Lowrance-Wagner (1975) algorithm which allows non-adjacent transpositions. For example: `damerau("ca", "abc") == 2` (fuzzgpu) vs `3` (rapidfuzz OSA). Use `fuzzgpu.distance.OSA` for OSA-compatible semantics.
|
|
125
165
|
|
|
166
|
+
### Jaro-Winkler Batch (p = 0.1)
|
|
167
|
+
| Batch Size | `fuzzgpu` (GPU) | `fuzzgpu` (CPU) | `rapidfuzz` | vs RF (GPU) | vs RF (CPU) |
|
|
168
|
+
| :--- | :---: | :---: | :---: | :---: | :---: |
|
|
169
|
+
| **100** | 0.02 ms | 0.01 ms | 0.02 ms | 0.87× | 1.14× |
|
|
170
|
+
| **1,000** | 0.18 ms | 0.13 ms | 0.12 ms | 0.70× | 0.94× |
|
|
171
|
+
| **10,000** | 1.01 ms | 0.83 ms | 0.97 ms | 0.96× | 1.18× |
|
|
172
|
+
| **50,000** | 5.61 ms | 3.25 ms | 6.69 ms | 1.19× | 2.06× |
|
|
173
|
+
|
|
174
|
+
### Needleman-Wunsch Affine Batch (match=1, mismatch=-1, gap_open=-2, gap_extend=-1)
|
|
175
|
+
| Batch Size | `fuzzgpu` (GPU) | `fuzzgpu` (CPU) | `rapidfuzz` |
|
|
176
|
+
| :--- | :---: | :---: | :---: |
|
|
177
|
+
| **100** | 0.07 ms | 0.05 ms | — |
|
|
178
|
+
| **1,000** | 1.65 ms | 0.43 ms | — |
|
|
179
|
+
| **10,000** | 12.49 ms | 3.91 ms | — |
|
|
180
|
+
| **50,000** | 47.86 ms | 26.95 ms | — |
|
|
181
|
+
|
|
182
|
+
> rapidfuzz has no Needleman-Wunsch API — no comparison available.
|
|
183
|
+
|
|
184
|
+
### Levenshtein Cross-Product Matrix (`cdist`)
|
|
185
|
+
| Matrix Size | Total Pairs | `fuzzgpu` (GPU) | `fuzzgpu` (CPU) | `rapidfuzz` | python-Levenshtein | vs RF (GPU) | vs RF (CPU) |
|
|
186
|
+
| :--- | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
|
187
|
+
| **10 × 10** | 100 | 0.05 ms | 0.02 ms | 0.01 ms | 0.06 ms | 0.18× | 0.56× |
|
|
188
|
+
| **50 × 50** | 2,500 | 0.58 ms | 0.11 ms | 0.09 ms | 2.17 ms | 0.16× | 0.81× |
|
|
189
|
+
| **100 × 100** | 10,000 | 0.67 ms | 0.25 ms | 0.18 ms | 5.75 ms | 0.27× | 0.70× |
|
|
190
|
+
| **200 × 200** | 40,000 | 1.19 ms | 0.81 ms | 0.61 ms | 22.31 ms | 0.51× | 0.75× |
|
|
191
|
+
|
|
126
192
|
---
|
|
127
193
|
|
|
128
194
|
## Installation
|
|
@@ -134,7 +200,7 @@ pip install fuzzgpu
|
|
|
134
200
|
```toml
|
|
135
201
|
# Rust
|
|
136
202
|
[dependencies]
|
|
137
|
-
fuzzgpu-core = "0.
|
|
203
|
+
fuzzgpu-core = "0.2.0"
|
|
138
204
|
```
|
|
139
205
|
|
|
140
206
|
---
|
|
@@ -230,7 +296,7 @@ fuzzgpu.set_cpu_only(True) # force CPU-only mode
|
|
|
230
296
|
|
|
231
297
|
```toml
|
|
232
298
|
[dependencies]
|
|
233
|
-
fuzzgpu-core = "0.
|
|
299
|
+
fuzzgpu-core = "0.2.0" # GPU + CPU fallback
|
|
234
300
|
# fuzzgpu-core = { version = "0.1.7", default-features = false } # CPU-only
|
|
235
301
|
```
|
|
236
302
|
|
|
@@ -13,7 +13,9 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
|
|
13
13
|
use fuzzgpu_core::damerau::damerau_levenshtein_batch;
|
|
14
14
|
use fuzzgpu_core::fuzz::ratio;
|
|
15
15
|
use fuzzgpu_core::jaro::{jaro_winkler, jaro_winkler_batch};
|
|
16
|
-
use fuzzgpu_core::levenshtein::{
|
|
16
|
+
use fuzzgpu_core::levenshtein::{
|
|
17
|
+
levenshtein_cdist_cpu, levenshtein_distance_raw, LevenshteinKernel,
|
|
18
|
+
};
|
|
17
19
|
use fuzzgpu_core::needleman::{needleman_wunsch_affine, needleman_wunsch_affine_batch};
|
|
18
20
|
use fuzzgpu_core::simd::levenshtein_myers;
|
|
19
21
|
|
|
@@ -24,7 +26,9 @@ fn gen_strings(count: usize, len: usize, seed: u64) -> Vec<String> {
|
|
|
24
26
|
for _ in 0..count {
|
|
25
27
|
let mut s = String::with_capacity(len);
|
|
26
28
|
for _ in 0..len {
|
|
27
|
-
state = state
|
|
29
|
+
state = state
|
|
30
|
+
.wrapping_mul(6364136223846793005)
|
|
31
|
+
.wrapping_add(1442695040888963407);
|
|
28
32
|
s.push((b'a' + ((state >> 33) as u8 % 26)) as char);
|
|
29
33
|
}
|
|
30
34
|
out.push(s);
|
|
@@ -47,7 +51,11 @@ fn cpu_benches(c: &mut Criterion) {
|
|
|
47
51
|
let long = gen_strings(2, 256, 3);
|
|
48
52
|
|
|
49
53
|
let mut g = c.benchmark_group("scalar");
|
|
50
|
-
for (name, s) in [
|
|
54
|
+
for (name, s) in [
|
|
55
|
+
("short_8", &short),
|
|
56
|
+
("medium_64", &medium),
|
|
57
|
+
("long_256", &long),
|
|
58
|
+
] {
|
|
51
59
|
let (s1, s2) = (&s[0], &s[1]);
|
|
52
60
|
g.bench_function(format!("levenshtein/{name}"), |b| {
|
|
53
61
|
b.iter(|| black_box(levenshtein_distance_raw(s1, s2)))
|
|
@@ -62,7 +70,11 @@ fn cpu_benches(c: &mut Criterion) {
|
|
|
62
70
|
b.iter(|| black_box(ratio(s1, s2)))
|
|
63
71
|
});
|
|
64
72
|
g.bench_function(format!("needleman_affine/{name}"), |b| {
|
|
65
|
-
b.iter(||
|
|
73
|
+
b.iter(|| {
|
|
74
|
+
black_box(needleman_wunsch_affine(
|
|
75
|
+
s1, s2, MATCH, MISMATCH, GAP_OPEN, GAP_EXTEND,
|
|
76
|
+
))
|
|
77
|
+
})
|
|
66
78
|
});
|
|
67
79
|
g.bench_function(format!("damerau/{name}"), |b| {
|
|
68
80
|
b.iter(|| black_box(fuzzgpu_core::damerau::damerau_levenshtein_distance(s1, s2)))
|
|
@@ -89,14 +101,27 @@ fn cpu_benches(c: &mut Criterion) {
|
|
|
89
101
|
b.iter(|| black_box(jaro_winkler_batch(query, &cand_refs, 0.1)))
|
|
90
102
|
});
|
|
91
103
|
g.bench_function("needleman_affine_batch_1000", |b| {
|
|
92
|
-
b.iter(||
|
|
104
|
+
b.iter(|| {
|
|
105
|
+
black_box(needleman_wunsch_affine_batch(
|
|
106
|
+
query, &cand_refs, MATCH, MISMATCH, GAP_OPEN, GAP_EXTEND,
|
|
107
|
+
))
|
|
108
|
+
})
|
|
93
109
|
});
|
|
94
110
|
// Long-string needleman (80 chars): the anti-diagonal wavefront GPU path's
|
|
95
111
|
// target workload; Rayon CPU is the comparison baseline.
|
|
96
112
|
let cands_nw80 = gen_strings(1000, 80, 0xAB);
|
|
97
113
|
let cand_refs_nw80: Vec<&str> = cands_nw80.iter().map(|s| s.as_str()).collect();
|
|
98
114
|
g.bench_function("needleman_affine_batch_1000_long80_large", |b| {
|
|
99
|
-
b.iter(||
|
|
115
|
+
b.iter(|| {
|
|
116
|
+
black_box(needleman_wunsch_affine_batch(
|
|
117
|
+
query,
|
|
118
|
+
&cand_refs_nw80,
|
|
119
|
+
MATCH,
|
|
120
|
+
MISMATCH,
|
|
121
|
+
GAP_OPEN,
|
|
122
|
+
GAP_EXTEND,
|
|
123
|
+
))
|
|
124
|
+
})
|
|
100
125
|
});
|
|
101
126
|
g.bench_function("damerau_batch_1000", |b| {
|
|
102
127
|
b.iter(|| black_box(damerau_levenshtein_batch(query, &cand_refs)))
|
|
@@ -128,8 +153,9 @@ fn cpu_benches(c: &mut Criterion) {
|
|
|
128
153
|
b.iter(|| black_box(levenshtein_cdist_cpu(&refs_a_1k, &refs_b_1k)))
|
|
129
154
|
});
|
|
130
155
|
// Sequential 10 x 1000 pairs: the CPU baseline for the batched-GPU bench.
|
|
131
|
-
let cands10: Vec<Vec<String>> =
|
|
132
|
-
|
|
156
|
+
let cands10: Vec<Vec<String>> = (0..10)
|
|
157
|
+
.map(|i| gen_strings(1000, 16, 0x100 + i as u64))
|
|
158
|
+
.collect();
|
|
133
159
|
let ops10: Vec<Vec<(&str, &str)>> = cands10.iter().map(|c| make_pairs(query, c)).collect();
|
|
134
160
|
g.bench_function("levenshtein_10x1000_seq_cpu", |b| {
|
|
135
161
|
b.iter(|| {
|
|
@@ -190,13 +216,23 @@ fn gpu_benches(c: &mut Criterion) {
|
|
|
190
216
|
match GpuNeedlemanAffineKernel::get() {
|
|
191
217
|
Ok(kernel) => {
|
|
192
218
|
g.bench_function("needleman_affine_batch_1000", |b| {
|
|
193
|
-
b.iter(||
|
|
219
|
+
b.iter(|| {
|
|
220
|
+
black_box(kernel.compute_batch(&pairs, MATCH, MISMATCH, GAP_OPEN, GAP_EXTEND))
|
|
221
|
+
})
|
|
194
222
|
});
|
|
195
223
|
// 80-char pairs: routes to the anti-diagonal wavefront kernel.
|
|
196
224
|
let cands_nw80 = gen_strings(1000, 80, 0xAB);
|
|
197
225
|
let pairs_nw80 = make_pairs(query, &cands_nw80);
|
|
198
226
|
g.bench_function("needleman_affine_batch_1000_long80_large", |b| {
|
|
199
|
-
b.iter(||
|
|
227
|
+
b.iter(|| {
|
|
228
|
+
black_box(kernel.compute_batch(
|
|
229
|
+
&pairs_nw80,
|
|
230
|
+
MATCH,
|
|
231
|
+
MISMATCH,
|
|
232
|
+
GAP_OPEN,
|
|
233
|
+
GAP_EXTEND,
|
|
234
|
+
))
|
|
235
|
+
})
|
|
200
236
|
});
|
|
201
237
|
}
|
|
202
238
|
Err(e) => {
|
|
@@ -247,9 +283,11 @@ fn gpu_benches(c: &mut Criterion) {
|
|
|
247
283
|
});
|
|
248
284
|
// 10 x 1000 pairs: one batched dispatch+readback vs 10 sequential
|
|
249
285
|
// sync-round-trips. Same total work as cpu_batch/..._seq_cpu.
|
|
250
|
-
let cands10: Vec<Vec<String>> =
|
|
251
|
-
|
|
252
|
-
|
|
286
|
+
let cands10: Vec<Vec<String>> = (0..10)
|
|
287
|
+
.map(|i| gen_strings(1000, 16, 0x100 + i as u64))
|
|
288
|
+
.collect();
|
|
289
|
+
let ops10: Vec<Vec<(&str, &str)>> =
|
|
290
|
+
cands10.iter().map(|c| make_pairs(query, c)).collect();
|
|
253
291
|
g.bench_function("levenshtein_10x1000_seq", |b| {
|
|
254
292
|
b.iter(|| {
|
|
255
293
|
for op in &ops10 {
|
|
@@ -268,10 +306,16 @@ fn gpu_benches(c: &mut Criterion) {
|
|
|
268
306
|
});
|
|
269
307
|
}
|
|
270
308
|
Err(e) => {
|
|
271
|
-
eprintln!(
|
|
309
|
+
eprintln!(
|
|
310
|
+
"WARN: no GPU device, skipping large-scale GpuLevenshteinKernel benches: {e}"
|
|
311
|
+
);
|
|
272
312
|
g.bench_function("levenshtein_batch_100k_large", |b| b.iter(|| black_box(0)));
|
|
273
|
-
g.bench_function("levenshtein_batch_1000_long256_large", |b|
|
|
274
|
-
|
|
313
|
+
g.bench_function("levenshtein_batch_1000_long256_large", |b| {
|
|
314
|
+
b.iter(|| black_box(0))
|
|
315
|
+
});
|
|
316
|
+
g.bench_function("levenshtein_cdist_1000x1000_large", |b| {
|
|
317
|
+
b.iter(|| black_box(0))
|
|
318
|
+
});
|
|
275
319
|
g.bench_function("levenshtein_10x1000_seq", |b| b.iter(|| black_box(0)));
|
|
276
320
|
g.bench_function("levenshtein_10x1000_batched", |b| b.iter(|| black_box(0)));
|
|
277
321
|
}
|