Кера и работата по снижаването

A compiler story about the unglamorous work between a clean graph and a real machine: deterministic lowering, target ownership, and evidence that keeps one...

Кера и работата по снижаването

Докладът за грешка, който не беше грешка

Историята обикновено започва с число, което е почти точно. Не е съвсем грешно. Не е счупено по начин, който да оцвети таблото в червено. Почти точно по скъпия начин: вид грешка, която позволява на един екип да пусне продукта, на друг да се поколебае, а на одитор да попита защо отговорът се е променил, когато същият модел е преместен от една машина на друга.

Една компилация се изпълнява на x86 сървър и дава стойност. Следващата компилация се изпълнява на ARM лаптоп и дава стойност, достатъчно близка, че демонстрацията да проработи. GPU пътят е по-бърз, но закръгля по различен начин по време на изчисленията. FPGA пътят е привлекателен за времето, но изведнъж софтуерният екип води разговор за хардуер. Всеки може да обясни малка част от разликата. Никой не притежава целия път от източника до резултата.

Точно за това пространство е създаден Kera. Не за идеята за модела, не за маркетинговия слайд, не за статия за бъдещ компилатор, а за компилаторната работа, която започва, когато екип каже, че едно и също изчисление трябва да работи на различни машини и все пак да означава едно и също нещо. Това задължение се нуждае от собственик. Обещанието на компилатора не е, че понижаването е елегантно. Обещанието е, че понижаването се третира като работа.

Понижаването е частта от компилатора, в която доброто намерение на езика се превръща в инструкции, които дадена цел може да изпълни. Това е и мястото, където неясните твърдения стават видими. Ако резултатът трябва да бъде бит за бит идентичен в CPU, GPU, FPGA и WebAssembly, тогава компилаторът не може да свие рамене пред разликите в целите. Той трябва да носи достатъчно структура, за да реши какво може да се промени и какво не трябва да се променя. Той трябва да познава паметовите пространства, ефектите, опкодовете, движението на данни, ограниченията на целите и разписките, които доказват, че две компилации са едно и също изчисление.

Изкушаващата версия на историята е да наречем това слой за преносимост. Това е твърде малко. Преносимостта казва, че програмата работи и другаде. Kera се цели в по-трудното твърдение: програмата е представена като граф с адресируемо съдържание, компилирана в .keg артефакт, понижена до множество бекенди и все пак дава същия отговор. Кухнята се променя. Рецептата не се.

Средата, която никой не иска да продава

Първият разговор със сериозен купувач рядко започва със синтаксис. Той започва с бъркотия. Има модел, който има значение. Има симулация, която някога е била изследване, а сега е оперативна. Има изчисление на риск, чийто резултат вече не може да варира според машината. Има внедряване в периферията, което не може да носи пълната облачна среда за изпълнение. Има екип, който иска ускорител, но не може да си позволи пренаписване всеки път, когато ускорителят се промени.

Повечето инструменти представят това като проблем с внедряването. Изберете целта, експортирайте модела, коригирайте средата за изпълнение, приемете известна вариация, след което напишете документ, обясняващ изключенията. Документът расте. Тестовата матрица расте. Броят на специалистите расте. В крайна сметка организацията плаща за хетерогенността два пъти: веднъж, когато купува хардуера, и отново, когато се опитва да докаже, че хардуерът е свършил същата работа.

Kera започва от противоположния край. Страницата на Kera го нарича статично типизиран системен език с граф-ориентирано, съдържателно адресируемо IR. Тази фраза има значение, защото графът не е декоративна компилаторна диаграма. Той е изпълнимият обект. Изходният код се понижава до насочени ациклични графи на операции, съхранявани като .keg файлове. Всеки възел носи определена структура. Дублираната работа може да бъде елиминирана структурно. Същият граф може да бъде понижен до CPU, GPU, FPGA и WASM, без да се третира всяка цел като отделна малка вселена.

Ето защо Kera трябва да се държи като сериозна компилаторна инфраструктура, а не като диаграма. Неудобната среда е мястото, където живее клиентът. Токенизацията трябва да запазва байтовите позиции. Парсингът трябва да захранва инструментите, както и компилацията. Проверката на типовете трябва да отхвърля несъответствията във формата и ръбовете, преди те да се превърнат в събития по време на изпълнение. Проверката на заемането трябва да разбира собствеността върху хост, устройство, закрепена и обединена памет. Оптимизацията трябва да е възпроизводима. Сериализацията трябва да даде на организацията артефакт, който може да запази.

Нищо от това не прави драматично заглавие. То е много по-полезно от заглавие. Означава, че когато дадена работна натовареност премине от разработка към продукция или от базово ниво на процесора към път през графичния процесор, компилаторът има стабилно нещо за сравнение: отпечатъка на графа. Ако отпечатъкът съвпада, работата е същата работа. Това е обща контролна точка, която инженер, купувач и одитор могат да разберат.

Графът е разписка, не картина

Старият начин да се обясни една програма е да се покаже изходният код и да се помоли читателят да се довери на компилатора. Страницата на Kera продължава да се връща към различен обект: графа. Всяка операция е възел. Ръбовете изразяват зависимости. Графът има съдържание. Съдържанието има хеш. Хешът се превръща в отпечатък, който пътува през изграждането.

Графът е трайният обект: изходен код, типове, региони, хешове и артефактът, който пътува.

Това звучи като вътрешна механика, докато не застанете до екип по съответствие. Екипът по съответствие не иска да знае, че даден доставчик има хубава компилаторна архитектура. Той иска да знае дали изчислението, одобрено миналия месец, е изчислението, което се изпълнява днес. Отпечатъкът на графа е мостът между тези светове. Той не е екранна снимка на компилатора. Той е компактен начин да се каже: този набор от операции, тези типове, тези входове, тези атрибути, тази структура на зависимостите.

Моделът на възлите е умишлено строг. Един възел е версия на схема, опкод, дескриптори на изхода, входни хешове и карти с атрибути, подредени в каноничен ред. Байтовете се хешират със SHA3-256 под разделени по домейни префикси, така че хешовете на възли, региони и графи живеят в отделни пространства от имена. Ако два възела имат еднакъв опкод, входове и атрибути, те имат еднакъв хеш. Вмъкването на дубликат връща съществуващия запис. Елиминирането на общи подизрази става следствие от представянето, а не героичен етап, добавен по-късно.

Това е видът подробност, която превръща една инженерна система в нещо, на което хората могат да разчитат. Купувачът не купува диаграма на компилатор. Купувачът купува по-малко спорове за това дали дадено изчисление се е променило. Разработчикът не купува лозунг за детерминиран ИИ. Разработчикът купува графичен формат, в който еднаквостта може да се проверява, съхранява и използва от инструментите. Файлът .keg следователно не е мисъл след внедряването. Той е преносим запис на работата.

Осемте малки врати, преди да се появи целта

Когато хората си представят компилатор, често скачат направо към бекенда. Представят си момента, в който кодът става AVX, PTX, Verilog или WASM. Този момент е важен, но е късно в историята. По-голямата част от работата по компилатора вече е свършена дотогава, в тихи етапи, които решават дали на бекенда може да се има доверие.

Бекендите идват късно. Повечето доверие се печели по-рано, в малките етапи на компилатора, които запазват значението.

Kera започва с лексер, който разпознава UTF-8 и преобразува изходния код в токени с пълно проследяване на trivia и възстановяване от грешки. Това звучи като редакторска инфраструктура, и е така. Но е и надеждност на инструментариума. Ако всяка байтова позиция е запазена, диагностиката, форматирането, инкременталният повторен разбор и поведението на езиковия сървър могат да съвпаднат с това, което потребителят действително е написал. Компилатор, който губи формата на изходния код рано, плаща за тази загуба навсякъде другаде.

Парсерът е базиран на събития: рекурсивно спускане с разбор на изрази по Pratt излъчва събития Start, Token, Finish и Error, вместо да изгражда едно единствено AST, което всички трябва да споделят. Потокът от събития захранва независимо строителя на дървото, форматиращия инструмент и езиковия сървър. Зеленото дърво е без загуби, запазва токените и trivia. Строителят на графа след това обхожда това дърво, поддържа разрешаване на имена и създава възел Region за всяка функция.

Едва след това изходният код става графът, който компилаторът носи. Структурната проверка на типове валидира съвместимостта по ръбовете на операциите и ограниченията на формата на тензорите. Типовете съвпадат само когато каноничните им хешове са идентични. Проверката за заеми налага един собственик на стойност, без псевдоними на променливи референции, и правила за собственост през пространствата на паметта. Анотациите за ефекти се проверяват за съгласуваност. Страничните ефекти не са оставени на вкус или условност.

Мениджърът на оптимизационни пасове след това върши разпознаваемата компилаторска работа: константно свиване, елиминиране на общи подизрази, елиминиране на мъртъв код, инлайнване, векторизация и сливане на цикли в цикъл до фиксирана точка. Важната фраза не е списъкът от пасове. Важното е какво се случва след всеки пас: валидатор на инвариантите на графа проверява хеш-консистентността и целостта на зависимостите. На оптимизацията е позволено да подобрява графа, но не и да го прави загадъчен.

Накрая, генераторите на код за конкретни цели излъчват нативен код, PTX, Verilog или WASM, а оптимизираният граф се сериализира във файл .keg с таблица на секциите. Файлът започва с магическите байтове KEG\0 и носи секции за низове, типове, атрибути, възли, региони и експорти. Това е пътят, който един екип трябва да притежава, преди да може честно да говори за цели.

Целите не са стикери върху слайд

Списък с цели е лесен за написване и труден за заслужаване. CPU, GPU, FPGA и WASM се побират спретнато в продуктова карта. Понижаването до тях е мястото, където се крие сметката. Всяка цел има свои навици, силни страни и начини на отказ. Компилатор, който иска детерминирано хетерогенно изпълнение, не може да третира тези навици като чужд проблем.

CPU, GPU, FPGA и WASM не са етикети. Те са целеви светове, в които една и съща графа трябва да оцелее.

На CPU Kera генерира роден машинен код за x86-64 и ARM64, като RISC-V Vector е в техническия лист. Пътят за x86 избира SSE2, AVX2 или AVX-512; пътят за ARM използва NEON; разпределителят на регистри и планировчикът са споделени. Продуктовото твърдение не е просто, че съществува двоичен файл. Твърдението е, че се избират SIMD ядра, специфични за целевата платформа, докато графата остава същото изчисление.

На GPU изходният материал назовава генериране на PTX за CUDA и ROCm, като тензорните ядра се използват, когато формата на ядрото позволява. Тази последна клауза върши работа. Апаратното ускорение не е вълшебен прах. Формата на ядрото или се вписва в пътя на тензорните ядра, или не. Компилаторът трябва да разкрие това честно, да премества данни между хост и устройство, когато е необходимо, и все пак да запази отпечатъка на графата като доказателство за това, което се изпълнява.

На FPGA пътят на понижаването се превръща в разговор за хардуер: Verilog синтез, оценка на ресурсите, анализ на времето и планиране на конвейера за предвидимо, точно по цикли поведение. Това е видът целева платформа, където размахването с ръце е особено скъпо. Ако купувачът има нужда от твърдо реално време, повърхността на компилатора трябва да говори в цикли, ресурси и конвейери, не само в скорост. Историята на Kera прави FPGA бекенд на същата графа, вместо пренаписване от друг екип.

На WASM страницата назовава 128-битов SIMD за edge и браузърно разгръщане. Това има значение, защото edge е мястото, където реалността на разгръщането често се сблъсква с чистотата. Браузър, малко устройство или ограничена среда за изпълнение не винаги могат да приемат същия стек като сървър. Път на понижаване, роден за графата, дава на екипа начин да пренесе същото изчисление в тази среда, без да превръща edge във втори продукт.

Детерминизмът трябва да оцелее при успеха

Детерминизмът е лесно да се обещае в малка стая. Става по-трудно, когато продуктът успее. Пристигат повече потребители. Появява се повече хардуер. Модел се мести от един чип на друг. Тестова базова линия, написана за една среда, трябва да покрие друга. Оптимизация, която изглежда безвредна, променя реда на редукция. Бърз път се появява в една среда за изпълнение, но не и в друга.

Детерминизмът е цикъл, не лозунг: изграждане, изпълнение, сравнение, поправка и запазване на графата стабилна.

Страницата на Kera представя детерминизма като побитово еднакъв резултат на всички платформи. Това не е козметично твърдение. То променя задълженията към продукта. Примитивната аритметика има дефинирана точност на семантиката за всички цели. Редукциите имат дефиниран ред при равенство, така че планирането и хардуерът не променят резултата. Примитивите за невронни мрежи имат детерминистични реализации на CPU, GPU, FPGA и WASM. Графът означава едно и също нещо, където и да се изпълнява.

Ето защо недефинираното поведение, паузите на garbage collector-а и имплицитните странични ефекти не са дребни езикови предпочитания. Те са пукнатини в повърхността на изпълнението. Страницата на Kera казва: без зависимост от LLVM, без garbage collector, без недефинирано поведение. Изричните типове, изричните ефекти и собствеността върху паметта в различните пространства са контроли на продукта. Те намаляват броя на местата, където резултатът може да се промени, докато всички гледат другаде.

Има и човешка страна на това. Когато контролер за роботика промени поведението си на различни устройства, когато финансов процес закръгли един цент по различен начин, когато научен резултат не може да бъде повторен от рецензент или когато игрална симулация се разминава между целите, спорът всъщност не е за теорията на компилаторите. Става дума за институционално доверие. Потребителският текст на Kera използва прости истории, защото основният проблем се усеща лесно: едни и същи инструкции, едни и същи отговори, на всеки компютър, който притежавате.

Това доверие трябва да оцелее при ускоряването. Ако производителността изисква екипите да се откажат от детерминизма, продуктът само е преместил риска. Kera се опитва да запази производителността и детерминизма в един и същ договор, като прави графа единица за значение, а целевия път единица за изпълнение.

Сигурността е част от понижаването

Дискусиите за компилатори често изолират сигурността като работа по време на изпълнение. Изходният материал на Kera не го прави. Той говори за сигурност, базирана на способности, SecurityManager, порта със забрана по подразбиране, PolicyBuilder, seccomp BPF, Linux namespaces и защитен от промени одитен журнал с фиксиран капацитет в буфер с пръстеновидна структура. Тези думи принадлежат на инженерната история, защото понижаването не свършва, когато инструкциите бъдат издадени. Издадената работа все още трябва да се изпълнява с граници.

Токените за способности се предоставят при стартиране. Привилегированите действия изискват изрична способност. Правилата за достъп се декларират предварително. BPF филтър ограничава системните извиквания. Изолацията на namespaces разделя процесните, mount и мрежовите изгледи. Събитията, свързани със сигурността, попадат в одитен журнал. Краткото изречение е: нищо не е разрешено по подразбиране.

Това има значение за хетерогенното изпълнение, защото целите създават повърхности. CPU процес, GPU трансфер, FPGA път, браузърна среда и разпределена задача не се провалят по идентичен начин. Продуктът трябва да запази политиката прикрепена към работата, докато тя се движи. Ако графът казва какво е изчислението, контролите на runtime казват какво изчислението може да прави.

Има и операционна причина да се включи сигурността в историята за компилатора. Екипите не искат един продукт за компилация, друг за политика, трети за регистриране и четвърти за обяснение, ако шевовете между тях станат мястото, където инцидентите се крият. Страницата на Kera не твърди, че решава цялата сигурност. Тя прави нещо по-тясно и по-полезно: прави разрешенията изрични и одитирани в средата за изпълнение, която компилаторът захранва.

Разпределената работа също е понижаване

Техническият лист назовава разпределено изпълнение, ring allreduce и възстановяване от контролни точки. В друг продукт те може да са елементи за брошура. При Kera те принадлежат към същия разговор за понижаване, защото паралелизмът променя формата на изчислението. Паралелизмът на данни, модел и тръбопровод не са просто начини да се върви по-бързо. Те са начини за разделяне на работата, без да се губи значението на оригиналния граф.

Ако голяма задача се изпълнява на много машини и даден възел се повреди, възстановяването от контролна точка не е удобство. То е част от това изчислението да бъде оперативно. Ако резултатите се агрегират надеждно, семантиката на агрегирането трябва да бъде дефинирана. Ако графът е адресиран по съдържание, разпределеният път трябва да запази идентичността на графа, вместо да измисля втора реалност, след като задачата напусне една машина.

Тук продуктовата интерпретация става ясна. Kera не е синтактичен слой с разпределен добавъчен модул. Тя се опитва да направи изчислението преносимо както по форма, така и по хардуер: една програма, един граф, множество цели за изпълнение и запис, който може да бъде проверен. Разпределеното изпълнение е още едно място, където понижаването трябва да носи договора за изпълнение.

Това променя и историята за персонала. Без общ път за компилация, бъг, който се появява само на една платформа, може да изисква хора, които познават едновременно чипа, инструментариума и средата за изпълнение. С един граф и общ път въпросът е по-остър: промени ли се графът, промени ли се пътят за понижаване, или целта наруши дефинираната семантика? По-добрите въпроси не премахват тежката работа. Те спират тежката работа да се разпространява произволно.

Защо някой трябва да притежава пътя

Понижаването не може да остане без собственик, след като носи резултати, на които хората разчитат. Граф, който работи на CPU, GPU, FPGA и WASM, се нуждае от повече от умело представяне. Той се нуждае от документация, диагностика, твърдения за целите, поведение на редактора, генериране на код, контрол на сигурността, разговори с поддръжката и начин да се каже на купувача точно какво се е променило, когато резултатът се промени.

Това не прави работата по-малко техническа. То прави техническата работа по-обвързваща. Kera в момента е търговски продукт на Dweve, а не издание с отворен код: всички права запазени, достъпен по търговски лиценз, създаден в Нидерландия и предназначен за организации, които се нуждаят от поддържан път на графа, а не просто публикуван. Ако страницата казва Rust 2021, IR на графа, персонализиран JIT, без LLVM, CPU/GPU/FPGA/WASM, проследяване на ефекти, собственост върху пространствата на паметта, сигурност на възможностите, разпределено изпълнение, вграждане в C и Python, CLI работни потоци, поддръжка на редактор и LSP инструменти, това не са декоративни вътрешности. Те са задължения, които трябва да издържат оценка от реални работни натоварвания.

Листът с възможности е внимателен по този въпрос. Той описва проектните цели и казва да се провери спрямо вашето работно натоварване. Тази сдържаност има значение. Тя е много по-здравословна от това да се преструваме, че всяка бенчмарк цифра се пренася. Системата трябва да даде на екипите начин да измерват, проверяват и сравняват в собствената си среда, а не да ги кара да приемат универсална история за скоростта.

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

Денят, в който графът става договорът

Представете си отново оригиналния доклад за бъга, но с Kera вече в работния процес. Финансовият екип одобрява изчисление на риска. Пръстовият отпечатък на графа се записва. Първото внедряване работи на CPU. По-късно се въвежда GPU път за анализ на портфейла. Още по-късно се използва FPGA път за ценообразуване с по-ниска латентност. Въпросът на всяка стъпка не е дали новият хардуер звучи впечатляващо. Въпросът е дали същият граф се понижава и дали семантиката на целта запазва резултата идентичен.

Разговорът се променя. Екипът на платформата може да говори за планиране и разходи. Екипът на компилатора може да говори за бекенди. Екипът за съответствие може да говори за пръстовия отпечатък. Собственикът на бизнеса може да попита дали преместването на хардуер е оперативно решение или пренаписване. Продуктът дава на всички тях един обект, към който да сочат.

Това е тихата стойност на адресируемото по съдържание IR. То превръща изпълнението на различни платформи от поредица убедителни обяснения в запис. То прави графа разписка. То позволява един и същ .keg файл да захранва всеки backend. То дава на организацията базова линия, която принадлежи на изчислението, а не на една конкретна машина.

Никой компилатор не може да премахне нуждата от инженерна преценка. Екипите все още трябва внимателно да избират целите, да тестват работните натоварвания честно, да разбират ограниченията на целите и да решават кои области изискват пълна побитова идентичност. Но Kera може да направи тези решения изрични. То може да предотврати случайното превръщане на миграция на производителността в семантична миграция.

Урокът за lowering

Kera е важно, защото трудната част не е да имаш умна идея за lowering. Трудната част е да пренесеш тази идея през всички скучни места, където инженерството или става надеждно, или се превръща във фолклор: диагностика, зелени дървета, type hashes, borrow checking, оптимизация с фиксирана точка, проверка на инварианти, .keg сериализация, backend емисия, политика за сигурност, одитни регистри, поддръжка в редактора и изпълнение, специфично за целта.

Lowering е работа, защото всяка цел се опитва да направи източника по-малко универсален. Детерминираното компилаторно инженерство е дисциплината да не позволиш това да се случи мълчаливо. CPU иска вектори. GPU иска kernels. FPGA иска цикли. WASM иска ограничения. Организацията иска един отговор. Работата на Kera е да запази значението на изчислението, като позволи на всяка цел да прави това, в което е добра.

Това е инженерна форма, а не абстракция на хартия. Тя има купувач, режим на отказ, файлов формат, компилаторен път и оперативни последици. Обещанието не е, че хардуерът става прост. Обещанието е, че сложността е представена, lowered, проверена и притежавана.

Когато пристигне следващото почти правилно число, екипът не трябва да започва с фолклор за това коя машина какво е изпълнила. Той трябва да започне с графа. Съвпадна ли fingerprint-ът? Кой backend емитира кода? Кои възможности бяха предоставени? Коя семантика на целта беше приложена? Кой артефакт беше съхранен? Това са продуктови въпроси. Kera съществува, защото те са също и компилаторни въпроси.