| // Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| // | |
| // Benchmarks for int.trailingZeroBitCount and int.oneBitCount, exercised | |
| // through a small bit-array implementation. Each operation has an | |
| // `Accelerated` variant (HW intrinsic for counting operations, Int32x4 SIMD | |
| // for word-level bitwise operations) and a `Swar` baseline that does the | |
| // same work without HW acceleration, so the speedup attributable to the | |
| // hardware path is visible directly. | |
| // | |
| // `main()` first runs a correctness check that asserts both | |
| // implementations agree across a range of sizes and densities, then | |
| // reports benchmark timings via the standard BenchmarkBase harness. | |
| import 'dart:math'; | |
| import 'dart:typed_data'; | |
| import 'package:benchmark_harness/benchmark_harness.dart'; | |
| // 32-bit storage words so the benchmark runs on both native (64-bit int) | |
| // and web (32-bit bitwise) without platform-specific word arithmetic. | |
| const int _wordBits = 32; | |
| const int _wordMask = _wordBits - 1; | |
| const int _wordShift = 5; | |
| class BitArray { | |
| final Uint32List _words; | |
| final int length; | |
| BitArray(this.length) | |
| : _words = Uint32List((length + _wordBits - 1) >> _wordShift); | |
| Uint32List get words => _words; | |
| void setBit(int i) { | |
| _words[i >> _wordShift] |= 1 << (i & _wordMask); | |
| } | |
| // ----- cardinality (popcount across all words) --------------------------- | |
| // Accelerated: hardware popcount via int.oneBitCount. | |
| int cardinalityAccelerated() { | |
| var total = 0; | |
| for (var i = 0; i < _words.length; i++) { | |
| total += _words[i].oneBitCount; | |
| } | |
| return total; | |
| } | |
| int cardinalitySwar() { | |
| var total = 0; | |
| for (var i = 0; i < _words.length; i++) { | |
| total += _popcountWord(_words[i]); | |
| } | |
| return total; | |
| } | |
| // ----- forEachSetBit (iterate set bits via ctz + clear-lowest) ----------- | |
| // Accelerated: int.trailingZeroBitCount per surviving bit. | |
| void forEachSetBitAccelerated(void Function(int) action) { | |
| for (var wordIdx = 0; wordIdx < _words.length; wordIdx++) { | |
| var w = _words[wordIdx]; | |
| final base = wordIdx << _wordShift; | |
| while (w != 0) { | |
| final bit = w.trailingZeroBitCount; | |
| final pos = base + bit; | |
| if (pos >= length) return; | |
| action(pos); | |
| // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan | |
| w &= w - 1; | |
| } | |
| } | |
| } | |
| void forEachSetBitSwar(void Function(int) action) { | |
| for (var wordIdx = 0; wordIdx < _words.length; wordIdx++) { | |
| var w = _words[wordIdx]; | |
| final base = wordIdx << _wordShift; | |
| while (w != 0) { | |
| final bit = _ctzWord(w); | |
| final pos = base + bit; | |
| if (pos >= length) return; | |
| action(pos); | |
| // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan | |
| w &= w - 1; | |
| } | |
| } | |
| } | |
| // ----- select(k) (position of the k-th set bit, k >= 0) ----------------- | |
| // Accelerated: int.oneBitCount to skip whole words, then walk the | |
| // target word with int.trailingZeroBitCount. | |
| int selectAccelerated(int k) { | |
| var remaining = k; | |
| for (var wordIdx = 0; wordIdx < _words.length; wordIdx++) { | |
| final w = _words[wordIdx]; | |
| final pop = w.oneBitCount; | |
| if (remaining < pop) { | |
| var v = w; | |
| while (remaining > 0) { | |
| // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan | |
| v &= v - 1; | |
| remaining--; | |
| } | |
| return (wordIdx << _wordShift) + v.trailingZeroBitCount; | |
| } | |
| remaining -= pop; | |
| } | |
| return -1; | |
| } | |
| // Fair non-accelerated baseline: SWAR popcount to skip whole words, | |
| // SWAR ctz inside the target word. | |
| int selectSwar(int k) { | |
| var remaining = k; | |
| for (var wordIdx = 0; wordIdx < _words.length; wordIdx++) { | |
| final w = _words[wordIdx]; | |
| final pop = _popcountWord(w); | |
| if (remaining < pop) { | |
| var v = w; | |
| while (remaining > 0) { | |
| // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetKernighan | |
| v &= v - 1; | |
| remaining--; | |
| } | |
| return (wordIdx << _wordShift) + _ctzWord(v); | |
| } | |
| remaining -= pop; | |
| } | |
| return -1; | |
| } | |
| // ----- totalBitLength (sum of int.bitLength across every word) ---------- | |
| // | |
| // A synthetic operation that exercises int.bitLength once per word, the | |
| // way cardinality exercises int.oneBitCount once per word. | |
| // Accelerated: int.bitLength (asm-intrinsic CLZ today). | |
| int totalBitLengthAccelerated() { | |
| var total = 0; | |
| for (var i = 0; i < _words.length; i++) { | |
| total += _words[i].bitLength; | |
| } | |
| return total; | |
| } | |
| // Software bit-trick equivalent of bitLength on a 32-bit word. | |
| int totalBitLengthSwar() { | |
| var total = 0; | |
| for (var i = 0; i < _words.length; i++) { | |
| final v = _words[i]; | |
| if (v != 0) total += _highBitInWord(v) + 1; | |
| } | |
| return total; | |
| } | |
| // ----- complementCardinality (popcount of bitwise NOT each word) ------- | |
| // | |
| // Exercises int.~ once per word, immediately followed by oneBitCount. | |
| // Accelerated: int.oneBitCount on each complemented word. | |
| int complementCardinalityAccelerated() { | |
| var total = 0; | |
| for (var i = 0; i < _words.length; i++) { | |
| // Mask back to 32 bits because ~uint32 sign-extends in Dart's int. | |
| total += (~_words[i] & 0xFFFFFFFF).oneBitCount; | |
| } | |
| return total; | |
| } | |
| // Fair non-accelerated baseline: SWAR popcount on each complemented word. | |
| int complementCardinalitySwar() { | |
| var total = 0; | |
| for (var i = 0; i < _words.length; i++) { | |
| total += _popcountWord(~_words[i] & 0xFFFFFFFF); | |
| } | |
| return total; | |
| } | |
| // ----- intersection (out = a AND b, materialized as a new BitArray) ----- | |
| // Word-level scalar AND. | |
| static void intersectionSwar(BitArray a, BitArray b, BitArray out) { | |
| final wa = a._words; | |
| final wb = b._words; | |
| final wo = out._words; | |
| if (wa.length != wb.length || wa.length != wo.length) { | |
| throw ArgumentError('a, b, and out must have the same length'); | |
| } | |
| final n = wa.length; | |
| for (var i = 0; i < n; i++) { | |
| wo[i] = wa[i] & wb[i]; | |
| } | |
| } | |
| // Accelerated: 4 words per Int32x4 vector op. Tail uses scalar. | |
| static void intersectionAccelerated(BitArray a, BitArray b, BitArray out) { | |
| final wa = a._words; | |
| final wb = b._words; | |
| final wo = out._words; | |
| if (wa.length != wb.length || wa.length != wo.length) { | |
| throw ArgumentError('a, b, and out must have the same length'); | |
| } | |
| final n = wa.length; | |
| final simdWords = n >> 2; | |
| final va = Int32x4List.view(wa.buffer, wa.offsetInBytes, simdWords); | |
| final vb = Int32x4List.view(wb.buffer, wb.offsetInBytes, simdWords); | |
| final vo = Int32x4List.view(wo.buffer, wo.offsetInBytes, simdWords); | |
| for (var i = 0; i < simdWords; i++) { | |
| vo[i] = va[i] & vb[i]; | |
| } | |
| for (var i = simdWords << 2; i < n; i++) { | |
| wo[i] = wa[i] & wb[i]; | |
| } | |
| } | |
| // ----- union (out = a OR b) ------------------------------------------- | |
| static void unionSwar(BitArray a, BitArray b, BitArray out) { | |
| final wa = a._words; | |
| final wb = b._words; | |
| final wo = out._words; | |
| if (wa.length != wb.length || wa.length != wo.length) { | |
| throw ArgumentError('a, b, and out must have the same length'); | |
| } | |
| final n = wa.length; | |
| for (var i = 0; i < n; i++) { | |
| wo[i] = wa[i] | wb[i]; | |
| } | |
| } | |
| static void unionAccelerated(BitArray a, BitArray b, BitArray out) { | |
| final wa = a._words; | |
| final wb = b._words; | |
| final wo = out._words; | |
| if (wa.length != wb.length || wa.length != wo.length) { | |
| throw ArgumentError('a, b, and out must have the same length'); | |
| } | |
| final n = wa.length; | |
| final simdWords = n >> 2; | |
| final va = Int32x4List.view(wa.buffer, wa.offsetInBytes, simdWords); | |
| final vb = Int32x4List.view(wb.buffer, wb.offsetInBytes, simdWords); | |
| final vo = Int32x4List.view(wo.buffer, wo.offsetInBytes, simdWords); | |
| for (var i = 0; i < simdWords; i++) { | |
| vo[i] = va[i] | vb[i]; | |
| } | |
| for (var i = simdWords << 2; i < n; i++) { | |
| wo[i] = wa[i] | wb[i]; | |
| } | |
| } | |
| // ----- xor (out = a XOR b, symmetric difference) ----------------------- | |
| static void xorSwar(BitArray a, BitArray b, BitArray out) { | |
| final wa = a._words; | |
| final wb = b._words; | |
| final wo = out._words; | |
| if (wa.length != wb.length || wa.length != wo.length) { | |
| throw ArgumentError('a, b, and out must have the same length'); | |
| } | |
| final n = wa.length; | |
| for (var i = 0; i < n; i++) { | |
| wo[i] = wa[i] ^ wb[i]; | |
| } | |
| } | |
| static void xorAccelerated(BitArray a, BitArray b, BitArray out) { | |
| final wa = a._words; | |
| final wb = b._words; | |
| final wo = out._words; | |
| if (wa.length != wb.length || wa.length != wo.length) { | |
| throw ArgumentError('a, b, and out must have the same length'); | |
| } | |
| final n = wa.length; | |
| final simdWords = n >> 2; | |
| final va = Int32x4List.view(wa.buffer, wa.offsetInBytes, simdWords); | |
| final vb = Int32x4List.view(wb.buffer, wb.offsetInBytes, simdWords); | |
| final vo = Int32x4List.view(wo.buffer, wo.offsetInBytes, simdWords); | |
| for (var i = 0; i < simdWords; i++) { | |
| vo[i] = va[i] ^ vb[i]; | |
| } | |
| for (var i = simdWords << 2; i < n; i++) { | |
| wo[i] = wa[i] ^ wb[i]; | |
| } | |
| } | |
| // ----- difference (out = a AND NOT b) ---------------------------------- | |
| // | |
| // Set difference: bits set in `a` and not in `b`. Implemented as | |
| // a & ~b at the word / SIMD level. | |
| static void differenceSwar(BitArray a, BitArray b, BitArray out) { | |
| final wa = a._words; | |
| final wb = b._words; | |
| final wo = out._words; | |
| if (wa.length != wb.length || wa.length != wo.length) { | |
| throw ArgumentError('a, b, and out must have the same length'); | |
| } | |
| final n = wa.length; | |
| for (var i = 0; i < n; i++) { | |
| wo[i] = wa[i] & (~wb[i] & 0xFFFFFFFF); | |
| } | |
| } | |
| static void differenceAccelerated(BitArray a, BitArray b, BitArray out) { | |
| final wa = a._words; | |
| final wb = b._words; | |
| final wo = out._words; | |
| if (wa.length != wb.length || wa.length != wo.length) { | |
| throw ArgumentError('a, b, and out must have the same length'); | |
| } | |
| final n = wa.length; | |
| final simdWords = n >> 2; | |
| final va = Int32x4List.view(wa.buffer, wa.offsetInBytes, simdWords); | |
| final vb = Int32x4List.view(wb.buffer, wb.offsetInBytes, simdWords); | |
| final vo = Int32x4List.view(wo.buffer, wo.offsetInBytes, simdWords); | |
| // Int32x4 has no andNot; build via a & ~b (bitwise negate is a single | |
| // NEON EOR-with-all-ones on ARM64). | |
| final ones = Int32x4(-1, -1, -1, -1); | |
| for (var i = 0; i < simdWords; i++) { | |
| vo[i] = va[i] & (vb[i] ^ ones); | |
| } | |
| for (var i = simdWords << 2; i < n; i++) { | |
| wo[i] = wa[i] & (~wb[i] & 0xFFFFFFFF); | |
| } | |
| } | |
| // ----- complement (out = NOT a) ---------------------------------------- | |
| static void complementSwar(BitArray a, BitArray out) { | |
| final wa = a._words; | |
| final wo = out._words; | |
| if (wa.length != wo.length) { | |
| throw ArgumentError('a and out must have the same length'); | |
| } | |
| final n = wa.length; | |
| for (var i = 0; i < n; i++) { | |
| wo[i] = ~wa[i] & 0xFFFFFFFF; | |
| } | |
| } | |
| static void complementAccelerated(BitArray a, BitArray out) { | |
| final wa = a._words; | |
| final wo = out._words; | |
| if (wa.length != wo.length) { | |
| throw ArgumentError('a and out must have the same length'); | |
| } | |
| final n = wa.length; | |
| final simdWords = n >> 2; | |
| final va = Int32x4List.view(wa.buffer, wa.offsetInBytes, simdWords); | |
| final vo = Int32x4List.view(wo.buffer, wo.offsetInBytes, simdWords); | |
| // No SIMD NOT; emit XOR-with-all-ones, which the VM lowers to a single | |
| // NEON EOR on ARM64. | |
| final ones = Int32x4(-1, -1, -1, -1); | |
| for (var i = 0; i < simdWords; i++) { | |
| vo[i] = va[i] ^ ones; | |
| } | |
| for (var i = simdWords << 2; i < n; i++) { | |
| wo[i] = ~wa[i] & 0xFFFFFFFF; | |
| } | |
| } | |
| bool wordsEqual(BitArray other) { | |
| if (_words.length != other._words.length) return false; | |
| for (var i = 0; i < _words.length; i++) { | |
| if (_words[i] != other._words[i]) return false; | |
| } | |
| return true; | |
| } | |
| // ----- helpers ---------------------------------------------------------- | |
| // SWAR popcount on a 32-bit word; non-accelerated equivalent of | |
| // int.oneBitCount. | |
| static int _popcountWord(int v) { | |
| v = v - ((v >> 1) & 0x55555555); | |
| v = (v & 0x33333333) + ((v >> 2) & 0x33333333); | |
| v = (v + (v >> 4)) & 0x0F0F0F0F; | |
| return ((v * 0x01010101) >> 24) & 0xFF; | |
| } | |
| // SWAR count-trailing-zeros on a 32-bit word; non-accelerated equivalent | |
| // of int.trailingZeroBitCount. Returns 32 for zero. | |
| static int _ctzWord(int v) { | |
| if (v == 0) return _wordBits; | |
| var n = 0; | |
| if ((v & 0xFFFF) == 0) { | |
| n += 16; | |
| v >>= 16; | |
| } | |
| if ((v & 0xFF) == 0) { | |
| n += 8; | |
| v >>= 8; | |
| } | |
| if ((v & 0xF) == 0) { | |
| n += 4; | |
| v >>= 4; | |
| } | |
| if ((v & 0x3) == 0) { | |
| n += 2; | |
| v >>= 2; | |
| } | |
| if ((v & 0x1) == 0) { | |
| n += 1; | |
| } | |
| return n; | |
| } | |
| // SWAR log2 on a 32-bit word; non-accelerated equivalent of | |
| // int.bitLength minus one (for nonzero v). | |
| static int _highBitInWord(int v) { | |
| var r = 0; | |
| if (v >= 0x10000) { | |
| v >>= 16; | |
| r += 16; | |
| } | |
| if (v >= 0x100) { | |
| v >>= 8; | |
| r += 8; | |
| } | |
| if (v >= 0x10) { | |
| v >>= 4; | |
| r += 4; | |
| } | |
| if (v >= 0x4) { | |
| v >>= 2; | |
| r += 2; | |
| } | |
| if (v >= 0x2) { | |
| r += 1; | |
| } | |
| return r; | |
| } | |
| } | |
| // ----- Correctness check ---------------------------------------------------- | |
| BitArray _randomArray(int size, Random rng, int densityPercent) { | |
| final bits = BitArray(size); | |
| for (var i = 0; i < size; i++) { | |
| if (rng.nextInt(100) < densityPercent) bits.setBit(i); | |
| } | |
| return bits; | |
| } | |
| void _assertEq(Object? actual, Object? expected, String label) { | |
| if (actual != expected) { | |
| throw StateError('FAIL $label: expected $expected, got $actual'); | |
| } | |
| } | |
| void checkCorrectness() { | |
| final rng = Random(0xDA27); | |
| for (final size in const [0, 1, 31, 32, 33, 63, 64, 65, 127, 1000, 50000]) { | |
| for (final density in const [0, 3, 50, 97, 100]) { | |
| final bits = _randomArray(size, rng, density); | |
| // Cardinality operations must agree. SWAR is the oracle. | |
| final card = bits.cardinalitySwar(); | |
| _assertEq( | |
| bits.cardinalityAccelerated(), | |
| card, | |
| 'cardinality.accelerated(size=$size, density=$density)', | |
| ); | |
| // forEachSetBit: accelerated and swar must enumerate the same | |
| // bits in the same order. | |
| final acc = <int>[]; | |
| final swar = <int>[]; | |
| bits.forEachSetBitAccelerated(acc.add); | |
| bits.forEachSetBitSwar(swar.add); | |
| _assertEq( | |
| acc.length, | |
| swar.length, | |
| 'forEach count (size=$size, density=$density)', | |
| ); | |
| for (var i = 0; i < acc.length; i++) { | |
| _assertEq( | |
| acc[i], | |
| swar[i], | |
| 'forEach[$i] (size=$size, density=$density)', | |
| ); | |
| } | |
| // select(k): accelerated and swar must agree. | |
| for (final k in [0, 1, card >> 1, card - 1]) { | |
| if (k < 0 || k >= card) continue; | |
| _assertEq( | |
| bits.selectAccelerated(k), | |
| bits.selectSwar(k), | |
| 'select(size=$size, density=$density, k=$k)', | |
| ); | |
| } | |
| _assertEq( | |
| bits.complementCardinalityAccelerated(), | |
| bits.complementCardinalitySwar(), | |
| 'complementCardinality(size=$size, density=$density)', | |
| ); | |
| _assertEq( | |
| bits.totalBitLengthAccelerated(), | |
| bits.totalBitLengthSwar(), | |
| 'totalBitLength(size=$size, density=$density)', | |
| ); | |
| // Bitwise pair ops: scalar and SIMD must produce identical results. | |
| final other = _randomArray(size, rng, density); | |
| final label = 'size=$size, density=$density'; | |
| final iSwar = BitArray(size); | |
| final iAcc = BitArray(size); | |
| BitArray.intersectionSwar(bits, other, iSwar); | |
| BitArray.intersectionAccelerated(bits, other, iAcc); | |
| _assertEq(iAcc.wordsEqual(iSwar), true, 'intersection($label)'); | |
| final uSwar = BitArray(size); | |
| final uAcc = BitArray(size); | |
| BitArray.unionSwar(bits, other, uSwar); | |
| BitArray.unionAccelerated(bits, other, uAcc); | |
| _assertEq(uAcc.wordsEqual(uSwar), true, 'union($label)'); | |
| final xSwar = BitArray(size); | |
| final xAcc = BitArray(size); | |
| BitArray.xorSwar(bits, other, xSwar); | |
| BitArray.xorAccelerated(bits, other, xAcc); | |
| _assertEq(xAcc.wordsEqual(xSwar), true, 'xor($label)'); | |
| final dSwar = BitArray(size); | |
| final dAcc = BitArray(size); | |
| BitArray.differenceSwar(bits, other, dSwar); | |
| BitArray.differenceAccelerated(bits, other, dAcc); | |
| _assertEq(dAcc.wordsEqual(dSwar), true, 'difference($label)'); | |
| // Complement: scalar and SIMD must produce the same bit-flipped array. | |
| final cSwar = BitArray(size); | |
| final cAcc = BitArray(size); | |
| BitArray.complementSwar(bits, cSwar); | |
| BitArray.complementAccelerated(bits, cAcc); | |
| _assertEq(cAcc.wordsEqual(cSwar), true, 'complement($label)'); | |
| } | |
| } | |
| } | |
| // ----- Benchmarks ----------------------------------------------------------- | |
| const int _benchSize = 1 << 20; // 1,048,576 bits = 32,768 words. | |
| class _BitArrayBenchmark extends BenchmarkBase { | |
| final int densityPercent; | |
| final void Function(BitArray) operation; | |
| late BitArray bits; | |
| _BitArrayBenchmark(String name, this.densityPercent, this.operation) | |
| : super('BitArray.$name'); | |
| @override | |
| void setup() { | |
| bits = _randomArray(_benchSize, Random(0xBEEF), densityPercent); | |
| } | |
| @override | |
| void run() { | |
| operation(bits); | |
| } | |
| } | |
| class _BitArrayPairBenchmark extends BenchmarkBase { | |
| final int densityPercent; | |
| final void Function(BitArray, BitArray, BitArray) operation; | |
| late BitArray a; | |
| late BitArray b; | |
| late BitArray out; | |
| _BitArrayPairBenchmark(String name, this.densityPercent, this.operation) | |
| : super('BitArray.$name'); | |
| @override | |
| void setup() { | |
| a = _randomArray(_benchSize, Random(0xBEEF), densityPercent); | |
| b = _randomArray(_benchSize, Random(0xC0DE), densityPercent); | |
| out = BitArray(_benchSize); | |
| } | |
| @override | |
| void run() { | |
| operation(a, b, out); | |
| } | |
| } | |
| List<BenchmarkBase> _benchmarks() { | |
| // Sinks the optimizer cannot fold away. | |
| var sink = 0; | |
| void accumulate(int x) { | |
| sink ^= x; | |
| } | |
| return [ | |
| // Cardinality: SWAR popcount vs int.oneBitCount. | |
| _BitArrayBenchmark( | |
| 'cardinality.swar', | |
| 25, | |
| (bits) => sink ^= bits.cardinalitySwar(), | |
| ), | |
| _BitArrayBenchmark( | |
| 'cardinality.accelerated', | |
| 25, | |
| (bits) => sink ^= bits.cardinalityAccelerated(), | |
| ), | |
| // forEachSetBit: Brian-Kernighan loop with SWAR ctz vs int.trailingZeroBitCount. | |
| _BitArrayBenchmark( | |
| 'forEachSetBit.swar', | |
| 25, | |
| (bits) => bits.forEachSetBitSwar(accumulate), | |
| ), | |
| _BitArrayBenchmark( | |
| 'forEachSetBit.accelerated', | |
| 25, | |
| (bits) => bits.forEachSetBitAccelerated(accumulate), | |
| ), | |
| // select(k) for k near the middle of a quarter-full array. | |
| _BitArrayBenchmark( | |
| 'select.swar', | |
| 25, | |
| (bits) => sink ^= bits.selectSwar(_benchSize >> 3), | |
| ), | |
| _BitArrayBenchmark( | |
| 'select.accelerated', | |
| 25, | |
| (bits) => sink ^= bits.selectAccelerated(_benchSize >> 3), | |
| ), | |
| // complementCardinality: popcount of (~w & 0xFFFFFFFF) per word. | |
| _BitArrayBenchmark( | |
| 'complementCardinality.swar', | |
| 25, | |
| (bits) => sink ^= bits.complementCardinalitySwar(), | |
| ), | |
| _BitArrayBenchmark( | |
| 'complementCardinality.accelerated', | |
| 25, | |
| (bits) => sink ^= bits.complementCardinalityAccelerated(), | |
| ), | |
| // totalBitLength: sum int.bitLength across every word. | |
| _BitArrayBenchmark( | |
| 'totalBitLength.swar', | |
| 25, | |
| (bits) => sink ^= bits.totalBitLengthSwar(), | |
| ), | |
| _BitArrayBenchmark( | |
| 'totalBitLength.accelerated', | |
| 25, | |
| (bits) => sink ^= bits.totalBitLengthAccelerated(), | |
| ), | |
| // Bitwise pair ops: scalar word-level vs Int32x4 SIMD. | |
| _BitArrayPairBenchmark('intersection.swar', 25, BitArray.intersectionSwar), | |
| _BitArrayPairBenchmark( | |
| 'intersection.accelerated', | |
| 25, | |
| BitArray.intersectionAccelerated, | |
| ), | |
| _BitArrayPairBenchmark('union.swar', 25, BitArray.unionSwar), | |
| _BitArrayPairBenchmark('union.accelerated', 25, BitArray.unionAccelerated), | |
| _BitArrayPairBenchmark('xor.swar', 25, BitArray.xorSwar), | |
| _BitArrayPairBenchmark('xor.accelerated', 25, BitArray.xorAccelerated), | |
| _BitArrayPairBenchmark('difference.swar', 25, BitArray.differenceSwar), | |
| _BitArrayPairBenchmark( | |
| 'difference.accelerated', | |
| 25, | |
| BitArray.differenceAccelerated, | |
| ), | |
| // Complement: scalar ~ vs Int32x4 XOR-with-all-ones. Ignores the | |
| // second input of the pair-benchmark harness. | |
| _BitArrayPairBenchmark( | |
| 'complement.swar', | |
| 25, | |
| (a, _, out) => BitArray.complementSwar(a, out), | |
| ), | |
| _BitArrayPairBenchmark( | |
| 'complement.accelerated', | |
| 25, | |
| (a, _, out) => BitArray.complementAccelerated(a, out), | |
| ), | |
| ]; | |
| } | |
| void main() { | |
| checkCorrectness(); | |
| for (final benchmark in _benchmarks()) { | |
| benchmark.report(); | |
| } | |
| } |