GPU тактыгынын тузагы: Apple Metal үстүндөгү fp32 бэктест кантип үнсүз таштанды кайтарат
"Иллюзиясыз бэктесттер" сериясынын бир бөлүгү.
Биз параметрлерди тескей чогулткан бэктестти GPU'га которгонубузда, ал 2 796× тезирээк болуп калды. Ал кайтарган сандар ынанымдуу эле. Ошондой эле алар, биринчи иштеген версияда, толук таштанды болчу — эки жүз эсе жаңылыш — жана эч нерсе кыйрабады, эч нерсе эскертпеди, эч нерсе жаңылыш көрүнбөдү.
Бул тузак Apple Silicon үстүндө GPU бэктестин CPU коддун жасай албаган түрдө коркунучтуу кылат: Metal'де float64 жок. Apple GPU'сунда бэктестиңиз тийген ар бир сан 32 биттик float, сиз аны сурасаңыз да, сурабасаңыз да. Ал эми жылма орточону векторлоштуруунун эң азгырган жолу — ар бир аткарууну ойлогон квант жетишкен O(n) prefix-sum ыкмасы — баа масштабында fp32 туруштук бере албаган так ушул формулировка. Ал ката бербейт. Ал толук ылдамдыкта иштеп, сизге 211 эсе жаңылыш жылма орточонун үстүнө курулган ынанымдуу көрүнгөн капитал ийри сызыгын берет.
Чечим — эң кызыктуу бөлүгү, анткени бул "көбүрөөк бит колдон" (сиз колдоно албайсыз) эмес жана бул "кылдаттыраак бол" (акылсыз код мурунтан эле кылдат) эмес. Чечим — ошол эле математикалык чоңдукту башка суммалоо аркылуу эсептөө — түз терезелик convolution — ал ар бир аралык маанини fp32 так туюнтарлык кичине кармайт. Ошол эле WMA, жети олуттуу орунга чейин ошол эле натыйжа, жана CPU'дагы бир агымдуу numba'дан 55.9× тезирээк. Бул макала — өлүктү жарып текшерүү: Metal сизге fp32'ни эмне үчүн таңуулайт, ачык формулировка так кайсы жерде толуп кетет, туурасы эмне үчүн толуп кетпейт жана биз кантип — ийри сызыктарды көзгө чабуу менен эмес, соодаларды санап — тез версия менен чынчыл fp64 версиясы дал келерин далилдедик.
Бул жердеги бардык сандар Apple M2 Max үстүндө, 150 000 бар × 80 параметр комбинациясы, 3'төн эң жакшысы, репонун scripts/bench_param_sweep.py (M5 GPU методу, коммит 97eadaf), scripts/engine_multitf_gpu.py (04d71e8) файлдарынан жана scripts/GPU_NOTES.md дизайн эскертүүлөрүнөн ченелген.
Metal'де float64 жок

CPU'да Python/numpy бэктест демейки боюнча кош тактыкта иштейт. float64 сизге 52 биттик мантисса берет: бүтүн сандар 2⁵³ ≈ 9×10¹⁵ чейин так туюнтулат, ал эми салыштырма тактык болжол менен 1.1×10⁻¹⁶. Сиз бул тууралуу дээрлик эч качан ойлонбойсуз, анткени баа масштабында — 30 000 тегерегиндеги BTC жабылуусу, 10¹⁴ жеткен жыйынды сумма — fp64'тө запастан ашык жайлык бар.
Apple'дын GPU'су ошол жайлыкты сунуштабайт, анткени ал float64'ти такыр сунуштабайт. Metal Shading Language'де double түрү жок; MLX, PyTorch-MPS жана Apple Silicon'го багытталган башка ар бир фреймворк муну мурас катары алат. Аны күйгүзгөнгө желекче жок жана жай-бирок-туура камбагалдамасы жок. Эсептөөңүз GPU'га тийсе, ал fp32'де болот:
- Мантисса: 23 бит (жашырын алдыңкы бит менен 24). Бүтүн сандар 2²⁴ = 16 777 216 ≈ 1.6×10⁷ чейин гана так.
- Салыштырма тактык: ~1.2×10⁻⁷. Болжол менен жети олуттуу ондук сан, андан ашык эмес.
Ошол 1.6×10⁷ шыптын бардыгы — бүт окуя. Ал жоомарт угулат — он алты миллион — сиз бэктест ошондон алда канча чоң аралык чоңдуктарды дайыма курарын байкаганга чейин, жана аралык чоңдук 1.6×10⁷'дан ашкан замат, fp32 сизге керектүү бөлчөк түзүлүштү айтпаганда да, удаа бүтүн сандарды да туюнта албай калат. Тактык жумшак начарлабайт; ал жардан кулайт, жана жар сиздин маалыматыңыз экинчи жолу ойлонбой өтүп кеткен санда.
Тузак — fp32 дээрлик дайыма жакшы. Бэктесттин көбү — баалар, кайтарымдар, PnL, Sharpe — жети сан жетиштүү болгон диапазондо ыңгайлуу жашайт. Ошондуктан акылсыз которуу иштейт, түтүн сынагынан өтөт, акылга сыярлык көрүнгөн натыйжа чыгарат. Каталык так бир операцияга — аралык чоңдук 10⁷'дан ашкан жалгыз жерге — локалдашкан, жана ошол операция баары эң алгач векторлоштурган операция.
Азгырган формулировка: WMA бир prefix-sum өткөрүмүндө
Биздин стратегия Hull жылма орточолоруна таянат. HMA — бирге куралган үч салмактуу жылма орточо; HMA3 варианты — төрт. Узундугу p терезенин үстүндөгү сызыктуу ядро менен салмактуу жылма орточо мындай:
150k бардын үстүндө миңдеген параметр комбинациясын тескей чогултканда, WMA convolution'дору дал негизги чыгым. Ошондуктан инстинкт — CPU'да туура инстинкт — ар бир WMA'ны prefix-sum менен O(n·p) эмес O(n) кылуу. Сиз бир жолу эки жыйынды сумманы алдын ала эсептейсиз,
андан кийин каалаган терезенин сызыктуу-салмактуу суммасы S1 жана S2'нин бир нече айырмачылыгы менен индекс жылышына кыскарат. Терезе боюнча цикл жок, терезе боюнча кыскартуу жок — эки cumsum өткөрүмү, ошону менен бүт WMA матрицасы массив арифметикасынан чыгат. Ал сонун векторлоштурулат, ал GPU'нун параллелдик-сканирлөө примитивине жеткиликтүү туура келет, жана fp64'тө ал так туура.
Бул ошондой эле fp32'де жасай ала турган эң жаман нерсе, жана себеби S2'нин ичинде жашырынып жатат.
S2 = mx.cumsum(j * price) # j is the global bar index: 0, 1, 2, ... , n-1
j · price мүчөсү — көйгөй. j 150 000'ге чейин баруусу жана price 30 000 тегерегинде болушу менен, акыркы мүчө өзү 4.5×10⁹, ал эми S2 — ушундай 150 000 мүчөнүн жүрүп жаткан суммасы. Ал баа масштабында калбайт. Ал fp32 санай албай калган аймакка көтөрүлөт.
Ал кайсы жерде толуп кетет: тузактын арифметикасы

Чоңдуктардын иреттерин катар коеюк, анткени бүт каталык дал ошол жерде жашайт.
S2 = cumsum(j · price) болжол менен price · n²/2 ≈ 30 000 · (150 000)²/2 ≈ 3×10¹⁴ жетет. Аны ~10¹⁴ дейли. Эми fp32'нин так-бүтүн шыбын эске түшүрөлү: ~1.6×10⁷. Жүрүп жаткан сумма fp32 так туюнта ала турган акыркы бүтүн санды жети иреттик чоңдукка ашырат.
Бул конкреттүү эмнени билдирет? 10¹⁴ тегерегинде, эки туюнтула турган fp32 сандын ортосундагы боштук — акыркы орундагы бир бирдик, ULP — болжол менен 2²³ ≈ 8×10⁶. Ошентип, S2 бир жолу 10¹⁴ диапазонуна көтөрүлгөндөн кийин, ал ±8 миллион чегинде гана белгилүү. Ал сактаган ар бир маани ~8×10⁶'нын эң жакын эселигине чейин тегеректелген.
Эми WMA калыбына келтирүү аны эмне кыларын карагыла. Жалгыз терезенин салмактуу суммасын чыгаруу үчүн, сиз эки коңшу S2 маанисин (плюс S1 оңдоолору) алып саласыз. Ошол эки S2 мааниси ар бири ~10¹⁴, ар бири ±8×10⁶ тегеректөө ызылдагын алып жүрөт. Алардын чыныгы айырмасы — сиз чындап каалаган терезелик чоңдук — нормалдаштыруудан кийин, баанын өзүнүн ирети, ~3×10⁴ болгон WMA'га туура келет. Демек арифметика мындай:
Бул эң таза түрүндөгү катастрофалык кыскаруу: ар бир операнддын тегеректөө катасы (±8×10⁶) сиз калыбына келтиргиси келген жооптон чоңураак. Сигнал ал чыгарылган сандардын ызылдоо тактыгынан кичине. Бул сиз бир нече сан жоготконуңуз эмес — сиз алардын баарын жоготосуз, жана кайтып келгени cumsum'дун топтолгон тегеректөөсү менен үстөмдүк кылынган.
GPU_NOTES.md'тен ченелген натыйжа: баа ~30 000'де 150k бардын үстүндө ушундай эсептелген WMA үчүн, fp64'кө каршы максималдуу салыштырма ката ~211 жетет. 211 пайыз эмес — 211×. Эсептелген жылма орточо чыныгысынан эки иреттик чоңдук алыс болушу мүмкүн. Жана бул жерде муну катадан эмес, тузактан кылган бөлүк: ал аягына чейин иштеп, чектелген, ынанымдуу сандарды кайтарат. Чексиздикке толуп кетүү жок, NaN жок, кыжырдануу жок. 211× жаңылыш жылма орточо баары бир жылма орточодой көрүнөт — ал жумшак, ал чектелген, кыскаруу жеңил болуп калган барларда ал болжол менен туура аймакта — ошондуктан ал ишенимдүү шилтемеге түз салыштыруу болбогон ар бир акыл-эс сынагынан өтүп кетет. Сиз толук бэктест, толук капитал ийри сызыгы, "оптималдуу" параметрлердин толук топтомун аласыз, баары ойдон чыгарылган индикатордун үстүнө курулган.
Чечим — көбүрөөк тактык эмес — башка сумма

Катаны көргөндөн кийин рефлекс — көбүрөөк тактыкка умтулуу — fp64'тө топтоо, же компенсацияланган (Kahan) суммалоону колдонуу. Metal'де биринчиси жөн эле жеткиликсиз. Бирок сизге экөө тең керек эмес, анткени көйгөй эч качан биттердин саны болбогон. Көйгөй — формулировка. Prefix-sum ыкмасы 10¹⁴ масштабдагы аралыктарды өндүрүп, кийин аларды кайра алып салат; ал жараткан чоңдуктар — алгоритмдин артефакти, жооптуку эмес. Аларды эч качан жаратпаган формулировканы тандаңыз, ошондо fp32 жакшы.
Ошол формулировка — аныктаманын өзү: түз терезелик convolution. Эки глобалдык жыйынды сумманын ордуна, узундугу p сызыктуу ядрону катар боюнча сырганытып, аны ордунда суммалаңыз. Ар бир чыгаруу эң көп дегенде p ≈ 200 мүчөнүн суммасы, жана ар бир мүчө weight × price, мында салмактар 1'ге кошула турган болуп нормалдаштырылган — ошентип ар бир мүчө price / p иретинде, ар бир жарым-жартылай сумма баа масштабынын тегерегинде (~3×10⁴) калат, жана эч бир аралык эч качан fp32 шыбына алты иреттик чоңдукка да жакындабайт. Кыскарта турган эч нерсе жок, анткени эч нерсе эч качан көбөйгөн эмес.
MLX'те бул бир примитив — mx.conv1d — ал так GPU'лар тез жасоо үчүн курулган нерсе:
def _mx_wma_valid(x, period):
w = mx.arange(1, period + 1, dtype=mx.float32) / (period * (period + 1) / 2.0)
return mx.conv1d(x.reshape(1, -1, 1), w.reshape(1, period, 1), padding=0).reshape(-1)
Ошол эле WMA, prefix-sum версиясына жана CPU'нун fp64 vec_wma/nb_wma'сына математикалык жактан бирдей. Бирок эми ченелген fp64'кө каршы максималдуу салыштырма ката 8.2×10⁻⁷ — так fp32 ызылдоо тактыгы ~1.2×10⁻⁷'де, макулдашуунун жети олуттуу орду. Кагазда жайыраак көрүнгөн формулировка (O(n) эмес O(n·p)) — туура болгон жалгызы, жана — ал GPU барлар менен терезелерди бир убакта параллелдештирген тыгыз convolution болгондуктан — ал ошондой эле күймөлдөй тез. Биз 211 салыштырма катадан 8×10⁻⁷'ге кантип суммалаарыбызды өзгөртүү менен өттүк, канча битте суммалаарыбызды эмес.
Мунун ушундай жол менен жасалуусунан эки практикалык эскертүү чыгат. Биринчиден, MLX numpy жасагандай conv1d аркылуу NaN'дарды таратпайт, ошондуктан жылытуу аймагы (биринчи p−1 бар, мында терезелик орточо аныкталбаган) GPU'да NaN менен белгиленбейт. Ага бул керек эмес: ар бир катардын жарактуу башталышы аналитикалык жактан белгилүү, жараксыз алдыңкылар эч качан окулбаган нөлдөр менен толтурулат, ал эми NaN толтуруу кийин CPU'да калыбына келтирилет — векторлоштурулган жана numba версияларынын бит-биттик ошол эле жарактуулук маскасы. Экинчиден, бүт тескей чогултуу бир cand_close катарын бөлүшөт жана комбинациялар аркылуу терезелерди көп кайра колдонот, ошондуктан көп чыгаруу каналы менен бир партиялык conv1d тескей чогултууга керектүү ар бир уникалдуу WMA'ны бир GPU чакырыгында эсептеп, бир mx.eval() менен материалдаштырат.
Сиз ага түшпөгөнүңүздү далилдөө: соода санынын паритети
Мына акыркы бөлүм жаратуучу ыңгайсыз суроо: эгер 211× жаңылыш WMA баары бир WMA'дай көрүнсө, 8×10⁻⁷ версиясы чынында эле туура, а жөн гана назигирээк жаңылыш эмес экенин кайдан билесиз? Сиз аны көзгө чаба албайсыз. Сизге агым-төмөндөгү, конвейердин дискреттик бөлүгү ачкан инварианты керек — жана бэктест сизге кемчиликсизин берет: соодалар.
Ладдердеги (M0–M4) башка GPU методдору толугу менен fp64'тө иштейт, ошондуктан биз аларды катуу эквиваленттүүлүк assert'ине карматабыз — бирдей соода саны, atol=1e-6'га чейин дал келген PnL. fp32 GPU методу (M5) муну курулушу боюнча өтө албайт, ал эми муну сыйдыруу үчүн assert'ти баары үчүн үнсүз бошотуу — так ушул серия каршы күрөшкөн жалгандыктын түрү. Ошондуктан M5 өзүнүн сандык паритет отчетун, report_equiv_fp32, алат, ал анын чыгарылган соодаларын fp64 шилтемесине салыштырат.
Калган каалаган макул болбоочулуктун механизмин так аташ баалуу, анткени ал катастрофалык кыскаруу эмес — ал сиз күткөн кадимки, кичинекей fp32 тегеректөө. Стратегия эки Hull орточонун кесилишинде, h каршы h3, атылат. Баа ~30 000'де индикаторго ~1×10⁻⁶ салыштырма ката — ~0.03 абсолюттук чайпалуу. Барлардын басымдуу көпчүлүгүндө эки ийри сызык андан алысыраак жана кесилиш ачык-айкын. Бирок чектеги барда — мында h − h3 өзү нөлдөн 0.03 чегинде — ошол чайпалуу салыштыруунун белгисин которуп, бир кесилишти бир барга жылдырып, бир соода кошуп же алып салышы мүмкүн.
Ушул себептен "айырмаланган комбинациялардын үлүшү" — баасыз ден соолук метрикасы, жана биздин биринчи паритет текшерүүбүз аны колдонуп өзүн уят кылды. 150k барда ар бир комбинацияда миңдеген кесилиш бар, ошондуктан жок дегенде бир чектеги бар дээрлик ар бир комбинацияда пайда болот — 80 комбинациянын 37'си "айырмаланды", бул үрөй учурат жана эч нерсени билдирбейт. Маанилүү метрика — канчалык:
- 80 комбинациянын баары боюнча PnL дельтасы: max |Δ| = 1.843 пайыздык пункт, max салыштырма = 1.25×10⁻²; кыйроо босогосу 5 п.п.
- Комбинация боюнча соода-санынын жылышы: миңдеген ичинен max |Δn| = 4 соода, max салыштырма = 2.5×10⁻³; кыйроо босогосу 1%.
- Жалпысында: 479 016 соодадан 90 жылдырылган соода — 0.019%.
Дээрлик жарым миллион ичинен токсон соода, алардын ар бири баа тиктен кичине тегеректөө чайпалуусу менен түртүлгөн чектеги кесилиш, эч бири кыйроо босогосуна жакын эмес. Бул — туура болгон fp32 методунун белгиси — кичине, чектелген, түшүндүрсө болгон макул болбоочулук — жана бул 211 салыштырма катадан такыр башка жаныбар. Босоголор "андай, бул жөн гана fp32" деп аталган бузук формулировканы кармаш үчүн бар; чыныгы дельталар алардан бир иреттик чоңдук төмөн келет. Соода саны — капитал ийри сызыгы боло бербеген оракул.
Пайда, жана GPU кайсы жерде жардам берүүнү токтотот
Туурасы аныкталгандан кийин, ылдамдыкты айтууга арзыйт — жана андан кийин чынчылдык менен эскертилиши керек, анткени GPU'нун артыкчылыгы конвейер боюнча бирдей эмес.
Изоляциядагы таза WMA convolution'дорунда — бүт метод тездетүү үчүн жашаган операция — fp32 conv1d партиясы бир агымдуу numba'дан 55.9× тезирээк иштейт, ошол 8.2×10⁻⁷ салыштырма катада. Бул таза, алма-алмага-салыштырма GPU-каршы-CPU саны: ошол эле математика, Metal GPU'га каршы компиляцияланган CPU коддун бир агымы.
Бирок тескей чогултуу convolution'дор гана эмес. HMA/HMA3 матрицалары GPU'да эсептелгенден кийин, соодалар дагы эле чыгарылышы керек — ар бир комбинациянын кесилиштери боюнча O(n) басуу — жана муну биз CPU'да fp64'тө жасайбыз, аларды GPU'да кайра ишке ашырбай, башка методдордун так соода семантикасын кайра колдонуп. Учтан-учуна timed() көрсөткүчү баарын камтыйт: ядро жылытуусу чыгарылган (numba'нын компиляциясын чыгаруу менен симметриялуу), бирок GPU→CPU которуу жана CPU соода чыгаруу камтылган. 150k бар × 80 комбинация, 3'төн эң жакшысы, M2 Max:
| Метод | Убакыт | Базалыкка каршы ылдамдануу | комб/сек |
|---|---|---|---|
| M0 pandas + Python loop* | 287.08s | 1.0× | 0.3 |
| M1 vectorized numpy | 3.14s | 91.5× | 25.5 |
| M2 numba (serial) | 2.02s | 142.3× | 39.7 |
| M3 multiprocess + vectorized | 0.50s | 570.2× | 158.9 |
| M4 multiprocess + numba (12 ядро) | 0.33s | 882.5× | 245.9 |
| M5 MLX GPU (fp32) | 0.10s | 2796.0× | 779.2 |
*M0 бир калыпта 5 комбинациялык үлгүдөн экстраполяцияланган.
Толук кыймылдаткыч M5 779 комб/сек жасайт — pandas базалыгынан 2 796×, бир агымдуу numba'дан 19.6×, жана numba иштеткен бүт 12 ядролуу CPU пулунан (M4) 3.2×. Бир GPU машинадагы ар бир CPU ядросун үч эсе жеңет.
Эми чынчыл эскертүү: учтан-учуна GPU артыкчылыгы (M2'ден 19.6×) convolution гана артыкчылыгынан (numba'дан 55.9×) кичине экенин байкаңыз. Ошол ажырым — Amdahl мыйзамынын убагында келиши. GPU convolution'дорду ушунчалык толук жок кылат, алар тыгырык болбой калат; калганы — GPU такыр тездетпеген O(n) CPU соода чыгаруу — эми M5'тин убакытына үстөмдүк кылат. Бул — ушул серияда ылдамдык-ладдери жана IPC-салыгы макалалары дайыма жеткен ошол эле сабак: бир чекиттен ары, жеңиш "тез бөлүктү тезирээк кылуу" эмес, оркестровка — маалымат кайда жашайт, кайсы этап азыр сериялык, түзмөк менен хосттун ортосунда жылдырууга эмне төлөп жатасыз. Соода чыгарууну ыңгайлаштырылган Metal ядросуна түртө турган гипотетикалык M6'ны кубалоо кичирейип бараткан CPU тилкесин гана кайтарат, ошондуктан биз аны курган жокпуз.
Жалпы сабак: үнсүз сандык таштанды — демейки
Hull орточолорунан жана MLX'тен артка кадам басыңыз, анткени тузак бул бир индикатордон алда канча ары жалпыланат.
GPU бэктестинин азгыруучу тартуусу — "бир чоң матрица": ар бир параметр комбинациясын тензорго тизип, бүт тескей чогултууну бир нече тыгыз массив операциясы катары иштетип, аппараттык каражатка жедиртиңиз. Ошол тартуу чыныгы — жогорудагы тездетүүлөр чыныгы. Бирок ал сиздин астыңыздагы сандык режимди үнсүз өзгөртөт, жана өзгөрүү коддо көрүнбөйт. CPU'да демейкилериңиз сизди коргоду: fp64, NaN таратуу, 10¹⁴'кө байкабай жете турган cumsum. Ошол эле массив туюнтмасын Metal'ге жылдырыңыз, сиз катуу 1.6×10⁷ бүтүн сан шыбы менен fp32'десиз, жана дал ошол код сабы — cumsum(j * price) — тактан таштандыга өтөт. Синтаксисте эч нерсе сизди эскертпейт. Компилятор ыраазы. Натыйжа чектелген жана ынанымдуу. fp32 катуу катуулук менен ката бербейт; ал сылык, сандар менен ката берет.
Чындап сизди коргогон үч адат арзан:
- Аралыктарыңыз кайда жашарын билиңиз, кире-чыгаларыңызды гана эмес. Кире (баалар ~10⁴) жана чыга (WMA ~10⁴) экөө тең fp32'нин так диапазонунун ичинде ыңгайлуу болчу. Кырсык толугу менен жашырын аралыкта болду (
S2~10¹⁴), аны ни API, ни түрлөр көрсөткөн жок. Кандайдыр бир fp32 кыскартууга ишенгенге чейин, эң чоң жарым-жартылай сумма кайда жетерин сураңыз — жана ал ~10⁷'дан ашса, формулировканы өзгөртүңүз. - Чоңдуктарды чектеп кармаган формулировкаларды артык көрүңүз. Prefix-сумманын үстүнөн түз convolution; глобалдык скандардын үстүнөн локалдык терезелер; суммалагандан кийин эмес мурун борборлоштуруу/айырмалоо. Чоң-кийин-кыскарт — антипаттерн. Туура алгоритм көбүнчө кагазда асимптотикалык жактан жаманыраак көрүнгөн, бирок эч качан кыскартуу керек болгон чоңдукту жаратпаган алгоритм.
- fp64 оракулуна дискреттик инвариант аркылуу текшериңиз. Ийри сызыктарды салыштырбаңыз; квантталган жана агым-төмөндөгү нерсени салыштырыңыз — соода саны, кесилиштердин саны, позиция-өзгөрүү окуялары. Дискреттик инвариант үнсүз 211× катаны кыйкырган ырастоо катасына айландырат, жана кабыл алынган 8×10⁻⁷ катаны кичине, чектелген, түшүндүрсө болгон дельтага айландырат. Бул алдыга караш каталыгы боюнча бир-бар жылыш сынагы сыяктуу эле тартип: көрүнбөгөн каталыкты көрүнгөнгө айландырган арзан диагностика.
Мунун эч бири экзотикалык сандык анализ эмес. Бул — тез бэктестке жай, ишенимдүү бирөө аны бекиткенге чейин ишенбөөнүн кадимки гигиенасы — тил тактыктын он эки иреттик чоңдукка үнсүз түшүп кеткенин эскертүүнү токтоткон жалгыз жерге кеңейтилген.
Негизги жыйынтыктар

- Apple'дын GPU'сунда float64 жок — бэктестиңиздеги ар бир GPU саны fp32. Бүтүн сандар ~1.6×10⁷'ге чейин гана так жана тактык ~1.2×10⁻⁷. Желекче жок, камбагалдама жок. Бэктесттин көбү мунудан аман калат; так бир операция адатта аман калбайт.
- prefix-sum WMA — тузак.
cumsum(j · price)~10¹⁴'кө көтөрүлөт, fp32'нин так шыбынан жети ирет ары, жана терезени калыбына келтирүү сизди тегеректөө катасы (±8×10⁶) жоопту эбак кичирейткен эки ушундай санды алып салганга мажбурлайт. Ченелген max салыштырма ката: 211×. Ал эч качан кыйрабайт — ал ынанымдуу таштандыны кайтарат. - Чечим — башка сумма, көбүрөөк бит эмес. Түз терезелик convolution (
mx.conv1d) ар бир жарым-жартылай сумманы баа масштабынын жанында кармайт, ошондуктан fp32 жети чынчыл сан кармайт: 8.2×10⁻⁷ салыштырма ката, жана бир агымдуу numba'дан 55.9× тезирээк. Сиз Metal'де fp64 сатып ала албайсыз, жана сизге керек да эмес. - Дискреттик инвариант менен текшериңиз, эч качан ийри сызык менен эмес. Соода-санынын паритети аны кармады: fp32 conv1d fp64 менен 479 016 соодадан 90'ында (0.019%) макул болбоду, баары чектеги кесилиштер, баары кыйроо босогосунан алда канча төмөн — туура методдун белгиси, 211× катадан айкын айырмаланган. "Айырмаланган комбинациялардын үлүшү" — алдамчы метрика; канчалыгын ченеңиз.
- Толук тескей чогултуу 779 комб/сек жасайт — pandas базалыгынан 2 796×, бүт 12 ядролуу CPU пулунан 3.2× — бирок учтан-учуна жеңиш (сериялык numba'дан 19.6×) convolution гана жеңишинен (55.9×) кичине, анткени CPU соода чыгаруу азыр тыгырык. Бир чекиттен ары, ылдамдык — оркестровка, арифметика эмес.
GPU которуу 2 796× тезирээк болду жана өзүнүн биринчи иштеген версиясында толук жаңылыш болчу — жана бул эки факттын бири-бирине эч тиешеси жок болчу. Ылдамдык чыныгы болчу. Таштанды — fp32 кармай албаган жана эч бир ката билдирүүсү эскербей турган жашырын 10¹⁴ аралыгы болчу. Эгер бэктест кескин тезирээк болуп, сандар дагы эле жакшы көрүнсө, бул ырастоо эмес. Metal'де "жакшы көрүнөт" — 211 салыштырма катанын так кандай көрүнгөнү.
Бул — ушул серия чыгып жаткан ладдердин GPU тепкичи: бэктест-кыймылдаткычтын ылдамдык ладдери, көп процесстин IPC салыгы, тешиктердин алдыга караш таксономиясы, жана "жакшы" деген эмне экенин чечкен максат-функция дизайны. Эгер туура эмес санды эсептөөдө тез болсо, ылдамдыктын баасы жок.
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.