Всем привет! Это моя первая статья на Хабре, так что буду рад любым комментариям. Я бы хотел затронуть одну из самых сложных и больных тем в IT и электронике, из-за которой создают сотни бенчмарков и тестов, но вопрос всё равно остается открытым: как сравнить 2 процессора? Какую архитектуру выбрать в конкретной задаче, когда у тебя ограниченный бюджет и условия?

Представьте ситуацию: вы выбираете между Intel Core i9 и Apple M2 (как пример двух мощных систем). Один потребляет 300 Ватт и греется как печка, другой — 30 Ватт и работает от батареи 20 часов. Один показывает 200 FPS в играх, другой — 90, но в три раза эффективнее. Один стоит $600, другой — встроен в ноутбук за $800. Кого вы выберете?

В данной статье я "пройдусь" по самым известным способам измерения эффективности процессоров, их проблемам и возможном решении.

1. Эффективны ли бенчмарки?

Один из самых очевидных и довольно часто эффективных способов измерить производительность вычислительной системы — бенчмарк. Их огромное множество, начиная с самописных алгоритмов и заканчивая сложнейшими синтетическими тестами.

По бенчмарку довольно легко понять, подходит ли вам процессор или нет, ведь все результаты теста видны в консоли. Но сделать бенчмарк, который вам покажет общую производительность на реальном коде и способность чипа выполнить вашу задачу максимально эффективно, притом учесть количество съеденных ватт и температуру в пике — довольно тяжело, и особенно когда у тебя нет этого процессора под рукой. А синтетические тесты вроде SPECint 2017 часто показывают только пиковую производительность без учета других характеристик.

Подавляющее большинство бенчмарков измеряет пропускную способность и пиковую скорость выполнения. Бенчмарков по измерению энергоэффективности (операции на джоуль и подобные) практически нет. Тем более в современной эре гетерогенных архитектур важнее стал показатель скорости передачи данных между общим процессором (CPU) и специальным акселератором вроде Google TPU (изза чего обучение моделей нередко эффективно на многоядерных CPU для минимизации пересылок данных). Исследования конца 2000-х и начала 2010-х показывали, что оптимизация под SPECint приводит к неоптимальным решениям с точки зрения энергопотребления. Это привело к появлению метрик EDP (Energy-Delay Product) и бенчмарков, измеряющих мощность в реальном времени.

А вот что насчет популярных флопсов, то даже раньше FLOPS (FLoat OPerationS) была не самой корректной оценкой производительности - если оценивать именно процессоры. Как и��ог: "гонкам за флопсами" и другие оптимизации микроархитектуры в пользу скорости на операциях с плавающей точкой.

Это привело к очень интересному феномену "Попадание в бенчмарк" (Benchmarketing) — оптимизации, которые как раз ведут к повышению производительности на заранее известных тестах и бенчмарках, теряя производительность на реальных программах. Некоторые производители настраивали SoC на короткие "спринты" высокой частоты именно во время запуска популярных бенчмарков, что не отражало устойчивую производительность (было популярно для Android-систем).

Не говоря уже о том, что очень много бенчмарков любят измерять результаты в баллах относительно эталона. Баллы могут считаться на разных бенчмарках по-разному, делая их довольно необъективной оценкой эффективности архитектуры.

Если интересно, можете почитать тут, здесь также описано несоответствие между результатом в бенчмарке и реальном использовании.

2. Численные метрики — полезнее баллов с бенчмарков

Конкретные числа - очень показательная штука, но надо выбрать "правильные" числа. А их не так уж и мало. Сейчас я разберу некоторые популярные метрики.

Hz - герцы как показатель скорости

Частота раньше считалась прямым показателем скорости. Выше частота - меньше времени занимает 1 такт конвейера (так как частота - количество тактов в секунду). Это кажется логичным, и потому до 2000-х годов производители чипов стремились повышать частоту любой ценой путем конвейеризации EX-стадии на несколько штук, уменьшая латентность каждой (подробнее о конвейере процессора). Правда потом уже все поняли, что повышение частоты >6 ГГц сулит большими проблемами с теплоотводом, а проектирование таких чипов — тяжёлая инженерная задача. Тем более для точности еще крайне важен IPC (Instructions Per Cycle), а не сырые гигагерцы.

IPC — удобные Instructions Per Cycle

IPC буквально означает "количество инструкций за цикл", или же инструкции за 1 такт процессора. Казалось бы, в чем смысл этой метрики, если процессор вроде бы и так выполняет по одной инструкции за такт? На деле все сложнее: разные инструкции имеют разные латентность (к примеру, mul в среднем в 3 раза медленнее сумматора), а так называемые зависимости по данным (см. data hazards) и подавна не дают выполнять ровно по инструкции за такт - техника bypassing хоть и решает проблемы RAW-зависимостей (Read-After-Write) и других хазардов, но обычно в сложных конвейерах приходиться ждать 1 такт на разрешение зависимости (как итог, IPC = 1 инстр / 2 такта = 0,5 на зависимых инструкциях). И наоборот: современные суперскалярные OOO (Out Of Order) процессоры имеют IPC > 1 за счет параллелизма инструкций.

Однако IPC коварен — он может сильно отличаться на разных задачах, и привести его к среднему значению бывает очень нелегко. Поэтому обычно берут средний IPC на реальных, смешанных программах, который чаще всего в разы ниже "синтетических" IPC с бенчмарков.

Средний IPC является очень важным параметром, особенно в связ��е с частотой. Обычно их комбинируют в единую метрику Perf, которая часто численно равна IPC * Hz (что логично - производительность равна количеству инструкций за такт, помноженное на количество тактов в секунду). Perf - среднее количество инструкций в секунду, если переформулировать. Именно Perf считается наиболее точной метрикой для измерения сырой производительности, ведь показывает усредненную скорость с учётом производительности на частых обращениях к памяти, кэш-миссам, мощностью векторных блоков и т.д.

Perf/Watt и Perf/mm² - "для гиков"

Практичные (кроме perf/$) метрики для большинства процессоров.

Perf/Watt — очень удобная метрика, особенно для RISC-процессоров, микроконтроллеров и IoT-устройств. Она обозначает среднее количество инструкций, которое наш чип способен выполнить, потратив 1 Ватт энергии. Этой единицей измерения очень удобно показывать эффективность чипов в микроконтроллерах, где каждый Ватт энергии на счету. Именно перфоманс на Ватт — главная фишка Apple Silicon благодаря множеству разных оптимизаций, включая UMA (Unified Memory Access) и мощному кластеру из E-cores, специально заточенных под энергоэффективность и фоновые задачи. Основная причина — отсутствие мощного кулера в ноутбуках, из-за чего тепло уходит пассивно через корпус, здесь как раз уместны низкие потребления.

Perf/mm² — очень интересная и не самая частая метрика измерения эффективности процессора, но, тем не менее, полезная. Она буквально обозначает, какую вычислительную мощь мы получаем на 1 мм² кремния этого чипа. Сложность в том, что по ней одной ты не узнаешь реальную производительность процессора, а получишь абстрактные "производительность на мм² площади". Как доп. критерий при покупке с ограниченным бюджетом может быть удобной, но часто она неполная. Тем не менее, высокая производительность на площадь явно намекает пользователю на современный техпроцесс (5нм, 3нм) и SoC (System-On-Chip), сильно уменьшая количество лишних шин данных и повышая плотность транзисторов, а также косвенно указывая на относительно дешёвую цену.

Есть еще одна метрика, довольно непрактичная — Perf/$. Я буду удивлен, если кто то с ней считался или вообще видел. Считать среднее количество инструкций в 1 доллар очень странно, но в связке с perf/watt может дать какое-то понимание.

3. Так в чем проблема?

А проблема вот в чем — нет единой метрики. Чтобы понять все "грани" той или иной модели чипа, надо знать хотя бы 2 из вышеперечисленных метрики или хорошо разбираться в архитектурах процессоров. Очень сложно понять, подходит ли той или иной процессор под ваши задачи, пока вы его не купите и не проведёте нужные вам бенчмарки или тесты. А сырые цифры - не для всех лёгкая штука.

Вот бы была такая метрика, которая учла бы все самые частые характеристики и могла высчитать "пользу" под вашу задачу. Точнее, уже формула.

4. Может, сделать единую формулу?

...которая объединит в себе и площадь, и мощность, и производительность!

Отличная математическая задачка. Давайте попробуем это сделать. Вот что мне пришло на ум:

Eff = (Perf * LogicalCores^a) / (TotalPower^b * TotalArea^c) / 10⁶

Где: 0<a<1; 0<b,c<1,5;

LogicalCores = (P_cores * SMT + E_cores)

Номер 1 — производительность

От нее зависит все. В чем будем измерять производительность? Конечно, не в герцах и не чистых IPC. К примеру, можно в Perf - он адекватно показывает среднее количество инструкций в секунду, а это как раз то, что определяет скорость одного ядра. Будем брать номинальную производительность на реальном коде, а не синтетике.

Также важной метрикой является количество ядер (LogicalCores), а точнее потоков и уровень параллелизма. Тут мы вводим коэффициент a — он показывает уровень, до которого мы можем распараллелить исходную задачу. 1 — задача идеально параллельна и все ядра загружены, 0 — распараллелить невозможно. При этом итоговый множитель нелинейный, что очень хорошо показывает закон Амдала.

Также тут использованы логические ядра (не путать с физическими). Многие современные процессоры поддерживают Hyper-Threading технологию, при которой одно ядро может запускать несколько потоков (обычно до 2). Логические ядра - это как раз количество физических ядер, умноженное на количество потоков на одно ядро. Для гетерогенных систем явно разделяем P-ядра и E-ядра.

Один минус — она не показывает латентность памяти и кэшей, что мы не учитываем. Как таковую пропускную способность памяти я не собираюсь измерять, ведь я фокусируюсь именно на измерении общей эффективности процессора.

Номер 2 — тепло и площадь

Это наши враги, убивающие производительность. Или же друзья — зависит от того, что мы ищем. Делители — TotalPower обозначает общее тепловыделение на все ядра чипа в Ваттах, а TotalArea — соответственно, всю площадь чипа в мм² (включая кэши, межъядерную коммуникацию и т.д, но не включая остальную периферию).

Также можно заметить, что b и c коэффициенты могут быть больше 1. Это зависит от задачи — если нужно выбрать самый энергоэффективный чип, то ставим на максимум b, если самый компактный — то высокий c.

Интересно, что TotalArea косвеннт обозначает цену чипа — ведь чем больше площадь, тем дороже чип, так как мы платим за бо́льший кусок кремния. В формуле c-коэффициент явно задает критерий площади, и в зависимости от кейса он может быть высоким.

Как это работает?

Коэффициенты вы подбираете сами под конкретные задачи. Вы сами выставляете a, b и c под нужные вам задачи, что очень важно. Она показывает не просто perf/watt, а именно то, насколько тот или иной процессор вам подходит.

Основная часть формулы довольно логична: мы делим общую производительность (это производительность одного ядра, умноженная на количество потоков) на общие затраты (вся потраченная мощность и затраты на площадь чипа). Коэффициенты, возводящие в степень компоненты формулы, регулируют их значимость. А конечный делитель 10⁶ просто нормализует результат до удобных для понимания.

Степени, кстати, с потолка не взяты. Коэффициент a, к примеру, прекрасно иллюстрирует закон Амдала, который даже на идеально параллельном коде не дает линейное ускорение при увеличении числа потоков. Даже при a = 0,9 на процессоре с 4 логическими ядрами мы получим примерный множитель 4^0,9 ≈ 3.48, а с 16 — только 12.1. С 24 ядрами — 17.46. Видно, что даже на максимально параллельном коде мы не получаем бонус в x4 раза, а только в x3,4. На меньшем коэффициенте a уровень нелинейности только растет.

Коэффициенты b и c аналогично влияют нелинейно на итоговый результат, регулируя "важность" параметров. Так, b = 1 делает линейный вклад потребления энергии в результат, а b = 1,5 сильно "наказывает" мощные процессоры, подводя результат в пользу энергоэффективных.

Пример конфигураций для разных задач (для ориентира):

  • Игровой ПК: a = 0,5 (2-3 ядра загружены); b = 0,5 (есть розетка и кулер); c = 0,2 (не важно). Эта конфигурация максимизирует однопоточную производительность

  • Сервер: a = 0,95 (максимальный); b = 1 (мощность важна); c = 0,4 (площадь кристалла мало влияет). Упор на многопоточность и perf/watt.

  • Телефон: a = 0,6 (в основном фоновый); b = 1,4 (очень важно); c = 0,6 (ограниченное место в корпусе). Упор на энергоэффективность.

Как пользоваться формулой

Красота этой формулы — полная конфигурируемость.

Посмотрим на конкретные примеры конфигураций — например, десктоп. Будем опираться на низкий параллелизм, небольшую мощность и не заоблачную цену:

— a = 0,6. Десктопные приложения имеют низкий параллелизм, single-thread решает.

— b = 1. Мощность важна, это видно - 1 приводит к линейной обратной зависимости от TotalPower

— c = 0,4. Площадь не так важна, ноутбук или моноблок большие.

Итак, давайте сравним 2 кандидата: Intel Core i9 и Apple M2 Pro (взял просто ради примера, вы можете сравнить вообще любые другие процессоры).

Важная оговорка: сразу скажу, что IPC (и частоту) я беру средние, из открытых исто��ников и проведённых бенчмарков вроде SPECint и Geekbench. Даже если в реальности он отличается на 0.1-0.3, это относительное сравнение, и разницы почти не сделает. Важен сам метод, а не абсолютные числа. Подставьте свои, более точные значения — и получите свою объективную оценку.

Intel Core i9 14900K:

  • Perf = IPC × Hz, как мы знаем. IPC ≈ 1,25 (в реальных программах); Hz ≈ 5,6 GHz (стабильная) -> Perf = 1,25 × 5,6 × 10⁹ = 7 000 000 000 инстр/сек в среднем

  • Logical cores = 8 * 2 + 16 = 32 потока

  • TotalPower ≈ 180 Ватт (под нагрузкой со слабым охлаждением)

  • TotalArea = 200 мм²

Считаем:

Eff = (7_000_000_000 * 32^0,6) / (180 ^ 1 * 200 ^ 0,4) / 10⁶ ≈ 56 * 10⁹ / 1499 / 10⁶ ≈ 37

Apple M2:

  • IPC ≈ 1,5 (среднее); Hz = 3,5 GHz (номинально) -> Perf = 5 250 000 000 инстр/сек

  • Logical cores = 6 * 2 + 4 = 16 потоков

  • TotalPower ≈ 20 Ватт (если учитывать только cpu)

  • TotalArea = 155 мм²

Eff = (5_250_000_000 * 16^0,6) / (20 ^ 1 * 155 ^ 0,4) / 10⁶ ≈ 27,7 * 10⁹ / 150,3 / 10⁶ ≈ 184

37 vs 184 — разница почти в 5 раз! Но не стоит думать, что M2 теперь имеет в 5 раз большую производительность: несмотря на высокую частоту и количество ядер, Core i9 проиграл в этом противостоянии, так как M2 имеет огромную энергоэффективность, компенсировав недостаток сырой скорости. И это кстати логично — в ноутбук вы вряд ли вставите флагманский i9, показывая правдоподобность формулы

Главное для понимания: эта формула дает оценочный показатель, на который можно полагаться при выборе архитектуры для конкретной задачи. Вы можете сравнить процессоры по-разному, меняя коэффициенты a, b и c и получая абсолютно разные результаты. Важно понимать, что сравнивать нужно только с едиными a, b, c, и это не абсолютные значения скорости/съеденных ватт и т.д, а оценка пользы процессора именно в заданной вами задаче, что гораздо важнее чистых флопсов и герц.

Кто хочет поиграть с формулой, вот быстрый copy-paste скрипт на питоне:

# Мини-скрипт для тестирования в консоли
# Ctrl+C для выхода, если что ;)

models = {}


def eff(perf, cores, power, area, a, b, c):
  return round((perf * cores**a) / (power**b * area**c) / 1_000_000, 2)


a, b, c = map(float, input("Введите a, b и c через пробел: ").split(' '))


while True:
    name = input("\nИмя модели: ")
    
    if name in models.keys():
        print("Прошлый результат:", models[name]["score"])
        continue
    
    models[name] = {}
    models[name]["ipc"] = float(input("Средний ipc: "))
    models[name]["hz"] = float(input("Средняя частота (ГГц): "))
    models[name]["cores"] = float(input("Кол.во логических ядер: "))
    models[name]["power"] = float(input("Общее потребление (Ватт): "))
    models[name]["area"] = float(input("Общая площадь чипа (мм²): "))

    models[name]["score"] = eff(models[name]["ipc"] * models[name]["hz"] * 10e9, models[name]["cores"], models[name]["power"], models[name]["area"], a,b,c)
    print("Результат:", models[name]["score"])

5. Возможные улучшения формулы

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

Тем не менее, в таком виде она уже невероятно полезно. Дальше можно ее просто немного нормализовать под все возможные кейсы. Вот как еще можно улучшить формулу:

Добавить пороговые значения

Внимательные читатели могли бы увидеть, что формула не учитывает пороговые значения. К примеру, уменьшение потребляемой мощности на десктопе после 50 Вт перестает быть значимой, так как все равно надобность в кулере становится минимальной. Тоже самое с площадью — формула может "поощрять" чрезмерно маленькие чипы, даже в ущерб эффективности. Это не критично, если вы не сравниваете всерьёз in-order RISCV-чип с 4 ядрами и какого нибудь монструозного Ryzen 9. Но можно попробовать закрыть этот недостаток через пороговые параметры и функцию max:

Eff = (Perf * LogicCores^a) / (max(TotalPower, P)^b * max(TotalArea, A)^c) / 10e6

Где P - наиболее оптимальная мощность; A - наиболее оптимальная площадь

Уменьшить значение SMT

SMT (Hyper-Threading) даеь нелинейный рост многопоточной производительности — P-ядро с SMT лишь примерно на 30% эффективнее ядра без SMT в многопоточных задачах. Оригинальная формула переоценивает её значение, хотя я должен сказать, что это практически не сказывается на результат при a < 0,9. Но для честности можно немного изменить расчет логических ядер:

LogicCores = Pcores * 1,3 + Ecores

В заключение

Надеюсь, статья вам понравилась. Я постарался затронуть все аспекты измерения скорости и насколько это на самом деле сложно — измерять эффективность процессоров. Даже 16 простейших RISC‑ядер могут быть лучше топового Ryzen в зависимости от задачи, бюджета и условий.