📝

Draft article

This draft is visible to admins and superusers only. Sign in with an authorized account.

← К списку статей
July 7, 2026
5 мин. чтения

Когда GPU окупается: roofline перебора параметров, где заголовочные 167x — на самом деле 27x алгоритма, умноженные на 6.2x железа

Когда GPU окупается: roofline перебора параметров, где заголовочные 167x — на самом деле 27x алгоритма, умноженные на 6.2x железа
#алготрейдинг
#бэктест
#производительность
#gpu
#roofline
#оптимизация
Часть 9 из 10 · Подборка
Быстрые движки бэктеста

Статья из серии "Бэктесты без иллюзий".

Лесенка скорости закончилась на намеренно неудовлетворительной ноте. Мы довели перебор 80 комбинаций параметров с 69.9 секунды на pandas до 0.23 секунды на параллельном numba на CPU ноутбука — измеренные 298x — а затем заявили, что GPU — не то недостающее звено. Комментарии не приняли это молча, и правильно сделали. "Не то недостающее звено" — это утверждение про одну нагрузку одного размера. Это не закон природы. Так что эта статья поступает честно и ставит GPU на стенд.

Результат — не вердикт, а кривая. Отрыв GPU от CPU — не одно число, которое можно напечатать на слайде; это функция того, сколько работы вы отдаете ему за один вызов. На нашем мультитаймфреймовом предрасчете индикаторов ускорение GPU относительно CPU-движка идет от 54.5x, когда мы просим по одной комбинации параметров за раз, до 359.6x, когда просим 61 за один вызов. То же ядро, те же данные, то же железо — изменился только батч. Бенчмарк, который приводит одно из этих чисел и прячет второе, измеряет не GPU — он выбирает заголовок.

И даже 359.6x — не то, чем кажется. Разберите его на части, и крупная мультитаймфреймовая заголовочная цифра 167x раскладывается на 27x алгоритма — переписывание, которое ускоряет и сам CPU в те же 27x, — умноженные на 6.2x собственно железа. Эти 27x сделал не GPU. Их сделала математика. Эта статья о том, как отличать одно от другого, потому что именно их смешение — способ продать видеокарту за $2,000 для решения задачи, которую один git commit решил бы бесплатно.

Происхождение данных: все числа ниже измерены на Apple M2 Max, fp32 на Metal GPU через MLX против fp64 numba на двенадцати потоках CPU, нашими бенчмарк-скриптами engine_multitf_gpu.py и bench_param_sweep.py, каждый — под шлюзом эквивалентности, подтверждающим, что GPU- и CPU-пути дают одни и те же сделки. Публичной статьи-компаньона на этот раз нет — записью служат сами скрипты.

Вопрос — это roofline, а не бенчмарк

График roofline: плоский потолок вычислений справа, крутой склон пропускной способности памяти слева, и точка батча, взбирающаяся по склону из угла, где доминируют накладные расходы, к точке перегиба

Причина, по которой у вопроса "насколько GPU быстрее" нет единственного ответа, — это roofline-модель (Williams, Waterman & Patterson, 2009). Достижимая производительность процессора ограничена двумя потолками сразу: плоским, заданным пиковыми вычислениями (FLOP/s), и наклонным, заданным пропускной способностью памяти, умноженной на арифметическую интенсивность — число полезных операций на каждый перемещенный байт. Работа, дешевая в пересчете на байт, живет под наклонным потолком и упирается в память (bandwidth-bound); работа, богатая вычислениями на байт, достигает плоского потолка и упирается в вычисления (compute-bound). Плоский потолок GPU возвышается над потолком CPU, но его наклонный потолок и, что критично, его фиксированная стоимость на вызов масштабируются вниз далеко не так изящно.

Для перебора параметров левой частью графика правит третье слагаемое: накладные расходы на запуск и пересылку. Каждый вызов GPU платит примерно фиксированную цену — диспетчеризовать ядро, переместить входные данные через границу памяти (unified на Apple Silicon, но не бесплатную), переместить результаты обратно. Смоделируем wall time для батча из BB комбинаций как

Tgpu(B)=O+bB,Tcpu(B)=aBT_{\text{gpu}}(B) = O + b\,B, \qquad T_{\text{cpu}}(B) = a\,B

где OO — те самые фиксированные накладные расходы, bb — стоимость одной дополнительной комбинации на GPU, а aa — на CPU. Тогда ускорение равно

S(B)=aBO+bB  B  ab.S(B) = \frac{a\,B}{O + b\,B} \;\xrightarrow{B \to \infty}\; \frac{a}{b}.

Эта одна дробь объясняет всю статью. При малых BB слагаемое OO в знаменателе давит отношение — вы заплатили за то, чтобы разбудить GPU, и почти его не использовали. С ростом BB вы амортизируете OO на большем числе комбинаций, и ускорение поднимается к своей асимптоте a/ba/bистинному аппаратному отношению. Половина пути достигается при B=O/bB = O/b: это "точка перегиба" (ridge point) в пространстве батча, говорящая, насколько широким должен быть ваш перебор, чтобы GPU выдал хотя бы половину того, на что способен. Перебор из нескольких десятков комбинаций сидит далеко слева от этого перегиба. Перебор из тысяч сидит на плоской части, где GPU наконец зарабатывает свой потолок.

Так что правильный вопрос — никогда не "быстрее ли GPU". Он звучит так: "находится ли мой перебор справа от перегиба, и достаточно ли работа на комбинацию упирается в вычисления, чтобы достать до плоского потолка, когда туда доберется". Верными должны быть оба условия. Остаток статьи измеряет, где эти пороги проходят на самом деле.

Вердикт для одного таймфрейма: GPU едва выигрывает

Два столбца для одного и того же однотаймфреймового перебора из 80 комбинаций: пул на двенадцати ядрах CPU на 246 комбинациях в секунду рядом с GPU на 779 — скромный разрыв в 3.2x, а не пропасть

Начнем с нагрузки, которую использовала лесенка скорости: однотаймфреймовый перебор HMA/HMA3, 80 комбинаций по 150,000 барам. Мы добавили к той лесенке шестую ступень — M5, индикаторы на GPU Apple через MLX, сделки по-прежнему извлекаются на CPU. С прогревом, лучшее из трех, шлюз эквивалентности зеленый:

Метод Время vs pandas Комбинаций/с
M0 pandas + цикл 287.08s 1.0x 0.3
M2 numba (1 ядро) 2.02s 142x 39.7
M4 пул процессов + numba (12 ядер) 0.33s 883x 245.9
M5 MLX GPU (fp32) 0.10s 2796x 779.2

На фоне наивного последовательного бейзлайна GPU выглядит героически — 2,796x. Но это сравнение, которое ни один честный человек делать не должен: оно сталкивает хорошую GPU-реализацию с худшей реализацией на CPU. Поставьте GPU против того CPU, который вы бы реально развернули — то же самое ядро на всех двенадцати ядрах, M4, — и выигрыш схлопывается до трезвых 3.2x (779 против 246 комбинаций в секунду). Целая видеокарта, гоняющая весь перебор, обходит пул на двенадцати ядрах CPU в три раза.

Три и две десятых — не ничто. Но и не причина, по которой кто-либо покупает GPU. И это ровно то, что roofline предсказывает для настолько узкого перебора: 80 комбинаций — это слева от перегиба. Фиксированные накладные расходы на запуск и пересылку OO — все еще ощутимый кусок задачи на 0.10 секунды, так что до асимптоты a/ba/b мы не добираемся. Хуже того, часть стоимости на комбинацию — это O(n)-проход извлечения сделок, который мы намеренно оставили на CPU, — слагаемое, которое GPU не может ускорить вовсе (почему — в следующем разделе). Для исследовательского цикла на одном таймфрейме такого размера исходный вердикт лесенки скорости остается в силе: GPU — не то недостающее звено. Параллельный numba уже вывел вас на 0.23–0.33 секунды, и сбривание их до 0.10 — не то, что разблокирует исследователя. Разблокирует оркестрация вокруг перебора.

Интересное слово в этом вердикте — такого размера. Сдвиньтесь вправо по оси батча — и история меняется.

Где на самом деле живет стоимость

Прежде чем наращивать батч, посмотрим, за что мы вообще платим, потому что roofline вознаграждает только тогда, когда дорогая часть упирается в вычисления. Спрофилируйте перебор — и почти весь он окажется одной вещью: свертками взвешенных скользящих средних. HMA — это три WMA; HMA3 — четыре; каждая комбинация прогоняет их заново по всему ряду. Извлечение сделок — пройти по двум массивам индикаторов, найти перевороты знака hma - hma3, записать исполнения — это один дешевый O(n)-проход. Этот перебор — сверточная нагрузка в костюме торговой стратегии.

Это разбиение — в точности два режима roofline:

  • Свертки упираются в вычисления (compute-bound). Каждая цена читается во множество перекрывающихся оконных сумм, так что арифметическая интенсивность — операции на перемещенный байт — высока. Эта работа тянется к плоскому потолку вычислений, а у GPU именно он и возвышается. Более того, окна перекрываются и между комбинациями: WMA длины 40 полезна десяткам комбинаций, так что батчевый предрасчет разделяет ее один раз вместо пересчета на каждую комбинацию. Батчинг не просто амортизирует накладные расходы запуска; он повышает арифметическую интенсивность за счет переиспользования чтений. Это та часть, которой место на GPU.
  • Извлечение сделок упирается в память (bandwidth-bound) и ветвится. Один последовательный проход, зависящие от данных ветвления на каждом пересечении, по сути никакого переиспользования. Его арифметическая интенсивность у самого дна, а поток управления враждебен SIMD-устройству. Перенос на GPU дал бы мало и стоил бы дорого; оно остается на CPU. А значит, это последовательный хвост в духе закона Амдала — фиксированный пол, который ускорение GPU не пробьет никогда, и часть причины, по которой число для одного таймфрейма насытилось на 3.2x.

В мультитаймфреймовой версии этого ядра прячется второй, более острый урок — и это источник тех 27x, которые мы все обещаем объяснить. Мульти-TF движок выравнивает HMA старшего таймфрейма на базовый 1-минутный индекс без look-ahead. Записанное очевидным способом, это O(length^1.5) работы на каждый бар — пересчитывать скользящие средние старшего таймфрейма на каждом базовом баре. Но выровненная HMA линейна по короткому буферу из последних нескольких закрытых свечей старшего таймфрейма плюс текущего close, так что все вычисление на бар схлопывается в фиксированный вектор весов: один conv1d по ряду закрытых свечей и следом O(n)-сборка (gather). Сотни миллионов избыточных операций становятся сверткой по куда более короткому ряду.

Это схлопывание — алгоритмический выигрыш, а не аппаратный. Это просто лучшая формула. Она работает на GPU — и точно так же работает на CPU: np.correlate плюс gather, в fp64. Держите это крепко в голове: самый большой отдельный фактор в мульти-TF заголовке — это переписывание, доступное машине вообще без GPU. Когда мы наконец разложим 167x, это и будут те 27x.

Отрыв растет с размером батча

Растущая кривая ускорения GPU относительно CPU в зависимости от размера батча: от 54.5x на одной комбинации мимо 359.6x на шестидесяти одной, с единственным провалом на тридцати двух, помеченным как артефакт границы чанка

Теперь то измерение, которое велел сделать roofline. Берем дорогую ось — предрасчет выровненной HMA старшего таймфрейма на базовом 1-минутном ряде, самом длинном потоке свечей, какой у нас есть, — и скармливаем GPU растущее число комбинаций длин на вызов, B=1,2,4,,61B = 1, 2, 4, \dots, 61. CPU-бейзлайн здесь — честный продакшен-движок: numba с prange по всем двенадцати ядрам. Для каждого батча измеряем оба пути и берем отношение.

Батч BB (комбинаций/вызов) Ускорение GPU vs 12-ядерный CPU-движок
1 54.5x
2 102.5x
4 129.5x
8 187.4x
16 267.4x
32 245.0x
61 359.6x

Это S(B)=aB/(O+bB)S(B) = aB/(O + bB), нарисованная реальными измерениями. При B=1B = 1 GPU уже впереди на 54.5x — потому что сравнение идет против наивного движка с пересчетом на каждый бар, так что алгоритмическое схлопывание вшито даже на одной комбинации, — но до своего потолка ему далеко: в вызове с одной комбинацией все еще доминируют фиксированные накладные расходы. Удвойте батч — и ускорение почти удваивается до 102.5x; к B=16B = 16 это 267.4x; к B=61B = 61 — 359.6x, и кривая на глазах продолжает расти. Отрыв растет с размером задачи. Это самое важное предложение про GPU и переборы параметров — и полная противоположность тому, как обычно цитируют ускорения GPU: как будто это константное свойство чипа.

Две ремарки честности, потому что это серия "Бэктесты без иллюзий", а чистая монотонная таблица всегда заслуживает подозрения.

Во-первых, провал: B=32B = 32 дает 245.0x — ниже, чем 267.4x при B=16B = 16. Это не шум, который надо замести под ковер, — это артефакт границы чанка. Наш conv1d укладывает 32 длины в один чанк ядра, так что B=32B = 32 заполняет ровно один чанк без зазора, а B=61B = 61 переливается во второй чанк, который волей случая насыщает устройство лучше. Смысл roofline — в тренде, а у реального устройства внутри есть ступени квантования; мы приводим эту неровность, а не выбираем точки в обход нее.

Во-вторых, и это важнее: и 54.5x, и 359.6x измерены против наивного CPU-движка, и ни одно из них не является аппаратным выигрышем. Оба числа все еще содержат в себе алгоритмическое схлопывание в 27x. Замените CPU-бейзлайн на схлопнутый алгоритм, работающий на CPU — та же формула, fp64, обе стороны оптимизированы, — и каждая строка сожмется примерно во столько же раз. Что ровно и есть та декомпозиция, которую следующий раздел делает точной.

Честная декомпозиция: 27x алгоритма, умноженные на 6.2x железа

Один столбец 167x, расщепляющийся на два сомножителя: большой 27x с подписью "алгоритм", доступный и CPU, и меньший 6.2x с подписью "железо" — единственная часть, которую действительно вносит GPU

Чтобы отделить алгоритм от кремния, нужно измерить три пути на одной и той же мультитаймфреймовой сетке, а не два. Поэтому бенчмарк гоняет:

  1. cpu-engine — продакшен-движок на numba, выровненная HMA с пересчетом на каждом баре, на всех ядрах. Наивный, но параллельный бейзлайн.
  2. cpu-collapsed — схлопнутые векторы весов, np.correlate плюс gather, fp64, на CPU. То же железо, что в (1), лучший алгоритм.
  3. gpu-mlx — схлопнутые веса как батчевый conv1d на Metal GPU, fp32. Тот же алгоритм, что в (2), другое железо.

Выстройте их в ряд — и полная мультитаймфреймовая заголовочная цифра чисто факторизуется:

167×gpu vs naive CPU  =  27×cpu-collapsed vs cpu-engine  ×  6.2×gpu vs cpu-collapsed\underbrace{167\times}_{\text{gpu vs naive CPU}} \;=\; \underbrace{27\times}_{\text{cpu-collapsed vs cpu-engine}} \;\times\; \underbrace{6.2\times}_{\text{gpu vs cpu-collapsed}}

Левый сомножитель, 27x, — это алгоритм: схлопывание пересчета на каждый бар в свертку из предыдущего раздела. К GPU он не имеет никакого отношения. Реализуйте его на numpy — и CPU вашего ноутбука станет на этой нагрузке быстрее в 27x по цене одного рефакторинга. Правый сомножитель, 6.2x, — это железо: честный выигрыш Metal GPU в равных условиях над тем же оптимизированным алгоритмом на двенадцати ядрах CPU. Эти 6.2x — единственная часть, ради которой GPU действительно был нужен.

Это вся мораль статьи, записанная арифметикой. Когда вендорский бенчмарк, README библиотеки или воодушевленный коллега показывает вам "167x на GPU", рефлексом должен быть один вопрос: каким был CPU-бейзлайн? Если бейзлайном была наивная реализация — а так почти всегда и бывает, потому что медленный бейзлайн делает слайд красивее, — то большая часть заголовка — это алгоритмический выигрыш, на который CPU имел ровно такое же право, и только остаток — железо. Здесь остаток — 6.2x. Цитата "167x" завышает вклад железа примерно в 27 раз.

И заметьте, как сам аппаратный сомножитель сдвинулся вместе с размером задачи. На маленьком однотаймфреймовом переборе истинный выигрыш GPU против лучшего CPU был 3.2x. На более крупном мультитаймфреймовом предрасчете — 6.2x: те же два чипа, почти вдвое большее аппаратное преимущество, чисто потому, что большая нагрузка забирается выше по roofline к плоскому потолку вычислений GPU, прежде чем CPU успевает за ней. Аппаратный отрыв — тоже не константа. Это точка на той же растущей кривой, а способ двигаться по этой кривой вправо — делать батч больше, а работу на комбинацию богаче.

Руководство к решению: насколько широким должен быть перебор?

Простая схема принятия решения: маленький перебор или тонкая работа на комбинацию уходят на CPU, широкий перебор из тысяч вычислительно тяжелых комбинаций уходит на GPU, а на GPU-ветке стоит шлюз паритета fp32

Свернем roofline обратно в решение, которое можно принять до того, как тратить деньги. GPU окупается, когда оба условия roofline выполняются одновременно: ваш перебор находится справа от перегиба по батчу (BO/bB \gg O/b, так что фиксированные накладные расходы на запуск и пересылку амортизированы), и ваша работа на комбинацию упирается в вычисления (арифметическая интенсивность достаточно богата, чтобы достать до плоского потолка, а не тонкий O(n)-проход). Конкретно, из того, что мы измерили:

  • Несколько десятков комбинаций однотаймфреймовой стратегии: пропустите GPU. Вы слева от перегиба; честный выигрыш над параллельным numba — ~3.2x на задаче, которая и так занимает десятую долю секунды. Узкое место — не ядро, а все вокруг него.
  • Тысячи комбинаций или по-настоящему мультитаймфреймовый / мультииндикаторный предрасчет: GPU зарабатывает свое место. Накладные расходы амортизируются, разделяемые свертки повышают арифметическую интенсивность, и аппаратный выигрыш поднимается до 6.2x и продолжает расти с батчем. Это тот режим, где GPU превращает перебор длиной в ночь в кофейную паузу.
  • Сначала заберитесь по CPU-лесенке — это дешевле, и это идет первым. 298x на CPU и алгоритмическое схлопывание в 27x бесплатны или почти бесплатны, и это предпосылки, а не альтернативы: 6.2x у GPU идут поверх схлопнутого алгоритма, который вам все равно пришлось написать. GPU, прикрученный к наивному пайплайну, в основном измеряет наивность.

На GPU-ветке есть еще и налог, не имеющий никакого отношения к скорости, и его обязательно нужно заложить в цену: у Metal GPU от Apple вообще нет fp64. Все работает в fp32, относительная точность ~1.2e-7. Это убивает хрестоматийный трюк быстрых скользящих средних — O(n) WMA на префиксных суммах (prefix-sum), — потому что при ценовом масштабе около 30,000 на 150k барах бегущие суммы достигают ~1e14, на семь порядков дальше безопасного целочисленного диапазона fp32; мы намерили относительные ошибки до ~2e2 (в двести раз, а не два процента). Рабочая формулировка — прямая оконная свертка, где каждая оконная сумма — ограниченное число слагаемых сопоставимого размера, и fp32 сохраняет точность до ~8e-7. Даже так стратегия, принимающая решение по знаку hma - hma3, будет изредка переворачивать пересечение на пограничном баре, где две кривые почти касаются, потому что округление fp32 склоняет почти-ничью. Именно поэтому GPU-путь поставляется со шлюзом эквивалентности, который измеряет, насколько разошлись сделки — дельта PnL в базисных пунктах, относительный сдвиг числа сделок, — а не утверждает побитово идентичный результат, которого он дать не может никогда. На нашем прогоне это расхождение составило 90 сдвинутых исполнений из 479,016 (0.019%) — глубоко внутри допуска, но бремя реально: уход на GPU означает владение историей численного паритета, а не просто более быстрым тактом. Эта инженерная стоимость — тоже часть break-even.

Числа имеют форму Apple; кривая — нет

Каждая цифра выше — это Apple M2 Max: устройство с unified memory, где GPU и CPU делят один пул, и GPU только с fp32, без двойной точности. Дискретная карта NVIDIA или AMD меняет константы, и стоит явно проговорить, в какую сторону сдвигается каждая, потому что форма аргумента переживает смену чисел, а сами числа — нет.

  • Накладные расходы на пересылку OO становятся хуже, а не лучше. Дискретная карта сидит за PCIe, так что входные данные и результаты делают настоящую копию через шину, которой unified memory избегает. Это сдвигает перегиб по батчу O/bO/b вправо — нужен еще более широкий перебор, прежде чем дискретный GPU амортизирует свой запуск. Левый край roofline на PCIe-устройстве круче, а не мягче.
  • Плоский потолок a/ba/b становится выше. У GPU для дата-центров куда больше FLOP/s и пропускной способности, чем у интегрированного, так что асимптотический аппаратный выигрыш на насыщающем переборе больше наших 6.2x. Награда за то, чтобы добраться до правой части кривой, растет; плата за сидение слева растет тоже.
  • fp64 возвращается, а с ним и трюк с префиксными суммами. На карте с настоящей двойной точностью O(n) WMA на префиксных суммах снова жизнеспособна, а шлюз паритета можно затянуть в сторону побитовой точности. Конкретный fp32-налог, который заплатили мы, — прямая свертка вместо префиксных сумм, шлюз, измеряющий расхождение, вместо assert — это деталь Apple Silicon, а не закон.

Ничто из этого не меняет тезис. На любом устройстве S(B)=aB/(O+bB)S(B) = aB/(O + bB): фиксированные накладные расходы, которые нужно амортизировать, и асимптота, к которой приближаешься только справа. Константы — это железо; кривая — это арифметика. Измерьте свои собственные OO, aa и bb пятистрочным перебором по размеру батча, прежде чем верить чьему-либо заголовку — включая наш.

Как это связано с остальным

Это четвертое измерение в небольшой под-серии о том, откуда на самом деле берется скорость бэктеста, и части складываются в один аргумент о том, что оптимизировать прежде, чем тратить:

  • Лесенка скорости забралась от pandas до параллельного numba на 298x на одном только CPU и оставила GPU открытым вопросом. Эта статья на него отвечает: GPU — реальная, но условная пятая ступень, стоящая 3.2x–6.2x поверх верхней CPU-ступени, и только когда перебор достаточно широк, чтобы взобраться по roofline.
  • IPC-налог сделал тот же ход в другую сторону — измерил, во что обходится покинуть процесс, — и пришел к выводу той же формы: граница (сокет, запуск GPU-ядра) дешева; налог — в том, как часто и как болтливо вы ее пересекаете. Пакетируйте вызовы GPU по той же причине, по которой пакетируете IPC: чтобы амортизировать фиксированную стоимость каждого пересечения.
  • Агрегированный parquet-кэш — это CPU-версия того, что делает GPU-предрасчет: вычислить общие индикаторы один раз и переиспользовать их на каждой комбинации. GPU просто доводит этот принцип "переиспользуй и пакетируй" до кремния.
  • А шлюз паритета fp32 — это проблема паритета бэктест-live в миниатюре: в момент, когда ваш быстрый путь вычисляет что-то слегка иное, чем референс, вы должны количественный отчет о расхождении, а не махание руками.

Связующая дисциплина — та же, которую продвигает вся эта серия: измеряйте то, что вам на самом деле продают. Ускорение — это отношение, а у отношения есть числитель и знаменатель. Большая часть разочарований в GPU происходит от знаменателя — CPU-бейзлайна, — выбранного, чтобы польстить, а большая часть пустой траты GPU — от перебора, слишком маленького, чтобы покинуть левый край roofline.

Выводы

  1. Ускорение GPU — это кривая, а не число. На нашем мультитаймфреймовом предрасчете отрыв от CPU шел от 54.5x при одной комбинации на вызов до 359.6x при шестидесяти одной — тот же чип, те же данные. Любая отдельная цифра — точка на этой кривой; спрашивайте, при каком размере батча она измерена.
  2. Всегда допрашивайте CPU-бейзлайн. Мультитаймфреймовый заголовок 167x чисто раскладывается на 27x алгоритма (схлопывание пересчета на каждый бар в свертку, которое ровно так же ускоряет CPU), умноженные на 6.2x собственно железа. Справедливый выигрыш GPU против лучшего CPU здесь — 3.2x на одном таймфрейме, 6.2x на нескольких — не 167x.
  3. Отрыв растет с размером задачи, и аппаратный сомножитель тоже. Больший батч и более богатая работа на комбинацию толкают вас вверх по roofline: сам честный аппаратный выигрыш вырос с 3.2x до 6.2x чисто за счет укрупнения нагрузки. Маленькие переборы сидят слева от перегиба и почти не выигрывают.
  4. Сначала почините алгоритм и заберитесь по CPU-лесенке — выигрыш GPU сидит поверх них, а не вместо. 6.2x измерены против схлопнутого алгоритма, который вам все равно пришлось написать. Прикрутите GPU к наивному пайплайну — и большая часть того, что вы измерите, будет наивностью, а не кремнием.
  5. Уход на GPU означает владение историей численного паритета. На Metal нет fp64, трюк WMA на префиксных суммах умирает на ценовом масштабе (относительная ошибка ~2e2), а стратегии на знаке пересечения переворачиваются на пограничных барах. Поставляйте шлюз эквивалентности, который количественно оценивает расхождение в базисных пунктах; засчитайте эту инженерную стоимость в свой break-even.

Когда кто-то говорит вам, что GPU сделал его бэктест в сто раз быстрее, он не сказал вам почти ничего. Спросите про размер батча и CPU-бейзлайн — и сотня обычно распадается на аппаратный выигрыш в единицы раз, обернутый вокруг алгоритмического, который можно было получить бесплатно. Иметь его стоит — на достаточно большом переборе, ровно по тем причинам, которые называет roofline, и ни одной причиной больше.

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

Авторы

Eugen Soloviov
Eugen Soloviov

Инженер торговых систем

Разработка торговых ботов с 2017 года: межбиржевой арбитраж (подключал до 30 бирж), парный арбитраж на коинтеграции между спотом и фьючерсами, скальпинг, фронтраннинг, торговля по новостям, сентиментный анализ, трендовые алгоритмы, а также алгоритмы управления и балансировки портфелей. Делает выставление ордеров до 1 мс, warehouse для big data, бэктестинг-движки, AI-агентов и интерфейсы для ботов (в т.ч. open-source profitmaker.cc). Стек: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, архитектура.

Newsletter

Будьте в курсе событий

Подпишитесь на нашу рассылку, чтобы получать эксклюзивную аналитику по AI-трейдингу и обновления платформы.

Мы уважаем вашу конфиденциальность. Отписаться можно в любой момент.