Вывод типов отдаёт доказанное отметкой на дереве, а не таблицей наружу
Обещанные две правки скорости сделаны, и обе оправдались: 1,77 раза геометрическим средним, до 4,50 на арифметике. Мы стали быстрее Python в 1,28 раза там, где были медленнее в 1,37.
Главное здесь — не число, а как оказалось правильно достать типы узлов. Вопрос стоял так: проверка типов знает тип каждого выражения, но выбрасывает его, а генератору кода он нужен, чтобы не печатать проверку, ответ которой известен заранее. Напрашивалось «пусть checkTypes вернёт таблицу узел → тип, а генератор кода в неё смотрит». Это неверный ход, и вот почему.
Генераторов кода восемь, и один из них написан на самом flang. Копия генератора C на flang (flang/self/emit-c.flang) проверку типов не видит вовсе — круг импортов. Значит таблица наружу означала бы либо второй вывод типов внутри копии, либо расхождение двух генераторов ровно там, где они обязаны совпадать побайтово.
Правильный ход уже стоял в дереве, оставалось им воспользоваться. В компиляторе есть проход отметок: анализ кладёт на узлы дерева поля («здесь доказана непустота», «здесь нужна проверка убывания меры»), а генератор кода их только читает и ничего не доказывает. Третья отметка легла туда же: поле числовая на узле двуместной операции, у которой доказан тип обоих операндов. Правка вышла примерно в 100 строк кода в свидетеле при оценке 150–250 — именно потому, что проход отметок уже был. Копия генератора на самом flang стоила ещё около 160 строк, и это та половина цены, которую оценка не учитывала вовсе.
Правило, которое из этого следует: знание анализа доезжает до генератора кода отметкой на дереве, а не отдельной таблицей. Таблица не переживает границу языков; отметка переживает, потому что едет вместе с самим деревом.
Чем подтверждено. Ветка work/skorost, коммиты c903983 (флаг сборки) и 2854d2b (отметка типа). Стенд benchmarks/speed, 11 кругов, медианы, сборки чередуются внутри круга; машина в этот раз была тихая, разброс 1–3 % вместо обычных 30 %.
| задача | было | после флага | после обеих | раз |
|---|---|---|---|---|
| коллатц | 596,8 | 396,2 | 132,5 | 4,50 |
| нод | 149,6 | 153,4 | 78,6 | 1,90 |
| сортировка | 240,9 | 201,7 | 178,8 | 1,35 |
| строки | 441,8 | 398,5 | 334,9 | 1,32 |
| дерево | 635,5 | 637,9 | 556,4 | 1,14 |
Вклад по отдельности: флаг сборки 1,14 раза, отметка типа ещё 1,55.
Чем ограничено, и это ограничение стоило отдельной работы. Отметка снимает не проверку тега, а МЕЖМОДУЛЬНЫЙ ВЫЗОВ: сверка тега печатается одним if в теле вызывающего. Первая версия снимала и её, опираясь на дверь входа, — и прогон корпуса нашёл, что дверь сверяет не всё: параметр полиморфизма она пропускает, и через него чужое значение доезжает до доказанного места. Разбор — a-dropped-type-check-gives-a-wrong-answer. Цена честности 2 % по среднему.
Необязательное поле (иногда является число) числом не считается вовсе: значение там может быть «ничто», и вызов помощника остаётся целиком.
Связано: biggest-win-for-least-work, slower-than-python-by-1-4, a-dropped-type-check-gives-a-wrong-answer, lto-speeds-up-the-build-too, byte-for-byte-comparison