Код к статье про устранение проверок границ массива в RyuJIT: шесть циклов, четыре рантайма (.NET 8/9/10/11), четыре машины. Один цикл JIT дожал в .NET 9, ещё один — в .NET 10, обратные циклы из issue 2017 года — в .NET 11, а цикл с i != Length таскает проверку до сих пор. Плюс два контроля, чтобы инструменту можно было верить.
Статья на Хабре: Бенчмаркая проверки границ: фикс, который шёл 8 лет, и проверка, которая жива до сих пор
| Код | net8 | net9 | net10 | net11 | Пруф |
|---|---|---|---|---|---|
for (i=0; i < len; i++) |
— | — | — | — | эталон |
while ((uint)i < (uint)len) |
ЕСТЬ | — | — | — | PR #100777 (jakobbotsch) |
if (p.Len < s.Len) s[p.Len] |
ЕСТЬ | ЕСТЬ | — | — | PR #115980 (EgorBo) |
for (i=len-1; i >= 0; i--) |
ЕСТЬ* | ЕСТЬ* | ЕСТЬ* | — | #9505, с 2017 |
while (i != 0) src[--i] |
ЕСТЬ | ЕСТЬ | ЕСТЬ | — | тред #84697 |
for (i=0; i != len; i++) |
ЕСТЬ | ЕСТЬ | ЕСТЬ | ЕСТЬ | #84697 (xtqqczze) |
* у обратного цикла проверка на .net 8/9/10 живёт в запасной копии цикла (loop cloning): в бинарнике есть, времени не отнимает — платите размером кода. Контроли: FirstPlusLast — одна проверка вместо двух на любом рантайме; AtUnknownIndex — проверка везде, убрать нельзя.
Сводный трекер таких пропусков у команды JIT: #109677 (EgorBo). Картина совпала до инструкции на всех четырёх машинах (Ryzen 9 5950X, i9-10900KF, 2 x Xeon Silver 4314 x2).
Цикл for (i = 0; i != len; i++) — единственный, переживший чистки вплоть до .NET 11: проверка на каждой итерации, на Xeon и i9 это стабильные +6–31% к горячему циклу. В горячем коде пишите канонический for (int i = 0; i < arr.Length; i++) по локальной переменной массива — его JIT гарантированно дожимает во всех рантаймах. Проверяется одной переменной окружения DOTNET_JitDisasm — snap.bat в комплекте.
Дизасм (главный пруф). Нужны SDK .NET 8/9/10/11 в одном корне (установка — SETUP.md). Из папки Disasm/: snap.bat (Linux: bash snap.sh) → disasm_net8..11.txt, сверяй с таблицей выше. Готовые листинги с машины №3 лежат рядом.
Бенчмарк. Из корня: dotnet run -c Release -f net10.0. Четыре цикла × размеры 1К (L1) / 64К (L2) / 1М (L3) / 16М (RAM) × рантаймы 8/9/10. Прогон долгий — это нормально. .NET 11 в бенчмарке нет: BenchmarkDotNet 0.15.8 его пока не собирает (BDN #3017) — картина на 11 берётся из дизасма. После прогона в results/ появятся таблицы, сводка _RECAP.txt и графики _CHARTS.html.
Результаты моих прогонов на четырёх машинах — в Results/Comp_1..4, графики для статьи — в Docs/.
Subjects.cs— все подопытные циклы, один источник на бенчмарк и дизасмBenchmarks/BoundsCheckBench.cs— бенчмарк, 4 цикла × 4 размера × 3 рантаймаDisasm/— снятие машинного кода + готовые листинги net8–net11SETUP.md— установка SDK, включая .NET 11 preview



