Check that the text, boxes and confidences don’t depend on the thread count or the CPU’s vector instructions.
The program reads the invoice at four degrees of parallelism and hashes the page JSON each time:
using System.Security.Cryptography; using System.Text; using OneImage; using OneOcr; OcrEngine engine = OcrEngine.LoadBundled(); Image image = Image.Load("invoice.png"); foreach (int threads in new[] { 1, 2, 4, 16 }) { OcrPage page = image.Ocr(engine, new OcrOptions { MaxDegreeOfParallelism = threads }); string json = OcrPageJson.ToJson([page]); string hash = Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(json)))[..16]; Console.WriteLine($"{engine.KernelPath,-9} threads={threads,-2} {page.Words.Count} words sha256={hash}"); } if (engine.PerformanceNotice is { } notice) Console.WriteLine(notice);
Avx512 threads=1 98 words sha256=DC165D8B4ADFF440 Avx512 threads=2 98 words sha256=DC165D8B4ADFF440 Avx512 threads=4 98 words sha256=DC165D8B4ADFF440 Avx512 threads=16 98 words sha256=DC165D8B4ADFF440
The same program with AVX-512 switched off, and then with AVX2 off, which leaves the scalar path:
$ DOTNET_EnableAVX512=0 dotnet run Avx2 threads=1 98 words sha256=DC165D8B4ADFF440 Avx2 threads=2 98 words sha256=DC165D8B4ADFF440 Avx2 threads=4 98 words sha256=DC165D8B4ADFF440 Avx2 threads=16 98 words sha256=DC165D8B4ADFF440
$ DOTNET_EnableAVX2=0 dotnet run Scalar threads=1 98 words sha256=DC165D8B4ADFF440 Scalar threads=2 98 words sha256=DC165D8B4ADFF440 Scalar threads=4 98 words sha256=DC165D8B4ADFF440 Scalar threads=16 98 words sha256=DC165D8B4ADFF440 The scalar kernel path is running although this CPU supports AVX2 and FMA; OCR is far slower (about 100 times for a NativeAOT binary) but gives the same text. A NativeAOT app published below x86-64-v3 does this: set <IlcInstructionSet>x86-64-v3</IlcInstructionSet> (or x86-64-v4 for AVX-512) in the app project. The RecursiveAI.OneOcr package sets v3 for x64 publishes unless you set your own. Turning the instruction sets off with DOTNET_EnableAVX2=0 or DOTNET_EnableHWIntrinsic=0 has the same effect.
- One hash on every kernel path and thread count. Dot products use fused multiply-add with a fixed 16-lane accumulation order whatever the vector width,
exp,tanh,logand softmax are OneOcr’s own, and no sum crosses threads. The rules are in Numeric rules. PerformanceNoticeis set when the scalar path runs on a CPU that could do better. In a NativeAOT app the usual cause is an instruction-set level belowx86-64-v3; theRecursiveAI.OneOcrpackage sets v3 for x64 publishes unless your project chooses its own.- The scalar path gives the same text but takes far longer: keep the switches above for checks like this one.