An empirical case study and exploration into high-performance UTF-8 (8-bit) and UTF-16 (16-bit wchar_t) substring searching within the Windows kernel environment. The study examines how the cost of entering vector execution in Ring 0 changes the optimal substring-search strategy across buffer sizes, pattern characteristics, and execution environments. Combining Boyer-Moore-Horspool, SWAR, AVX-512, AVX2, and ARM64 NEON, this project examines the low-level trade-offs of kernel-mode vector optimization, cache locality, and general-purpose register (GPR) fallback algorithms. While not strictly benchmark-driven during its design, the repository includes comprehensive hardware telemetry to validate its architectural findings.
- Dynamic Ring 0 Routing: Bypasses vector registers for small buffers to eliminate the
KeSaveExtendedProcessorStateextended processor-state save/restore overhead. - Hybrid Boyer-Moore-Horspool (BMH): Blends sub-linear bad-character skipping with 4x unrolled SIMD memory sliding.
- L1 Cache–Conscious Golden Ratio Hashing: Folds wide UTF-16 skip tables from an unfeasible 256KB footprint down to 8KB to ensure residency in the L1 D-Cache.
- Hardware-Agnostic SWAR Fallbacks: Implements 64-bit SIMD-Within-A-Register bit-manipulation hacks to process 8 bytes (or 4 wide characters) per cycle without engaging FPU/YMM/ZMM state.
- Cross-Architecture Vector Pipelines: Native implementations for x86_64 (AVX-512BW, AVX2) and ARM64 (NEON).
- Experimental BNDM Evaluation: Features Backward Nondeterministic DAWG Matching implementations with an analysis of why bit-parallel automata underperform BMH in kernel workloads.
- The Kernel Optimization Landscape & Ring 0 Trade-offs
- Algorithm Architecture: Hybrid BMH
- Execution Engines: SIMD vs. SWAR
- The Experimental Branch: Why BNDM Falls Behind
- Benchmark Analysis
- Repository Structure
- API Reference & Usage
- Building & Testing
- License
In user-mode applications, maximizing substring search performance is often a matter of streaming vectors directly through AVX-512 or AVX2 pipelines. In Windows kernel mode (Ring 0), vector optimization introduces strict architectural trade-offs:
+-------------------------------------------------------------------------+
| Call: Find() |
+------------------------------------+------------------------------------+
|
Buffer > 20KB (AVX-512) or 6KB (AVX2)?
|
+------------------+------------------+
| NO | YES
v v
+---------------------------+ +-----------------------+
| GPR SWAR / | | KeSaveExtendedState |
| BMH Fast Path | +-----------+-----------+
| (Zero FPU State Overhead) | |
+---------------------------+ +-----------v-----------+
| AVX-512/AVX2/NEON |
| 4x Unrolled Slide |
+-----------+-----------+
|
+-----------v-----------+
| KeRestoreExtendedState|
+-----------------------+
The Windows kernel does not preserve floating-point and extended vector (XMM/YMM/ZMM) registers across thread context switches by default. To safely issue SIMD instructions in Ring 0 without corrupting user-mode thread state, the caller must allocate an XSTATE_SAVE structure and invoke KeSaveExtendedProcessorState.
This state preservation routine writes processor context to memory via XSAVE/XRSTOR, introducing fixed microsecond overheads that severely penalize short operations:
- AVX2 (
XSTATE_MASK_AVX): Requires backing up ~800 bytes of YMM register state. - AVX-512 (
XSTATE_MASK_AVX512_FULL): Requires backing up ~2.7 KB of ZMM and Opmask register state, drastically increasing the context switch penalty.
Smart Auto is the default runtime routing mode that selects between the GPR/SWAR/BMH path and the vectorized path according to the implementation's buffer-size threshold and processor capabilities:
- Small Buffers (< 6 KB): Bypasses vector registers completely and routes directly to the GPR-based SWAR fallback, incurring zero floating-point save overhead.
- Medium Buffers (6 KB - 20 KB): Wraps execution in safe YMM state-saving calls to unleash the AVX2 pipeline. AVX-512 is restricted here because its massive 2.7 KB state-save latency eclipses the throughput gains on buffers of this size.
- Large Buffers (> 20 KB): Fully amortizes the AVX-512 ZMM state-saving overhead, unleashing extreme 64-byte unrolled memory streaming.
Table precomputation in Initialize() relies on ExAllocatePool2 with POOL_FLAG_PAGED and POOL_FLAG_CACHE_ALIGNED. The initialization routines are annotated with PAGED_CODE() and must execute at PASSIVE_LEVEL or APC_LEVEL.
The primary production engines (CKmStrSearch8 and CKmStrSearch16) combine Boyer-Moore-Horspool bad-character shift rules with unrolled vector sliding.
- Flat 256-Entry Lookup Table: Precomputes a 1KB bad-character jump table for all ASCII/extended-ASCII byte values.
- Pattern Length Bifurcation: Long patterns benefit from sub-linear jumping because the skip distance overcomes the latency of memory table lookups. Short patterns bypass table lookups entirely to avoid pipeline-stalling memory dependencies, executing a 4x unrolled pure vector slide across the text buffer.
UTF-16 characters (wchar_t) span a 65,536 value space. A naive BMH table would require 256KB (65536 * 4 bytes), guaranteed to blow out the CPU's L1 Data Cache (typically 32KB–48KB per core), leading to continuous L2/L3 cache misses.
16-Bit Character (ch)
|
v
[ ch * 0x9E3779B9U ] ---> Multiplicative Hash (Golden Ratio 2^32 / phi)
|
(hash >> 21) ---> 11-Bit Extraction (0 .. 2047)
|
v
[ 8KB Table ] ---> 2,048-entry skip table designed to remain L1-cache resident
- Golden Ratio Multiplicative Hashing: Characters are hashed into an 11-bit index (2048 entries). By folding the sparse 256KB state space into a dense 8KB footprint, the algorithm inherently prevents the cache thrashing that degrades standard wide-character BMH implementations.
- Cache Residency & Allocation: The table occupies 8KB, keeping it well within a typical L1 D-cache footprint, allocated with
POOL_FLAG_CACHE_ALIGNEDto prevent Translation Lookaside Buffer (TLB) invalidation during the continuous memory scanning loop. - Collision Safety: On bucket collisions, the smallest jump distance is stored. While hash collisions slightly reduce skip aggressiveness, correctness is preserved.
The highest-throughput engine utilizes AVX-512 Byte/Word instructions. It evaluates 256 bytes per iteration using four unrolled 64-byte ZMM registers. Unlike AVX2, which requires expensive bit-packing (_mm256_movemask_epi8), AVX-512 generates hardware bitmasks (__mmask64 / __mmask32) natively from the comparison instruction.
AVX-512 Vectorization Pipeline (4x Unrolled)
+-----------------------------------------------------------------------------------+
| Lane 0: [ 64 Bytes ZMM0 ] === cmpeq_epi8_mask(vLast) ===> __mmask64 mask0 |
| Lane 1: [ 64 Bytes ZMM1 ] === cmpeq_epi8_mask(vLast) ===> __mmask64 mask1 |
| Lane 2: [ 64 Bytes ZMM2 ] === cmpeq_epi8_mask(vLast) ===> __mmask64 mask2 |
| Lane 3: [ 64 Bytes ZMM3 ] === cmpeq_epi8_mask(vLast) ===> __mmask64 mask3 |
| |
| Evaluate: if ((mask0 | mask1 | mask2 | mask3) != 0) -> _BitScanForward64() |
+-----------------------------------------------------------------------------------+
The AVX2 pipeline evaluates 128 bytes per iteration using four unrolled YMM registers (_mm256_cmpeq_epi8 / _mm256_cmpeq_epi16).
SIMD Vectorization Pipeline (4x Unrolled AVX2)
+-----------------------------------------------------------------------------+
| Lane 0: [ 32 Bytes YMM0 ] === cmpeq(vLast) ===> Mask0 |
| Lane 1: [ 32 Bytes YMM1 ] === cmpeq(vLast) ===> Mask1 |
| Lane 2: [ 32 Bytes YMM2 ] === cmpeq(vLast) ===> Mask2 ===> OR ===> testz |
| Lane 3: [ 32 Bytes YMM3 ] === cmpeq(vLast) ===> Mask3 |
+-----------------------------------------------------------------------------+
- AVX2 (x86_64): Candidate match offsets are extracted by folding lanes together via
_mm256_or_si256, rejecting non-matches with_mm256_testz_si256, and isolating the precise byte offset with_BitScanForward. - NEON (ARM64): Processes 64 bytes per iteration using four 128-bit vector registers (
vceqq_u8/vceqq_u16). Emulates x86 movemask functionality by applying a power-of-two bitshift array (vandq_u8) followed by an across-vector vector addition.
When operating beneath the 6KB vector threshold, the engine uses 64-bit general-purpose registers to evaluate memory without invoking FPU state.
Using Mycroft’s bit-twiddling zero-byte detection algorithm:
// 8-Bit SWAR Byte Matching:
ULONGLONG chunk = *reinterpret_cast<const ULONGLONG*>(ptr);
ULONGLONG v = chunk ^ c8; // Matching bytes become 0x00
if (((v - 0x0101010101010101ULL) & ~v & 0x8080808080808080ULL) != 0)
{
// Zero-byte match detected in 8-byte word
}Located in the Experimental/ directory, KmBndmSearch8 and KmBndmSearch16 explore an alternative algorithmic theory based on Backward Nondeterministic DAWG Matching (BNDM).
These implementations are not pure BNDM. They are heavily hybridized to survive the constraints of the kernel environment:
- SWAR Fast-Forwarding: To prevent CPU branch prediction stalls and severe O(N*M) degradation on repetitive inputs, the algorithm does not blindly execute the automaton. Instead, it uses the GPR-based SWAR technique to rapidly scan for the last character of the pattern before engaging the backward automaton state machine.
- State Capacity Bifurcation: The non-deterministic suffix automaton's state is packed entirely into a single 64-bit General Purpose Register (
ULONGLONG uD). Therefore, patterns exceeding 64 characters bypass the BNDM logic entirely, defaulting to a fast SWAR linear slide or explicit vector streaming. - Bitmask Bloom Merging (16-bit): To avoid an unfeasible 65,536-entry (512KB) array for 16-bit characters, the wide engine hashes characters into a 16KB (2,048-entry) table. Hash collisions are handled by logically ORing (
|=) the bitmasks together. This essentially acts as a Bloom filter, allowing false positive prefix matches that are later discarded by a full-string verification loop. - SIMD Bypass: BNDM is utilized strictly as the scalar fallback. For buffers large enough to justify the context switch overhead, BNDM is bypassed entirely in favor of a pure 4x unrolled AVX2/NEON memory slide.
Despite its favorable theoretical/algorithmic characteristics, the BNDM variant consistently lags behind the Hybrid BMH implementation in kernel evaluations. Hardware telemetry reveals four persistent architectural bottlenecks:
- Scalar Inner Loop Overhead: The Boyer-Moore-Horspool algorithm computes multi-byte jumps with a single bad-character table lookup, whereas BNDM must repeatedly step backward, compute hash lookups, and execute bitwise AND/shift operations per character (
uD &= Mask[Hash(ch)]; uD <<= 1;). - Loop-Carried Data Dependencies: BMH streams linear memory chunks that interact predictably with CPU branch predictors and prefetchers, while BNDM creates a strict sequential dependency on the single register state
uD, stalling instruction-level parallelism (ILP). - Wide-Character Hash Collision Penalties: In BMH, 16-bit hash collisions only reduce the optimal jump distance, but in wide-character BNDM, collisions force bitmasks to logically merge (
|=), generating false prefix matches that require expensive full-string verification loops. - Pre-SIMD Scalar Threshold Deficit: While both algorithms rely on identical 4x unrolled vector sliding for large buffers, BNDM lags in smaller buffers before the SIMD threshold is reached due to the heavy overhead of the automaton fallback.
Testing was performed using a multi-threaded kernel test harness (KmStrSearchShared.h). Threads synchronize on a fast mutex before initiating the search loops simultaneously, ensuring the CPU and memory bus are completely saturated to calculate accurate GB/s throughput. CRT strstr/wcsstr serves as the baseline reference.
Features full AVX-512BW support and 1.25 MB of private L2 cache per core.
Standard Execution (Single match near end):
| Buffer Size | CRT Baseline | Scalar Hybrid | AVX2 Explicit | AVX-512 Explicit | Smart Auto |
|---|---|---|---|---|---|
| Tiny (40 B) | 3.61 GB/s | 2.07 GB/s | 0.73 GB/s | 0.73 GB/s | 1.95 GB/s |
| Small (256 B) | 15.03 GB/s | 5.18 GB/s | 2.09 GB/s | 1.98 GB/s | 4.88 GB/s |
| Medium (10 KB) | 22.38 GB/s | 8.43 GB/s | 45.75 GB/s | 48.65 GB/s | 45.71 GB/s |
| Large (1 MB) | 22.66 GB/s | 8.50 GB/s | 65.46 GB/s | 80.92 GB/s | 85.93 GB/s |
Simulates real-world kernel log scanning to test skip table entropy and realistic jump distances.
| Buffer Size | CRT Baseline | Scalar Hybrid | AVX2 Explicit | AVX-512 Explicit | Smart Auto |
|---|---|---|---|---|---|
| Tiny (40 B) | 3.35 GB/s | 1.76 GB/s | 0.69 GB/s | 0.58 GB/s | 1.65 GB/s |
| Small (256 B) | 13.35 GB/s | 5.14 GB/s | 1.98 GB/s | 1.98 GB/s | 4.99 GB/s |
| Medium (10 KB) | 22.44 GB/s | 12.29 GB/s | 45.30 GB/s | 49.89 GB/s | 46.58 GB/s |
| Large (1 MB) | 22.71 GB/s | 12.54 GB/s | 87.82 GB/s | 103.16 GB/s | 105.61 GB/s |
Mismatched Dense Text (Worst-case repetitive partial matches):
| Buffer Size | CRT Baseline | Scalar Hybrid | AVX2 Explicit | AVX-512 Explicit | Smart Auto |
|---|---|---|---|---|---|
| Tiny (40 B) | 0.56 GB/s | 2.08 GB/s | 0.73 GB/s | 0.73 GB/s | 1.93 GB/s |
| Small (256 B) | 0.24 GB/s | 5.82 GB/s | 2.07 GB/s | 2.01 GB/s | 5.56 GB/s |
| Medium (10 KB) | 0.21 GB/s | 11.95 GB/s | 45.28 GB/s | 50.26 GB/s | 46.32 GB/s |
| Large (1 MB) | 0.21 GB/s | 12.48 GB/s | 61.01 GB/s | 79.22 GB/s | 81.67 GB/s |
Standard Execution (Single match near end):
| Buffer Size | CRT Baseline | Scalar Hybrid | AVX2 Explicit | AVX-512 Explicit | Smart Auto |
|---|---|---|---|---|---|
| Tiny (80 B) | 6.72 GB/s | 4.19 GB/s | 1.45 GB/s | 1.44 GB/s | 3.83 GB/s |
| Small (512 B) | 14.35 GB/s | 8.62 GB/s | 4.24 GB/s | 3.86 GB/s | 8.13 GB/s |
| Medium (20 KB) | 26.67 GB/s | 12.21 GB/s | 64.19 GB/s | 75.83 GB/s | 68.43 GB/s |
| Large (2 MB) | 22.31 GB/s | 12.17 GB/s | 45.71 GB/s | 50.15 GB/s | 52.01 GB/s |
Simulates real-world kernel log scanning to test skip table entropy and realistic jump distances.
| Buffer Size | CRT Baseline | Scalar Hybrid | AVX2 Explicit | AVX-512 Explicit | Smart Auto |
|---|---|---|---|---|---|
| Tiny (80 B) | 6.28 GB/s | 3.58 GB/s | 1.39 GB/s | 1.39 GB/s | 3.30 GB/s |
| Small (512 B) | 16.05 GB/s | 9.29 GB/s | 4.02 GB/s | 3.83 GB/s | 8.71 GB/s |
| Medium (20 KB) | 23.79 GB/s | 17.85 GB/s | 64.12 GB/s | 73.71 GB/s | 71.41 GB/s |
| Large (2 MB) | 22.36 GB/s | 15.39 GB/s | 50.16 GB/s | 51.95 GB/s | 52.61 GB/s |
Mismatched Dense Text (Worst-case repetitive partial matches):
| Buffer Size | CRT Baseline | Scalar Hybrid | AVX2 Explicit | AVX-512 Explicit | Smart Auto |
|---|---|---|---|---|---|
| Tiny (80 B) | 0.81 GB/s | 3.86 GB/s | 1.43 GB/s | 1.42 GB/s | 3.63 GB/s |
| Small (512 B) | 0.35 GB/s | 7.39 GB/s | 4.24 GB/s | 3.88 GB/s | 7.10 GB/s |
| Medium (20 KB) | 0.31 GB/s | 11.53 GB/s | 62.77 GB/s | 73.76 GB/s | 66.30 GB/s |
| Large (2 MB) | 0.31 GB/s | 12.54 GB/s | 45.93 GB/s | 49.52 GB/s | 50.67 GB/s |
AVX2 supported, AVX-512 not supported.
Standard Execution (Single match near end):
| Buffer Size | CRT Baseline | Scalar Hybrid | AVX2 Explicit | Smart Auto |
|---|---|---|---|---|
| Tiny (40 B) | 2.05 GB/s | 1.98 GB/s | 0.23 GB/s | 1.85 GB/s |
| Small (256 B) | 1.56 GB/s | 5.10 GB/s | 0.55 GB/s | 4.90 GB/s |
| Medium (10 KB) | 1.48 GB/s | 8.72 GB/s | 31.17 GB/s | 18.61 GB/s |
| Large (1 MB) | 1.35 GB/s | 8.57 GB/s | 50.81 GB/s | 45.89 GB/s |
Mismatched Dense Text (Worst-case repetitive partial matches):
| Buffer Size | CRT Baseline | Scalar Hybrid | AVX2 Explicit | Smart Auto |
|---|---|---|---|---|
| Tiny (40 B) | 0.53 GB/s | 2.11 GB/s | 0.41 GB/s | 1.98 GB/s |
| Small (256 B) | 0.24 GB/s | 5.91 GB/s | 1.08 GB/s | 5.75 GB/s |
| Medium (10 KB) | 0.22 GB/s | 12.47 GB/s | 33.57 GB/s | 25.57 GB/s |
| Large (1 MB) | 0.21 GB/s | 12.27 GB/s | 52.63 GB/s | 49.60 GB/s |
Simulates real-world kernel log scanning to test skip table entropy and realistic jump distances.
| Buffer Size | CRT Baseline | Scalar Hybrid | AVX2 Explicit | Smart Auto |
|---|---|---|---|---|
| Tiny (40 B) | 2.12 GB/s | 1.75 GB/s | 0.29 GB/s | 1.66 GB/s |
| Small (256 B) | 1.59 GB/s | 5.18 GB/s | 0.63 GB/s | 4.94 GB/s |
| Medium (10 KB) | 1.47 GB/s | 12.60 GB/s | 33.03 GB/s | 18.69 GB/s |
| Large (1 MB) | 1.36 GB/s | 12.08 GB/s | 56.03 GB/s | 57.06 GB/s |
Standard Execution (Single match near end):
| Buffer Size | CRT Baseline | Scalar Hybrid | AVX2 Explicit | Smart Auto |
|---|---|---|---|---|
| Tiny (80 B) | 3.47 GB/s | 4.06 GB/s | 0.33 GB/s | 3.87 GB/s |
| Small (512 B) | 1.67 GB/s | 8.48 GB/s | 2.47 GB/s | 8.31 GB/s |
| Medium (20 KB) | 1.48 GB/s | 12.15 GB/s | 48.30 GB/s | 47.85 GB/s |
| Large (2 MB) | 1.49 GB/s | 11.82 GB/s | 52.00 GB/s | 48.19 GB/s |
Simulates real-world kernel log scanning to test skip table entropy and realistic jump distances.
| Buffer Size | CRT Baseline | Scalar Hybrid | AVX2 Explicit | Smart Auto |
|---|---|---|---|---|
| Tiny (80 B) | 3.98 GB/s | 3.59 GB/s | 0.34 GB/s | 3.40 GB/s |
| Small (512 B) | 1.69 GB/s | 9.19 GB/s | 2.85 GB/s | 8.72 GB/s |
| Medium (20 KB) | 1.49 GB/s | 17.62 GB/s | 50.91 GB/s | 49.75 GB/s |
| Large (2 MB) | 1.48 GB/s | 14.65 GB/s | 55.29 GB/s | 57.07 GB/s |
Mismatched Dense Text (Worst-case repetitive partial matches):
| Buffer Size | CRT Baseline | Scalar Hybrid | AVX2 Explicit | Smart Auto |
|---|---|---|---|---|
| Tiny (80 B) | 0.93 GB/s | 4.01 GB/s | 0.71 GB/s | 3.70 GB/s |
| Small (512 B) | 0.41 GB/s | 7.60 GB/s | 1.49 GB/s | 7.65 GB/s |
| Medium (20 KB) | 0.37 GB/s | 12.54 GB/s | 48.64 GB/s | 31.63 GB/s |
| Large (2 MB) | 0.37 GB/s | 12.33 GB/s | 49.70 GB/s | 50.80 GB/s |
- L2 Cache Saturation vs. Memory Bus Boundaries: On the i7-1165G7, 8-bit AVX-512 streaming on 1 MB realistic log corpuses peaked at 105.61 GB/s. Since the 1 MB buffer fits entirely within the Tiger Lake 1.25 MB L2 cache, 64-byte ZMM streaming saturated the internal cache bus perfectly. When scanning 2 MB buffers, throughput dropped to ~50–52 GB/s across all engines, hitting the physical dual-channel DDR4-3200 saturation barrier.
-
Severe Pathological Degradation in CRT: Under mismatched dense text (
aaaaaaaaabinsideaaaaaaaa...), CRT collapses to 0.21–0.37 GB/s across both bare-metal and virtualized platforms due to$O(N \cdot M)$ mismatch backtracking. The hybrid last-character heuristic bypasses this, delivering 49–81 GB/s (up to 385x faster). - Hypervisor State-Saving Latency: On the i7-8086K VMware guest, entering vector execution in tiny buffers is penalized even more severely than on bare metal (AVX2 drops to 0.23–0.33 GB/s vs. 3.8–4.0 GB/s for SWAR). The Smart Auto bypass protects against this virtualized context penalty.
-
AVX-512 Instruction Efficiency: On cache-bound medium buffers, AVX-512 delivers 15–18% higher throughput than AVX2 by emitting direct
__mmask32bitmasks via_mm512_cmpeq_epi16_mask, avoiding the movemask/bit-packing overhead of AVX2.
├── Experimental/
│ ├── KmBndmSearch8.h # 8-bit BNDM bit-parallel class declaration
│ ├── KmBndmSearch8.cpp # 8-bit BNDM implementation
│ ├── KmBndmSearch16.h # 16-bit BNDM bitmask bloom merger declaration
│ └── KmBndmSearch16.cpp # 16-bit BNDM implementation
├── Shared/
│ └── KmStrSearchShared.h # Unified Kernel/User multi-threaded test harness
├── StrSearch/
│ ├── KmStrSearch8.h # 8-bit substring search class declaration
│ ├── KmStrSearch8.cpp # AVX-512, AVX2, NEON, SWAR, and BMH implementation (8-bit)
│ ├── KmStrSearch16.h # UTF-16 Hybrid BMH class declaration
│ └── KmStrSearch16.cpp # Golden Ratio Hash, AVX-512, AVX2, NEON, SWAR (16-bit)
├── TestKm/
│ └── KmStrSearchDrv.cpp # Kernel driver for test execution
└── TestUm/
└── KmStrSearchUm.cpp # User-mode test suite wrapper
The classes CKmStrSearch8 (for char / UTF-8) and CKmStrSearch16 (for wchar_t / UTF-16) provide matching APIs.
#include <ntddk.h>
#include "KmStrSearch16.h"
VOID SearchExample(const wchar_t* pKernelLogBuffer, size_t cchLogLength)
{
PAGED_CODE(); // Required: Class allocates from Paged Pool
CKmStrSearch16 searchEngine;
const wchar_t needle[] = L"BUGCHECK_CODE_CRITICAL";
// 1. Initialize table (PASSIVE_LEVEL or APC_LEVEL)
if (!searchEngine.Initialize(needle, wcslen(needle)))
{
return; // Allocation failure or invalid length
}
// 2. Execute Search using 'Auto' routing
int matchIndex = searchEngine.Find(pKernelLogBuffer, cchLogLength);
if (matchIndex != -1)
{
// Pattern matched at pKernelLogBuffer[matchIndex]
}
}Callers can override the auto-bypass heuristic by explicitly specifying an execution engine:
// Force scalar GPR execution (guarantees zero XSTATE context saving)
int idxScalar = searchEngine.Find(pBuffer, cchLen, CKmStrSearch16::SearchEngine::Scalar);
// Explicitly execute AVX2 or AVX-512 (automatically wraps KeSaveExtendedProcessorState)
int idxAVX2 = searchEngine.Find(pBuffer, cchLen, CKmStrSearch16::SearchEngine::AVX2);
int idxAVX512 = searchEngine.Find(pBuffer, cchLen, CKmStrSearch16::SearchEngine::AVX512);- Visual Studio 2026
- C++20 Toolset Support: Ensure
/std:c++20is enabled in your compilation flags. - Windows Driver Kit (WDK): The latest MS WDK is required to compile the kernel driver targets.
Native MSVC 2026 solution and project files are provided in the repository to build both the test kernel driver and the user mode test suite code.
- TestKm (Kernel Driver): Compiles the
KmStrSearchDrv.systest kernel driver containing the complete performance and correctness test suite. This represents the primary evaluation environment. - TestUm (User Mode): Compiles the user mode test suite code wrapping the shared test logic, designed exclusively for rapid functional validation, unit testing, and debugging.
- Live Tracing: The kernel driver outputs benchmark results and correctness validations in real-time using
DbgPrintExtraces. These logs are broadcast under theDPFLTR_IHVDRIVER_IDcomponent filter usingDPFLTR_INFO_LEVELandDPFLTR_ERROR_LEVELlevels, making them easily viewable via WinDbg or Sysinternals DebugView. - Persistent Logging: At the conclusion of the test suite, the driver flushes all accumulated output strings and saves the complete telemetry report to a text file located at
\SystemRoot\Temp\KmStrSearchPerf.txt.
TestUm) test suite code is provided for convenience and logic verification, its performance metrics cannot be trusted to reflect true system capabilities. The search engines, specifically the dynamic SIMD/SWAR routing logic and state-saving bypasses, are designed exclusively for the Windows kernel architecture. User-mode environments handle thread context switching and vector register preservation entirely differently. Always refer to the KmStrSearchDrv.sys kernel driver telemetry for accurate Ring 0 performance evaluations.
This project is licensed under the MIT License. See the LICENSE file for details.