Развенчание мифов об x32 ABI

в 11:14, , рубрики: linux, x32 ABI, переводы, Серверная оптимизация

Наверное, некоторые из вас слышали о халяве под названием x32 ABI.

Вкратце о x32 ABI

Если вкратце, то это возможность использовать все преимущества 64-хбитной архитектуры, но при этом сохраняя 32-хбитные указатели. Потенциально при этом приложение будет расходовать меньше памяти, хоть и не сможет адресовать более 4 ГиБ памяти.

Пример. В своём коде вы определяете массив целых чисел и заполняете его значениями. Сколько при этом вы расходуете памяти? Если очень грубо изобразить, то получится примерно так:
32 бита: Указатель + Счётчик числа элементов + N целых чисел = N+2 32-хбитных числа
64 бита: Указатель + Счётчик числа элементов + N целых чисел = N+2 64-хбитных числа = 2N+4 32-хбитных числа
Вот инженеры и задумались: а что если попробовать использовать 32-хбитные указатели на 64-хбитной архитектуре? Архитектура X86-64 имеет систему команд CISC и позволяет это сделать. В этом случае наш массив выше будет расходовать памяти 2N+3 вместо 2N+4. Экономия конечно же незначительная, но дело в том, что в современном коде количество разного рода указателей в структурах нередко доходит до десятка, и использование коротких указателей потенциально позволит экономить до 50% памяти (в идеальном случае).

Для тех кому надо расчёты точнее:
* Насколько большие массивы (и значения) в PHP? (Подсказка: ОЧЕНЬ БОЛЬШИЕ)
* Сколько памяти потребляют объекты в PHP и стоит ли использовать 64-битную версию?

Но как оказалось халявы не будет.

Перевод статьи Debunking x32 myths

Было много комментариев к моей предыдущей статье об x32 ABI. Некоторые из них интересные, другие же люди просто не понимают о чём пишут. У меня сложилось впечатление, что возникло что-то вроде культа карго вокруг этой темы. Люди думают: «Зачем-то же они это делают, так что я тоже смогу это использовать», при этом техническая грамотность, чтобы оценить эту самую пользу, отсутствует.

Так что в том же духе, который я использовал, чтобы пройтись по ccache`у почти четыре года назад (ух-ты, моему блогу уже столько лет, ну не молодец ли я?), я попробую развенчать мифы и заблуждения об этом x32 ABI.

x32 ABI код быстрее

Не совсем верно. Сейчас у нас только несколько результатов тестирования, выложенных теми, кто это самое ABI и создал. Конечно вы ожидаете, что те, кто потратил время, чтобы настроить систему, нашёл её интересной и более быстрой, но, честно говоря, у меня есть сомнения по поводу результатов, по причинам, которые будут ясны после прочтения нескольких следующих предложений.

Также интересно заметить, что, несмотря на то, что общие замеры оказались быстрее, разница — не принципиальная. И даже презентация Интела показывает большие отличия только в сравнении с оригинальным x86, который и так понятно, что хуже x86-64.

Также эти результаты получены с помощью синтетических тестов, а не от реального использования системы, а вы знаете, если конечно знаете, что такие результаты могут врать с три короба.

x32 ABI код компактнее

Новый ABI генерирует меньший код, что значит, что больше инструкций попадёт в кеш процессора, а также у нас будут меньше файлы. Это абсолютно неверно. Генерируемый код, в общем, такой же как и для x86-64, так как не изменяется набор инструкций, просто изменяется так называемая модель данных, которая означает, что вы изменяете размер для long (и связанных типов) и размер указателей (но также меняется и размер доступного адресного пространства).

Теоретически верно, что если вы собираетесь использовать меньшие структуры данных, то их больше влезет в кеш данных (но не в кеш инструкций, будьте уверены (прим. пер.: CISC внутри себя сразу преобразует все короткие инструкции в длинные)), но разве это верный подход? По моему опыту, лучше сосредоточится на написании кода, который оптимальнее расположится в кеше, если ваш код пожирает кеш. Вы можете использовать утилиты dev-util/dwarves от Арнальдо (acme). pahole, например, скажет вам как ваши структуры данных будут разделены в памяти.

Также помните, что для совместимости системные вызовы будут оставлены такими же как в x86-64, что значит, что весь код ядра и системные структуры данных, которые вы будете использовать, будут такими же как и для x86-64. Что означает, что большое количество структур не поменяют свой размер в новом ABI (прим. пер.: бинарного интерфейса).

Наконец, если снова обратится к презентации, вы можете увидеть на слайде 24, что код x32 ABI может быть длиннее, чем оригинальный x86-код. Хорошо бы было, если бы они включили ещё и пример для x86-64 кода (так как я не владею VCISC(прим. пер.: имеется ввиду группа 64-хбитных инструкций из CISC)), но я думаю, что это и так один и тот же код.

Давайте для интереса сравним размер файла libc.so.6. Вот вывод утилиты rbelf-size из моего набора Ruby Elf:

        exec         data       rodata        relro          bss     overhead    allocated   filename
     1239436         7456       341974        13056        17784        94924      1714630   /lib/libc.so.6
     1259721         4560       316187         6896        12884        87782      1688030   x32/libc.so.6

Запускаемый код даже больше в x32 варианте. Большое изменение конечно в структуре данных (data, rodata, relro and bss), так как указатели теперь сокращённые. Я честно говоря даже обеспокоен: «Как можно для Си библиотеки иметь так много указателей в собственных структурах?»; но это вопрос не по теме. Даже с учётом того, что указатели короче, разница не такая большая. В общем, вы будете иметь экономию что-то вроде 30 КиБ, что навряд ли изменит картину маппинга памяти.

Снижение размера данных полезно

Ну да, это ведь главный вопрос. Конечно структуры данных меньше с x32, так как для этого он и делался, в конце концов. Но главный вопрос вероятно будет: «Это так важно?»; я не думаю. Даже в примере выше с Си библиотекой, где разница ощутима, она всего около 20% занимаемого места. И это Си библиотека! Библиотека, которая предполагает, что вы будете писать гораздо меньшие интерфейсы.

Сейчас если вы прибавите к этому все возможные библиотеки, то, возможно, вы можете сэкономить пару мегабайт данных, конечно же, но вы также должны учесть все проблемы портирования, которые я собираюсь обсудить скоро. Да, это правда, что С++ и большая часть языков с виртуальной машиной будут иметь меньше трудностей, особенно при копировании объектов, благодаря уменьшенным указателям, но пока мы может утверждать это с большой натяжкой. В особенности с тех пор как большинство ваших буферов данных должны быть выравнены хотя бы по 8 байт (64 бита), чтобы использовать новые инструкции. И вы уже выравниваете их по 16 байт (128 бит), чтобы использовать некоторые наборы инструкций SIMD.

И для тех, кто думает, что x32 сэкономит место на диске. Запомните, что вы не можете иметь «чистую» x32 систему, то что вы получите — будет смешение трёх подходов: x86, x86-64 и x32.

Это не имеет применения для приложений использующих более 4 ГиБ памяти

Да, конечно, это, возможно, правда. Но серьёзно, вы действительно беспокоитесь о размере указателей? Если вы реально хотите убедиться, что приложение не использует больше, чем определённое количество памяти, используйте системные лимиты! Они, безусловно, менее «тяжёлые» чем создание нового ABI в целом.

Интересно, что есть 2 разных, противоположных подхода для приложений в полном 64-хбитном адресном пространстве с памятью меньше чем 4 ГиБ:

  • ASLR (Address Space Layout Randomization), который может реально загружать различные объекты приложения по широкому диапазону адресов (прим. пер.: то есть как бы разбрасывать по памяти)
  • и Prelink, который делает так, что каждый уникальный объект в системе всегда загружается по одному и тому же адресу, и это действительно противоположно тому, что делает ASLR
Приложения используют long, но им не нужно 64-хбитное адресное пространство

(Прим. пер.: автор имеет ввиду 64-хбитный long)
И, конечно же, решение — создание нового ABI для этого, по мнению некоторых людей.

Я не собираюсь говорить, что много людей для приложений до сих пор используют long, не задумываясь о том, зачем они это делают. Возможно, они имеют небольшие диапазоны чисел, которые хотят использовать, и всё же при этом они используют большие типы, такие как long, так как возможно изучали программирование на системах, которые используют long как синоним int, или даже на системах где long 32-хбитный, а int — 16-ибитный (привет MS-DOS!).

Решение этой проблемы простое — используйте стандартные типы, предоставленные stdint.h, такие как uint32_t и int16_t. Так вы всегда будете использовать размер данных, который ожидаете. Это также работает на большем количестве систем, чем вы ожидали, и работает с FFI и другими техниками.

Ассемблерных вставок не так уж и много

Это сказали мне несколько людей после моего предыдущего поста, где я жаловался, что в новом ABI мы потеряем большую часть ассемблерных вставок. Это утверждение может быть верно, но на самом деле их не так уж и мало как вы думаете. Даже если исключить все мультимедийные программы, криптографические программы, которые неплохо используют SIMD через ассемблерные вставки (а не через оптимизации компилятора).

Также есть проблема с ассемблерными вставками в такой вещи как Ruby, где Ruby 1.9 не компилится на x32. С Ruby 1.8 ситуация более интересная, потому что он компилится, но выбрасывает segfaults at runtime при запуске. Не напоминает вам ничего?

Кроме того, Си библиотека сама по себе идёт с большим количеством ассемблерных вставок. И единственная причина почему вам не надо так много портировать проста — H.J. Lu, который заботится о большей части из них, — один из авторов нового ABI, что означает, что код уже портирован.

x32 ABI будет совместим с x86, если не сейчас, то в будущем

Ну хорошо, я не упоминал об этом ранее, но это одно из заблуждений, которое я заметил перед тем, как меня закидали камнями. К счастью, презентация поможет в этом. Слайд 22 чётко даёт понять, что новый ABI не совместим. Среди прочего вы можете заметить, что ABI по крайней мере исправляет некоторые фактические ошибки в x86, включая использование 32-хбитных типов данных для off_t и другие. Опять же, я немного касался этой темы два года назад.

Это будущее 64-хбитных процессоров

Нет, опять же обратимся к слайдам, в особенности к слайду 10. Это явно сделано для проприетарных систем, чем вообще для замены x86-64! Ну как вы теперь себя чувствуете?

Портирование будет тривиальным, вам просто надо изменить несколько строчек ассемблерных вставок и поменять размер указателей

Это не тот случай. Портирование требует решить ряд других вопросов, и ассемблерные вставки — просто вершина айсберга. Ломать понятие того, что в x86-64 указатели 64-хбитные, — само по себе большая задача, но не такая большая как можно предположить на первый взгляд (и также для Windows), по сравнению с реализацией FFI стиля Си биндингов. Помните, я говорил, что это не простой ответ?

Процессор выполняет лучше 32-хбитные инструкции, чем 64-хбитные

Интересно, что только один процессор, который по утверждению Интела в презентации работает лучше на 32-хбитных инструкциях, — это Atom. Цитирую: «Задержки на 64-хбитных IMUL операциях вдвое выше, чем на 32-хбитных на Atom`е».

Итак, что же такое IMUL? Это операция знакового умножения. Вы умножаете указатели? Это бессмысленно. Кроме того, указатели не знаковые. И вы говорите мне, что вы больше беспокоитесь о платформе (Atom`е), которая имеет большие задержки, когда люди используют 64-хбитные данные вместо положенных 32-хбитных? И ваше решение для этой проблемы — создание нового ABI, где тяжело использовать 64-хбитные типы. И всё это вместо того, чтобы просто исправить в программе то, что порождает эти проблемы?

Я вероятно должен остановиться на этом, так как этот последний комментарий о Atom`е и IMUL`е порадует многих людям, которые лишь поверхностно понимают новый интерфейс.

Автор: gnomeby

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js