Claude заменил 4,9 МБ таблиц эмулятора PSP на 10,5 КБ

Claude Opus 5.5 примерно за час понял, как Sony PSP считает синус, логарифм и ещё шесть функций, и ни разу не заглянул внутрь чипа: хватило выходов и полной проверки по всем 2^32 входам. Как рассказывает блог Ppsspp, 4,9 МБ таблиц эмулятора PPSSPP сменились 10,5 КБ коэффициентов, и результат совпадает с железом бит в бит.
Коротко
- Для эмулятора PPSSPP подготовлен pull request, который заменяет таблицы поправок fp64 точной реализацией восьми специальных функций VFPU, от обратной величины и корня до синуса, арксинуса и логарифма.
- Внутри нашлась учебная схема квадратичной интерполяции со 128 сегментами на функцию и общим квадратором, и каждая измеренная функция стала быстрее, например sin за 5,9 нс вместо 14,6.
- По одним выходам нельзя понять, делит ли эта схема сумматор со скалярным произведением vdot, как было на Dreamcast, а часть «невозможных» результатов по дороге оказалась багами поискового кода.
Если вы не следили: в VFPU, векторном сопроцессоре PSP, есть блок специальных функций, как в современных GPU, и ни одна из них не возвращает правильно округлённый по IEEE результат. Что именно они выдают, давно известно, а как считают, не знал никто. Участник проекта fp64 добился точного совпадения грубой силой: снял полные таблицы выходов с настоящей приставки и положил поверх простых приближений таблицы поправок.
Полная проверка гипотезы по всем 2^32 входам занимала 30 секунд
Толчком стала работа skmp, который с помощью трёх ИИ-моделей разобрал FPU процессора SH-4 в Sega Dreamcast. Автор блога PPSSPP решил просто поручить задачу Claude, и Opus 5.5 справился примерно за час. Дальше в записи идёт собственный отчёт Claude, слегка отредактированный.
Главным инструментом стал код fp64: он совпадает с железом на каждом входе и прогоняется на ноутбуке за секунду. Прежде чем на него опереться, новый тест pspautotests cpu/vfpu/exact проверил особые значения и перебор экспонент на настоящей PSP. Оракул совпал, а заодно всплыли расхождения в других CPU-бэкендах эмулятора.
Подгонка полиномом упёрлась в 93% точных ответов
Первая догадка, кусочный квадратичный полином, давала около 93% точных выходов при любом размере кусков и степени. Хенрик предположил, что функции собраны из операций vdot: на Dreamcast блок скалярного произведения FIPR переиспользовался для аппроксимации. Один vdot с коэффициентами-флоатами застрял на 92%.
Лучший вещественный полином стабильно промахивался примерно на одну единицу младшего разряда (ulp). Тогда Claude сменил вопрос: вместо «насколько близко можно подойти» он спрашивал, существуют ли коэффициенты, которые воспроизводят каждый выход точно. Каждый выход превращается в интервал допустимых значений, а пересекать интервалы дёшево.
На каждых 64 подряд идущих входах выход ложится ровно на прямую, а общий наклон делят 1 024 таких отрезка, то есть 2^16 входов, и никогда 2 048. Значит, старшие 7 бит мантиссы выбирают один из 128 сегментов. Остаток дал пилу высотой в один ulp: квадратичное слагаемое округляется вниз до целых ulp отдельно от линейного, и метод наименьших квадратов такое не видит.
Соседние сегменты rcp сошлись на числе 143,9636
Как именно округляется квадрат, долго понять не удавалось: перебор вариантов упирался в стену из 99,65% верных выходов. Часть тупиков оказалась багами самого Claude: слишком грубый шаг поиска, дважды вычтенная константа и особенность zsh, из-за которой перебор параметров молча ничего не проверял.
Выручило случайное совпадение. У сегментов rcp со 115-го по 120-й лучший квадратичный коэффициент совпал до четырёх знаков, 143,9636, а одинаковые десятичные означали одинаковые целые под ними. Коэффициенты оказались кратны 8/2^20, а целочисленные последовательности совпали между функциями: сегмент 32 у exp2 повторяет сегмент 116 у rcp.
Выходит, квадратор один на все функции. По 109 значениям коэффициента n Claude восстановил его выход напрямую: t² округляется вверх до кратного 256. После этого подошли все 512 сегментов rcp, rsqrt, sqrt и exp2, и код с первого запуска совпал с кодом fp64 на всех 2^32 входах, если не считать одного бага в обёртке.
sin считал с другого конца, а log2 урезал коэффициенты
sin не подходил ни при каком размере сегмента. Подсказкой стали места, где выход шагает не в ту сторону: у rcp они между входами 64k + 63 и 64k + 64, у sin на один вход позже. Железо индексирует четверть волны с другого конца, через y = 2^23 − x, то есть считает синус как косинус дополнительного угла.
Каждый сегмент sin работает в ulp своего первого, самого большого выхода, даже если результат сползает в порядок ниже. asin подтвердил правило с обратной стороны: у него 1,25% выходов поднимаются в следующий порядок и сохраняют 23 значащих бита вместо 22.
Тяжелее всех дался log2. На входах из [1, 4) он подошёл сразу, а выше и ниже нет. Оказалось, тракт урезает коэффициенты под точность выхода. Для отрицательных экспонент шаг равен 2^-15, квадратичное слагаемое исчезает целиком, поэтому этот путь выглядел как простая линейная интерполяция.
sin стал считаться за 5,9 нс вместо 14,6
Таблицы на 4,9 МБ, которые грузились из assets/vfpu, сменились 10,5 КБ статических коэффициентов. Ускорилась каждая функция из таблицы замеров: rcp с 4,2 до 3,6 нс, rsqrt с 5,4 до 3,6, exp2 с 6,3 до 3,9, log2 с 7,1 до 5,1, asin с 6,0 до 3,6, sin с 14,6 до 5,9.
vrot, которому нужны синус и косинус сразу, стал примерно на 10% быстрее за счёт общего приведения аргумента. Из-за прироста скорости в PPSSPP собираются включить точную эмуляцию этих функций по умолчанию, а будущие версии станут легче на несколько мегабайт.
В PSP стоит квадратичный интерполятор с ПЗУ на 128 записей для каждой функции
Старшие 7 бит мантиссы выбирают строку в маленькой таблице с тремя числами: константой c0 в целых ulp, наклоном m примерно на 18 бит и коэффициентом n при квадрате на 7–8 бит. Остальные биты задают смещение внутри сегмента, причём квадратор видит только старшие 10 его бит. Слагаемые складываются, и сумма обрезается до шага выхода.
Примерно так пользовались бумажными таблицами Брадиса: находите ближайшую строку, а промежуточное значение досчитываете по поправкам. Разница в том, что поправку на кривизну здесь округляют отдельно, и как раз она давала пилу, которую не видела подгонка. По словам Claude, схема взята из литературы: минимаксный интерполятор Пиньейро, Обермана, Мюллера и Бругеры или многофункциональный интерполятор NVIDIA (Оберман и Сиу, 2005) почти для того же набора функций.
Отчёт сам признаёт главное ограничение: по выходам нельзя сказать, общий ли у схемы финальный сумматор с vdot, как на Dreamcast; отдельно усечённые произведения с этим лишь совместимы. Вся работа к тому же стоит на оракуле fp64, без которого часового разбора бы не было. На наш взгляд, ценнее всего в отчёте список собственных багов Claude: несколько «невозможных» результатов оказались ошибками поиска, и без полной проверки они легко сошли бы за выводы.
Когда таблицы уйдут из сборки
Экономию нескольких мегабайт обещают «в будущих версиях» PPSSPP, но ни номер версии, ни сроки в записи не называются. Открытым остаётся и вопрос про сумматор: выходы на него не ответят, для этого нужен другой способ заглянуть в чип.
Читайте также
Комментарии
Пока никто не написал. Будьте первым.
Присоединяйтесь к разговору
Войдите через Google, чтобы оставить комментарий. Имя и аватар подставятся из вашего профиля Google, а комментарий появится после модерации.
