Project

General

Profile

Ассемблер. Реализация POPCNT для MC P1

Added by sprin about 11 years ago

Здравствуйте.

Решил реализовать для MC P1 алгоритм реализации команды POPCNT, которая может быть использована для эффективного поиска в огромном объёме данных.
Она работает посредством подсчета количества бит множества в объекте данных.
Пример приложений, которые получат преимущества от использования этой инструкции: выявление генома, распознавание почерка, медицина и быстрое вычисление хэмминговского расстояния и заполнения.

Ресурсы:

программно-ориентированные ускорители (набор команд)
SSE4 - Википедия
chessprogramming.wikispaces.com собраны основные алгоритмы и ссылки по теме POPCNT.
Benchmarking CRC32 and PopCnt instructions


Алгоритмы реализовал (с учётом возможностей MC P1) по 4 варианта для входных значений размерностью 32 бит и 64 бит.

mc_POPCNT32_v1, mc_POPCNT64_v1 - The PopCount routine
mc_POPCNT32_v2, mc_POPCNT64_v2 - Lookup (Подстановка из таблицы на 256 элементов готовых значений)
mc_POPCNT32_v3, mc_POPCNT64_v3(+b) - Lookup (Подстановка из таблицы на 256 элементов готовых значений)
mc_POPCNT32_v4, mc_POPCNT64_v4 - HAKMEM 169


В теории, если все команды будут выполняться за 1 такт, то можно посчитать минимальное время выполнения функции, что я и сделал:

Графики (если все команды будут выполняться за 1 такт)

Show


На практике результаты не проверялись.


Replies (51)

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by krufter_multiclet about 11 years ago

Спасибо, протестируем и добавим в библиотеку.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by sprin about 11 years ago

Здравствуйте.

Протестировал алгоритмы реализации команды POPCNT на отладочном комплекте НW1-MCp04.

Для тестирования взял простую задачу: "В цикле считается сумма значений возвращаемых алгоритмом".

1. Входные значения из счётчика цикла. (Циклов: 1048576)

Примерно так (но на ASM)

Show

2. Входные значения из таблицы в памяти. (размерность исходной таблицы 65536 байт, шаг в массиве 1 байт, считывалось 4 или 8 байт)

Примерно так (но на ASM)

Show


Скорость замерял по таймеру "TIM0"

Настройки таймера

Show


Использовался: MultiCletSDK_ru.20131105.exe

Тесты:
  • 1. Входные значения из счётчика цикла. (Циклов: 1048576)
    • Тест 1: Вызов функции. Входные параметры через стек. Результат суммируется с результатом в ПАМЯТИ (читаем из памяти->суммируем->записываем в память)
    • Тест 2: Вызов функции. Входные параметры через стек. Результат суммируется с результатом в РОН (читаем из РОН->суммируем->записываем в РОН)
    • Тест 3: Вызов функции INLINE. Входной параметр задаётся в РОН. Результат суммируется с результатом в ПАМЯТИ (читаем из памяти->суммируем->записываем в память)
    • Тест 4: Вызов функции INLINE. Входной параметр задаётся в РОН. Результат суммируется с результатом в РОН (читаем из РОН->суммируем->записываем в РОН)
  • 2. Входные значения из таблицы в памяти. (размерность исходной таблицы 65536 байт, шаг в массиве 1 байт, считывалось 4 или 8 байт)
    • Тест 5: Вызов функции. Входные параметры через стек. Результат суммируется с результатом в ПАМЯТИ (читаем из памяти->суммируем->записываем в память)
    • Тест 6: Вызов функции. Входные параметры через стек. Результат суммируется с результатом в РОН (читаем из РОН->суммируем->записываем в РОН)
    • Тест 7: Вызов функции INLINE. Входной параметр задаётся в ИР (т.е. значение читается из памяти). Результат суммируется с результатом в ПАМЯТИ (читаем из памяти->суммируем->записываем в память)
    • Тест 8: Вызов функции INLINE. Входной параметр задаётся в ИР (т.е. значение читается из памяти). Результат суммируется с результатом в РОН (читаем из РОН->суммируем->записываем в РОН)

НW1-MCp04 MC P1: 80 МГц
Алгоритм Тест 1 Тест 2 Тест 3 Тест 4 Тест 1 Тест 2 Тест 3 Тест 4 Тест 1 Тест 2 Тест 3 Тест 4
Мегабайт/сек * На 1 цикл уходит тактов (примерно) Такты (на 1048576 циклов)
mc_POPCNT32_v1 2,312 2,312 132,000 132,000 138412076 138412076
mc_POPCNT32_v2 3,317 3,317 92,000 92,000 96469036 96469036
mc_POPCNT32_v3 2,988 2,988 8,005 4,188 102,125 102,125 38,125 72,875 107085868 107085868 39977020 76415020
mc_POPCNT32_v4 1,774 1,774 172,000 172,000 180355116 180355116
mc_POPCNT64_v1 4,391 4,391 139,000 139,000 145752108 145752108
mc_POPCNT64_v2 5,827 5,827 104,750 104,750 109838380 109838380
mc_POPCNT64_v3 5,502 5,502 13,725 7,432 110,938 110,938 44,469 82,125 116326444 116326444 46628928 86114348
mc_POPCNT64_v3b 5,536 5,536 110,250 110,250 115605548 115605548
mc_POPCNT64_v4 3,281 3,281 186,000 186,000 195035180 195035180
  • 1 Мб = 2^20 байт

Почему "Тест 3" и "Тест 4" дали такие результаты, мне непонятно. Предполагалось, что при использовании РОН ("Тест 4") результат будет не хуже, чем через память("Тест 3").


Алгоритм Тест 5 Тест 6 Тест 7 Тест 8 Тест 5 Тест 6 Тест 7 Тест 8 Тест 5 Тест 6 Тест 7 Тест 8
Мегабайт/сек * На 1 цикл уходит тактов (примерно) Такты (на 65536 циклов)
mc_POPCNT32_v1 2,295 2,295 133,001 133,001 8716334 8716334
mc_POPCNT32_v2 3,281 3,281 93,001 93,001 6094894 6094894
mc_POPCNT32_v3 2,961 2,961 5,606 4,100 103,062 103,062 54,439 74,439 6754302 6754302 3567720 4878440
mc_POPCNT32_v4 1,764 1,764 173,001 173,001 11337774 11337774
mc_POPCNT64_v1 4,360 4,360 140,001 140,001 9175086 9175086
mc_POPCNT64_v2 5,769 5,769 105,797 105,797 6933494 6933494
mc_POPCNT64_v3 5,457 5,457 9,458 7,220 111,846 111,846 64,534 84,534 7329934 7329934 4229290 5540010
mc_POPCNT64_v3b 5,486 5,486 111,250 111,250 7290904 7290904
mc_POPCNT64_v4 3,264 3,264 187,001 187,001 12255278 12255278
  • 1 Мб = 2^20 байт

Аналогично, почему "Тест 7" и "Тест 8" дали такие результаты, мне непонятно. Предполагалось, что при использовании РОН ("Тест 8") результат будет не хуже, чем через память("Тест 7").


Кусок кода по INLINE для "Тест 3" и "Тест 4":

Кусок кода по INLINE

Show


RE: Ассемблер. Реализация POPCNT для MC P1 - Added by Zveruga about 11 years ago

А можно таблицу результатов тестирования суммирования битов в блоках размером 250 и 65537 байт с указанием количества потребовавшихся тактов, чтобы сравнить с тестами Петра Канковского (если я правильно понял фамилию Peter Kankowski)?

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by krufter_multiclet about 11 years ago

А где в 4-м тесте используются РОНы? Что-то я не заметил их. Или прикреплённые выше исходники - это не последняя версия?

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by sprin about 11 years ago

Zveruga wrote:

А можно таблицу результатов тестирования суммирования битов в блоках размером 250 и 65537 байт с указанием количества потребовавшихся тактов, чтобы сравнить с тестами Петра Канковского (если я правильно понял фамилию Peter Kankowski)?

Добавил в пост выше тестирование через таблицу размерностью 65536, шаг в массиве 1 байт, считывалось 4 или 8 байт. Это немного не то что надо, но оценить можно.


krufter_multiclet wrote:

А где в 4-м тесте используются РОНы? Что-то я не заметил их. Или прикреплённые выше исходники - это не последняя версия?

Добавил в пост выше кусок кода INLINE как пример реализации. Суммировать в РОН или ПАМЯТЬ задаётся в "параметрах условной компиляции", если не 0, то блок будет компилироваться, одновременно оба параметра не могут быть 0 или не 0:

     ; Параметры условной компиляции
    .alias UseGPR3 1
    .alias UseSummPOPCNT 0


RE: Ассемблер. Реализация POPCNT для MC P1 - Added by Zveruga about 11 years ago

В одном цикле подсчитывается количество битов равных "1" в одном байте? Если так, то это на порядок хуже чем в процессорах архитектуры х86. Минимум 54 такта на подсчет количества битов в одном байте это слишком много. Даже если перебирать все биты поочерёдно, коих всего 8 штук, должно выйти быстрее. Где-то, что-то не так.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by sprin about 11 years ago

Zveruga wrote:

В одном цикле подсчитывается количество битов равных "1" в одном байте? Если так, то это на порядок хуже чем в процессорах архитектуры х86.

Нет, в зависимости от реализации алгоритма или в 4 (POPCNT_32) или в 8 (POPCNT_64) байтах.
Пост выше по таблице уточнил.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by Zveruga about 11 years ago

Если в цикле х32 подсчитывается количество битов в 4-х байтах, то во время теста в 65536 циклов будет вычислено 262144 байта.

Как я понял вы применили алгоритм bit hacks, когда есть специальная таблица из 256 байтов и рассчитываемый байт является индексом для поиска в этой таблице готового результата. Как в таком алгоритме обойтись без памяти не знаю.

Согласно тестам Петра Канковского на других процессорах без применения специальных инструкций используя метод bit hacks для вычисления 262151 байтов затрачивается 507126 тактов.

Мультиклет затрачивает на таблицу из 262144 байта минимум 3567720 тактов, это в 7 раз хуже.

В общем нужно разбирать текущий алгоритм и искать алгоритмы расчета без использования памяти.

Возможно причина кроется в индексном обращении к памяти. Эта команда выполняется не за один такт.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by krufter_multiclet about 11 years ago

1) Можно попробовать отключить контроль чтения и записи, бит 6 в PSW.
2) Обращение к памяти конечно может что-то задержать, но не так сильно.
3) В идеале нам нужно эту программу проанализировать и посмотреть где и что нас задерживает.
4) Измеряли сколько тактов длится параграф code_main?
5) Если анализировать большее количество данных в одном параграфе, то скорость будет подниматься в разы.
6) code_loop можно же перенести в параграф code_main, этим мы сократим время.
7) Сэкономить такты мы также можем применением в качестве счётчика индексных регистров.

На мой взгляд получить ускорение в 7 раз реально, но надо постараться сделать быстрее чем у x86.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by sprin about 11 years ago

Я тут ещё немного поэкспериментировал, получилось:

1. Сделал цикл + расчёт в одном параграфе, результат остался примерно прежним (по сравнению с "Тест 7" mc_POPCNT64_v3 разница в тактах равна 22580, т.е. менее 0,6%)
2. 6 бит регистра PSW на результаты не повлиял на пункт 1 (остальные варианты не тестировал)
3. Решил ради интереса убрать запись РЕЗУЛЬТАТА в ПАМЯТЬ/РОН ("wrl @r4, SummPOPCNT"/"setl #gpr3, @r4" см. "Кусок кода по INLINE") на "Тест 7" mc_POPCNT64_v3 получилось:

Алгоритм Мегабайт/сек На 1 цикл уходит тактов (примерно) Такты (на 65536 циклов)
mc_POPCNT64_v3 16,599 36,771 2409854

т.е. получается ... одним словом плохо у МС P1 с записью. Тратить примерно 27 тактов на запись, это как-то многовато. Хотя может у МС умный планировщик, и некоторые команды он выбрасывает, тогда на запись получится меньше 27 тактов?

И ещё похоже, что между параграфами проходит примерно от 10 тактов.

4. Индексные регистры конечно можно использовать в качестве автоматического счётчика, но ограничение в 65536 итераций сильно усложняет их использование.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by krufter_multiclet about 11 years ago

Советы по ускорению я конечно дать могу, но сначала мы посмотрим временные диаграммы, как и что работает на вашей программе.
Тут не в записи дело, c записью плохо быть не должно, просто дело в том, что когда стоит запись в конце клетки не пойдут выбирать команды из следующего параграфа, но 27 тактов многовато, нам нужно у себя перепроверить сначала. Спасибо за подробные тесты и эксперименты, как проанализируем результат работы программы, так сразу отпишусь.

Так ради эксперимента попробуйте поставить getl 123 после последней команды в параграфе.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by sprin about 11 years ago

krufter_multiclet wrote:

Так ради эксперимента попробуйте поставить getl 123 после последней команды в параграфе.

Пробовал, ставить в конец параграфа "code_main" 3 команды "getl 0". На результат не повлияло.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by krufter_multiclet about 11 years ago

С первым процессором пока разбираемся, на новом процессоре P2 код во вложении отработал в несколько раз быстрее.
Вложение

Пример для теста

Show

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by krufter_multiclet almost 11 years ago

sprin, а можете файл выложить с ответами, т.е. то что должно лежать в памяти после 65536 циклов.
У процессоров нет умного планировщика, т.е. команды не отбрасываются, если они есть в буфере, то они выполнятся.
И ещё вопрос если, например, процессор сделает за 30 тактов один цикл из тех трёх параграфов, что я привёл в посте выше, то это нормально или нет?

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by sprin almost 11 years ago

Здравствуйте.

krufter_multiclet wrote:

sprin, а можете файл выложить с ответами, т.е. то что должно лежать в памяти после 65536 циклов.
У процессоров нет умного планировщика, т.е. команды не отбрасываются, если они есть в буфере, то они выполнятся.
И ещё вопрос если, например, процессор сделает за 30 тактов один цикл из тех трёх параграфов, что я привёл в посте выше, то это нормально или нет?

1. Как я понимаю, вы используете "Тест 4", только на другое количество тактов. Тут есть один нюанс, если вы используете код, что привели выше, то надо дополнительно сначала обнулять регистр суммы. А итоговую сумму для такого цикла как в примере, можно вычислить по формуле: POPCNT(SUM(1 .. 2 N)) = (2 N) / 2 * N, где N >= 1. Т.е. (2 16) / 2 * 16 = 524288, для (2 20) / 2 * 20 = 10485760. Если не ошибся, то вроде так. Было бы неплохо протестировать и на массиве ("Тест 7" и "Тест 8" исходник давал выше )
2. Понятно
3. Тут вопрос не ко мне, но если сравнивать с Intel, то это примерно раз в 5 медленнее. (На самом деле сравнивать с Intel не считаю разумным, лучше сравнивать в своей весовой категории, было бы неплохо сравнить с Raspberry PI). Если количество сделанных циклов достаточно большое, то влияние параграфов на считывание время начала и окончания расчётов будут минимальны, и получится будут влиять только 2 параграфа (в примере выше: цикл, тело). Так что лучше брать достаточно большое количество циклов. Дальше просто считаем сколько тактов ушло на 1 цикл.

Кстати можно ещё протестировать на P2 двумя параграфами (цикл, тело) и одним параграфом (цикл + тело) и посмотреть сколько уходит тактов между параграфами и есть ли разница (на P1 её почти не было).

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by Zveruga almost 11 years ago

Тут без специальной команды ни как. Любое условие, а в алгоритме popcnt их много, замедляет работу Мультиклета. Значит нужно применить такой алгоритм, в котором в пределах одного параграфа производится максимум вычислений без каких-либо сравнений (выполнения перехода между параграфами). По моему простым побитовым сдвигом байта для подсчета битов используя команды slr и adc (сложение с учетом переноса) можно посчитать быстрее, но у Мультиклета 4 клетки и сдвиг байта в каждой клетке будет выдавать результат во флаг переноса. Реализовать такой алгоритм оптимально будет невозможно. Тут поможет только ввод специальной команды, коих в Мультиклете так мало.

Можно также реализовать такой алгоритм как обработка каждого байта 8 раз через команду and выделяющую бит, а потом сдвигая результат до первого разряда. Далее нужно просто сложить все 8 результатов. Но такой алгоритм тоже займет много тактов, но исключит все промежуточные условные переходы.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by krufter_multiclet almost 11 years ago

В новом процессоре команд добавится прилично. Но мне пока запрещено выкладывать систему команд в открытый доступ.

Значит нужно применить такой алгоритм, в котором в пределах одного параграфа производится максимум вычислений

Чем больше вычислений производится в одном параграфе, тем лучше. Сравнивать с Intel необходимо, только нужно определиться с каким именно и посмотреть результат работы.
Протестирую время работы при большом цикле, а также посмотрю разницу между двумя параграфами (цикл, тело) и одним параграфом (цикл + тело). Но для начала нужно разобраться откуда такие задержки в P1.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by krufter_multiclet almost 11 years ago

sprin wrote:

3. Тут вопрос не ко мне, но если сравнивать с Intel, то это примерно раз в 5 медленнее. (На самом деле сравнивать с Intel не считаю разумным, лучше сравнивать в своей весовой категории, было бы неплохо сравнить с Raspberry PI). Если количество сделанных циклов достаточно большое, то влияние параграфов на считывание время начала и окончания расчётов будут минимальны, и получится будут влиять только 2 параграфа (в примере выше: цикл, тело). Так что лучше брать достаточно большое количество циклов. Дальше просто считаем сколько тактов ушло на 1 цикл.

Кстати можно ещё протестировать на P2 двумя параграфами (цикл, тело) и одним параграфом (цикл + тело) и посмотреть сколько уходит тактов между параграфами и есть ли разница (на P1 её почти не было).

Т.е. если у нас те 3 параграфа идут за 30 тактов, то Интел сделает за 6 тактов? А можно ссылку на результаты Интела.
Просто 6 тактов никак не выжать и на другом процессоре.

P.S. Завтра проверю тест на новом процессоре, посмотрим отличия.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by sprin almost 11 years ago

krufter_multiclet wrote:

Т.е. если у нас те 3 параграфа идут за 30 тактов, то Интел сделает за 6 тактов? А можно ссылку на результаты Интела.
Просто 6 тактов никак не выжать и на другом процессоре.

Первый пост 4 ссылка - "Benchmarking CRC32 and PopCnt instructions".

Там смотрим в "PopCnt instruction" табличку. Строка с тестом "Table" и размерностью "262151 byte" (т.к. там входные данные по 4 байта, а нам надо для 65536 циклов). Получаем примерно 6,347 тактов за 1 цикл. Тут ничего особого нет, т.к. на Intel часть команд декодируются и выполняются параллельно.

Есть ещё одна особенность, та реализация теста "Table" не совсем хорошо сделана, если взять второй вариант , то получится ещё быстрее.


Вот пример на довольно стареньком процессоре:

Intel(R) Celeron(R) D CPU 3.06GHz

Show

Результат

Show

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by krufter_multiclet almost 11 years ago

По ссылке взяли следующий код для Intel:

код на СИ для Intel

Show

код на ассемблере для Intel

Show

Параметры процессора:

Intel(R) Core(TM)2 Duo CPU E7500 @ 2.93GHz

Show

У нас получилось на нашем процессоре от Intel для 262151000 циклов время 0m3.436s, откуда получаем 20 тактов на цикл при частоте 1,6 ГГц.
Теоретически если в одном параграфе для мультиклеточного процессора сделать больше вычислений, то можно превзойти этот результат.
Правильно ли мы протестировали Intel?

Makefile (61 Bytes) Makefile
popcnt (8.65 KB) popcnt
cpu_info.txt (1.61 KB) cpu_info.txt
popcnt.cpp (1.04 KB) popcnt.cpp
popcnt.s (4.45 KB) popcnt.s
time.txt (41 Bytes) time.txt

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by krufter_multiclet almost 11 years ago

Пока предварительно наилучшим образом влияет производство 4-6 вычислений в одном параграфе. Даже на P1 удалось добиться предварительно со 118 тактов уменьшения времени на цикл до 18 тактов (но откуда такая задержка мы ещё разбираемся). На P2 думаю можно ещё быстрее, время нужно найти свободное, чтобы тесты прогнать. Как сделаю, так и сообщу и приведу листинги. Т.е. сделать библиотеку, которая будет быстрее Intel работать можно на мультиклеточном процессоре в ближайшее время.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by sprin almost 11 years ago

krufter_multiclet wrote:

У нас получилось на нашем процессоре от Intel для 262151000 циклов время 0m3.436s, откуда получаем 20 тактов на цикл при частоте 1,6 ГГц.
Теоретически если в одном параграфе для мультиклеточного процессора сделать больше вычислений, то можно превзойти этот результат.
Правильно ли мы протестировали Intel?

Чем вас не устроили уже готовые тесты Benchmarking CRC32 and PopCnt instructions -> Download the code MSVC, 30 KB ? К тому же там подсчёт идет именно через такты, а не через таймер.


krufter_multiclet wrote:

Пока предварительно наилучшим образом влияет производство 4-6 вычислений в одном параграфе. Даже на P1 удалось добиться предварительно со 118 тактов уменьшения времени на цикл до 18 тактов (но откуда >такая задержка мы ещё разбираемся). На P2 думаю можно ещё быстрее, время нужно найти свободное, чтобы тесты прогнать. Как сделаю, так и сообщу и приведу листинги.

Хорошо.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by krufter_multiclet almost 11 years ago

Мы хотели проверить готовые тесты и получить похожие результаты. Тесты готовые насколько я понимаю 3-х летней давности, поэтому хотелось бы повторить их и получить результат в тактах на конкретных процессорах. Но после проверки приведённой функции мы получили 20 тактов, а не 6-7. Может мы что-то не правильно посчитали для процессора Intel, либо тест проводился на процессоре с аппаратной поддержкой popcnt. В принципе можно автору тех тестов написать и спросить на каком именно процессоре он их проводил и почему у нас результаты отличаются.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by sprin almost 11 years ago

krufter_multiclet wrote:

Мы хотели проверить готовые тесты и получить похожие результаты. Тесты готовые насколько я понимаю 3-х летней давности, поэтому хотелось бы повторить их и получить результат в тактах на конкретных процессорах. Но после проверки приведённой функции мы получили 20 тактов, а не 6-7. Может мы что-то не правильно посчитали для процессора Intel, либо тест проводился на процессоре с аппаратной поддержкой popcnt. В принципе можно автору тех тестов написать и спросить на каком именно процессоре он их проводил и почему у нас результаты отличаются.

Что-то я не понимаю, я дал ссылку на исходный код для тестирования, надо только собрать, запустить и получить результат. Я выше приводил результат для Intel(R) Celeron(R) D CPU 3.06GHz, они получены по программе автора (+ несколько доп. тестов), которая находится на той же странице, сразу перед комментариями.

RE: Ассемблер. Реализация POPCNT для MC P1 - Added by krufter_multiclet almost 11 years ago

Ну вот запустил на своём процессоре:

Pentium Dual Core(R) CPU 5700 3.00GHz

Show

Т.е. получается для теста Table при 262151 итерации 561225 тактов. Откуда получаем, что на одну итерацию требуется чуть больше 2 тактов. С этим результатом мы несогласны. Мы считаем, что время в тесте измеряется неправильно.

Вот в этих функциях:

UINT64 inline GetRDTSC() {
   __asm {
      ; Flush the pipeline
      XOR eax, eax
      CPUID
      ; Get RDTSC counter in edx:eax
      RDTSC
   }
}

UINT Benchmark(CRC_FUNC func, const BYTE * buffer, SIZE_T length, OUT RES & result) {
    UINT min_time = UINT_MAX;
    RES res = 0;

    for (UINT j = 0; j < 20; j++) {
        UINT64 start_time = GetRDTSC();
        res = func(buffer, length);
        UINT time = (UINT)(GetRDTSC() - start_time);
        min_time = min(min_time, time);
    }
    result = res;
    return min_time;
}

Мы видим приведение 64-х разрядного числа к 32-х разрядному, в результате чего можем получить неверный результат по тесту процессора Intel.
Просто прежде чем что-то запускать мы анализируем верно ли работает тест и верно ли идёт подсчёт времени. Перепишем сейчас подсчёт времени как нужно и посмотрим на результат.

(1-25/51)