Skip to content

Commit a32542b

Browse files
committed
some progress
1 parent e2930d4 commit a32542b

File tree

2 files changed

+89
-34
lines changed

2 files changed

+89
-34
lines changed

benchmark/Benchmark.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
using System.IO;
1212
using System.Collections.Generic;
1313
using System.Linq;
14+
using BenchmarkDotNet.Engines; // Correct namespace for HardwareCounter
15+
1416

1517

1618
namespace SimdUnicodeBenchmarks
@@ -105,7 +107,18 @@ protected void IntroduceError(byte[] utf8, Random random)
105107
}
106108

107109

108-
[MemoryDiagnoser]
110+
[MemoryDiagnoser]
111+
// [HardwareCounters(
112+
// HardwareCounter.BranchInstructions,
113+
// HardwareCounter.BranchMispredictions,
114+
// HardwareCounter.CacheMisses,
115+
// HardwareCounter.TotalCycles,
116+
// HardwareCounter.TotalInstructions,
117+
// HardwareCounter.L1CacheMisses,
118+
// HardwareCounter.L2CacheMisses,
119+
// HardwareCounter.L3CacheMisses,
120+
// HardwareCounter.InstructionRetired
121+
// )]
109122

110123
public class SyntheticBenchmark : BenchmarkBase
111124
{

src/UTF8_validation.cs

Lines changed: 75 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
using System.Linq;
55
using System.Runtime.CompilerServices;
66

7-
87
// C# already have something that is *more or less* equivalent to our C++ simd class:
98
// Vector256 https://learn.microsoft.com/en-us/dotnet/api/system.runtime.intrinsics.vector256-1?view=net-7.0
109
// I extend it as needed
@@ -84,6 +83,25 @@ namespace SimdUnicode
8483
public static unsafe class Utf8Utility
8584
{
8685

86+
// Helper functions for debugging
87+
// string VectorToString(Vector256<byte> vector)
88+
// {
89+
// Span<byte> span = stackalloc byte[Vector256<byte>.Count];
90+
// vector.CopyTo(span);
91+
// return BitConverter.ToString(span.ToArray());
92+
// }
93+
94+
// string VectorToBinary(Vector256<byte> vector)
95+
// {
96+
// Span<byte> span = stackalloc byte[Vector256<byte>.Count];
97+
// vector.CopyTo(span);
98+
99+
// var binaryStrings = span.ToArray().Select(b => Convert.ToString(b, 2).PadLeft(8, '0'));
100+
// return string.Join(" ", binaryStrings);
101+
// }
102+
103+
104+
87105

88106

89107
// Returns a pointer to the first invalid byte in the input buffer if it's invalid, or a pointer to the end if it's valid.
@@ -98,14 +116,23 @@ public static unsafe class Utf8Utility
98116
var checker = new SimdUnicode.utf8_validation.utf8_checker();
99117
int processedLength = 0;
100118

119+
// Helpers.CheckForGCCollections("Before AVX2 procession");
101120
while (processedLength + 32 <= inputLength)
102121
{
122+
// Console.WriteLine("-------New AVX2 vector blocked processing!------------");
123+
103124
Vector256<byte> currentBlock = Avx.LoadVector256(pInputBuffer + processedLength);
125+
// Helpers.CheckForGCCollections($"Before check_next_input:{processedLength}");
104126
checker.check_next_input(currentBlock);
127+
// Helpers.CheckForGCCollections($"After check_next_input:{processedLength}");
105128

106129
processedLength += 32;
130+
107131
}
108132

133+
// Helpers.CheckForGCCollections("After AVX2 procession");
134+
135+
109136
if (processedLength < inputLength)
110137
{
111138
Span<byte> remainingBytes = stackalloc byte[32];
@@ -121,6 +148,24 @@ public static unsafe class Utf8Utility
121148

122149
}
123150

151+
// CheckForGCCollections("After processed remaining bytes");
152+
153+
154+
// if (processedLength < inputLength)
155+
// {
156+
// // Directly call the scalar function on the remaining part of the buffer
157+
// byte* invalidBytePointer = GetPointerToFirstInvalidByte(pInputBuffer + processedLength, inputLength - processedLength -1);
158+
159+
// // You can then use `invalidBytePointer` as needed, for example:
160+
// // if (invalidBytePointer != pInputBuffer + inputLength) {
161+
// // // Handle the case where an invalid byte is found
162+
// // }
163+
164+
// // Update processedLength to reflect the processing done by the scalar function
165+
// processedLength += (int)(invalidBytePointer - pInputBuffer);
166+
// }
167+
168+
124169
checker.check_eof();
125170
if (checker.errors())
126171
{
@@ -131,15 +176,19 @@ public static unsafe class Utf8Utility
131176
}
132177
}
133178

134-
135-
public static class utf8_validation
179+
// C# docs suggests that classes are allocated on the heap:
180+
// it doesnt seem to do much in this case but I tthought the suggestion to be sensible.
181+
public struct utf8_validation
136182
{
137-
public class utf8_checker
183+
public struct utf8_checker
138184
{
139185
Vector256<byte> error;
140186
Vector256<byte> prev_input_block;
141187
Vector256<byte> prev_incomplete;
142188

189+
190+
191+
143192
public utf8_checker()
144193
{
145194
error = Vector256<byte>.Zero;
@@ -156,11 +205,13 @@ public utf8_checker()
156205
public void check_next_input(Vector256<byte> input)
157206
{
158207
// Check if the entire 256-bit vector is ASCII
208+
159209
Vector256<sbyte> inputSBytes = input.AsSByte(); // Reinterpret the byte vector as sbyte
160210
int mask = Avx2.MoveMask(inputSBytes.AsByte());
161211
if (mask != 0)
162212
{
163213
// Contains non-ASCII characters, process the vector
214+
164215
check_utf8_bytes(input, prev_input_block);
165216
prev_incomplete = is_incomplete(input);
166217
}
@@ -316,21 +367,31 @@ private Vector256<byte> must_be_2_3_continuation(Vector256<byte> prev2, Vector25
316367
return comparisonResult.AsByte();
317368
}
318369

370+
371+
private static readonly byte[] MaxArray = new byte[32]
372+
{
373+
255, 255, 255, 255, 255, 255, 255, 255,
374+
255, 255, 255, 255, 255, 255, 255, 255,
375+
255, 255, 255, 255, 255, 255, 255, 255,
376+
255, 255, 255, 255, 255, 0b11110000 - 1, 0b11100000 - 1, 0b11000000 - 1
377+
};
378+
Vector256<byte> maxValue = Vector256.Create(MaxArray);
379+
319380
[MethodImpl(MethodImplOptions.AggressiveInlining)]
320381

321382
private Vector256<byte> is_incomplete(Vector256<byte> input)
322383
{
323384
// Console.WriteLine("Input Vector is_incomplete: " + VectorToString(input));
324-
byte[] maxArray = new byte[32]
325-
{
326-
255, 255, 255, 255, 255, 255, 255, 255,
327-
255, 255, 255, 255, 255, 255, 255, 255,
328-
255, 255, 255, 255, 255, 255, 255, 255,
329-
255, 255, 255, 255, 255, 0b11110000 - 1, 0b11100000 - 1, 0b11000000 - 1
330-
};
331-
Vector256<byte> max_value = Vector256.Create(maxArray);
332-
333-
Vector256<byte> result = SaturatingSubtractUnsigned(input, max_value);
385+
// byte[] maxArray = new byte[32]
386+
// {
387+
// 255, 255, 255, 255, 255, 255, 255, 255,
388+
// 255, 255, 255, 255, 255, 255, 255, 255,
389+
// 255, 255, 255, 255, 255, 255, 255, 255,
390+
// 255, 255, 255, 255, 255, 0b11110000 - 1, 0b11100000 - 1, 0b11000000 - 1
391+
// };
392+
// Vector256<byte> max_value = Vector256.Create(maxArray);
393+
394+
Vector256<byte> result = SaturatingSubtractUnsigned(input, maxValue);
334395
// Console.WriteLine("Result Vector is_incomplete: " + VectorToString(result));
335396

336397
return result;
@@ -352,25 +413,6 @@ private Vector256<byte> SaturatingSubtractUnsigned(Vector256<byte> left, Vector2
352413

353414
return subtractionResult.AsByte();
354415
}
355-
356-
357-
// Helper functions for debugging
358-
private string VectorToString(Vector256<byte> vector)
359-
{
360-
Span<byte> span = stackalloc byte[Vector256<byte>.Count];
361-
vector.CopyTo(span);
362-
return BitConverter.ToString(span.ToArray());
363-
}
364-
365-
private string VectorToBinary(Vector256<byte> vector)
366-
{
367-
Span<byte> span = stackalloc byte[Vector256<byte>.Count];
368-
vector.CopyTo(span);
369-
370-
var binaryStrings = span.ToArray().Select(b => Convert.ToString(b, 2).PadLeft(8, '0'));
371-
return string.Join(" ", binaryStrings);
372-
}
373-
374416
}
375417
}
376418
}

0 commit comments

Comments
 (0)