---
title: "Same bytes on every machine"
url: https://recursiveai.org/oneocr/cookbook/same-bytes-everywhere/
description: "Check that the text, boxes and confidences don’t depend on the thread count or the CPU’s vector instructions."
version: "1.0.0"
---

# Same bytes on every machine

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:

```csharp
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);
```

```text
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:

```shell
$ 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
```

```shell
$ 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`, `log` and softmax are OneOcr’s own, and no sum crosses threads. The rules are in [Numeric rules](https://recursiveai.org/oneocr/docs/numerics/).
- `PerformanceNotice` is 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 below `x86-64-v3`; the `RecursiveAI.OneOcr` package 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.
