23 февраля 2013 в 15:14

Развенчание мифов об 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`е порадует многих людям, которые лишь поверхностно понимают новый интерфейс.




UPD. Только что попробовал собрать PHP на своей виртуалке с Gentoo x32 ABI RC. Как и Ruby, он не компилится.
Перевод: Diego Elio Pettenò
Андрей Нехайчик @gnomeby
карма
43,2
рейтинг 0,0
Самое читаемое Администрирование

Комментарии (20)

  • НЛО прилетело и опубликовало эту надпись здесь
  • 0
    Все же, а так ли нужны 64-разрядные данные?
    Мне всегда казалось, что 64х система — это в первую очередь возможность иметь неограниченное (в ближайшей исторической перспективе) адресное пространство. И лишь значительно потом — целые 64-разрядные числа. Смысла в 64-битной арифметике я лично особого не вижу. Там где действительно нужны такие числа, вполне себе используется float/double.
    Платформу же, где реализовали 64-битность, но при этом забыли вставить FPU, найти, наверное, можно, но далеко не на каждой улице.
    Я понимаю, что openstreetmap дальше на 32-разрядном инте уже не может, но это далеко не столь частая ситуация…
    В общем для меня 64-битность начинается в первую очередь с адресов, а не с данных…
    • +14
      да, нужны. примите как должное.
      если ваши задачи не встречают их — то просто поверьте, поймёте потом.
    • НЛО прилетело и опубликовало эту надпись здесь
    • +8
      Размеры файлов/смещения в файлах тоже в double? Ну будет из-за округления плюс минус один байт, подумаешь. А еще счетчики, временные метки, вычисления с большими промежуточными значениями.
    • +2
      а ARM без FP вообще не редкость…
      • +2
        Казалось бы, причём тут x32. В любом случае ARMv8 без fpu не будет
    • +2
      В OpenStreetMap уже столкнулись с проблемой 32 бит для указателей на объекты. Некоторые скрипты просто стали работать некорректно на 32 битных системах. Собственно, всё теперь нужно адаптировать под больший размер. На 64 битных системах сделать это проще просто потому что они такие.
    • +2
      Посмотрите презентацию, там много подробностей. Но если вкратце, то 64-хбитная база хороша следующим:
      * в 2 раза больше регистров (что хорошо для мультимедия операций)
      * более быстрый FP с помощью SSE
      * непосредственно 64-хбитные целые, которые нужны для:
      ** unixtime
      ** хранения размера файлов больших, чем 2ГиБ
      • +2
        Дык, 32 ABI этому не мешает. Регистров остаётся столько же. FP и SSE тоже. Никто не запрещает использовать 64-битные целые. Единственное, появляется определённая специфика для работы со структурами данных более 2 Гб.
        • 0
          Это понятно. Но мы ведём диалог о том, зачем вообще нужны 64-битные данные.
    • 0
      Не забывайте, что fpu-операции на порядок медленнее целочисленной арифметики.
      • 0
        Это почему?
        Набросал тупой тестик на умножение

        void main()
        {
                double z = 1.1212;
                for (int i = 0; i < 1000000000; i++) {
                        if ((i & 15) == 0) z = i;
                        else z *= 1.4581;
                }
                printf("%f", z);
        }


        против

        void main()
        {
                int z = 17;
                for (int i = 0; i < 1000000000; i++) {
                        if ((i & 15) == 0) z = i;
                        else z *= 37;
                }
                printf("%d", z);
        }


        версия с int работает 1.1 секунду, с double — 0.9 секунды.
  • НЛО прилетело и опубликовало эту надпись здесь
    • +2
      Ну так вы и напишите здесь.
  • +1
    Хочу заметить, что когда мы говорим об «указателях», нужно помнить, что heap поддерживается как раз кучей структур, состоящих из двух или трех указателей, так что если приложение выделяет много небольших объектов (а таких приложений немало), то можно ощутить разницу. Если на рядовом десктопе (где минимум 4gb ram сегодня) эта разница и может показаться незначительной, то на всяких VDS-ках это уже может пригодиться.
  • 0
    Говоря о применении IMUL, автор не прав.
    MSVC последний использует imul для доступа к элементу массива по индексу

    class Player
    {
    public:
            char name[437];
    } *players;
    
    bool hasName(size_t playerNo)
    {
            return players[playerNo].name[0] != '\0';
    }


    hasName:
    mov     rdx, cs:[players]
    xor     eax, eax
    imul    rcx, 1B5h
    cmp     [rcx+rdx], al
    setnz   al
    retn
  • 0
    Удивительно, что человек такие тексты пишет, и при этом, похоже, совершенно не понимает, как оно работает. Например он удивляется, как много указателей в С-библиотеке. А он задумывался, что в программе вообще-то как раз и используются в основном указатели? Массивы, структуры, объекты, процедуры, методы — указатели. А что, вообще не указатели в современном коде? Переменные? Так, для доступа к ним всё-равно будут сгенерированы указатели. А как иначе процессор «узнает» где они находятся? Даже стек и тот фактически является указателем. И все эти вызовы функций — тоже указатели. И передача параметров через стек — тоже работа с указателями. 99% кода — работа с указателями.
    И про то, что не нужно бояться загрязнения стека, нужно просто правильно располагать данные… Во-первых, «правильно» располагать, это вовсе не тривиально. И чем меньше гарнулярность, тем больше будет оптимального кода. Во-вторых, «располагает» не только программист. Передача параметров через стек и локальные переменные тоже требуют выравнивания. И программист на это повлиять фактически не может. А там тот ещё адов ад может твориться. Активная работа с 64-битными указателями ( а как уже говорил выше, это активная работа может возникнуть совершенно на пустом месте — передача несколько объектов в процедуру и работа с ними) может в разы более сильно загрязнить стек. Данных в стеке станет в два раза больше, а ассоциативность кэша процессоров ограничена — будут вытеснены какие-то иные данные по совершенно другим (кратным) адресам. Фактически, переход на 64 бита требует удвоения пропускной способности памяти, размера кэш-памяти и уровня её ассоциативности (а с этим ОЧЕНЬ трудно. Тут просто миллионом транзисторов не отделаешься. Да и при современном развитии многопоточности ассициативность лишней не бывает). И есть мнение, что мы не видим резкого падения при переходе на x64 только по той причине, что там система команд и регистров более сбалансированная (нет бремени совместимости с древним x32/x16 кодом) и за счёт этого узкие месте нивелируются большей степенью оптимальности кода (больше регистров, более универсальная и регулярная система команд). Если совместить более плотную модель памяти (фактически x32 ABI) и новую архитектуру, то на этих узких местах можно получить заметный прирост производительности.
    Короче, есть такое мнение, что здравое зерно x32 ABI есть. И не малое. Очень странно, что это не было внедрено сразу (что было бы крайне логично и облегчило бы миграцию x32 на x64). Имеет ли смысл сейчас — уже большой вопрос. Пока индустрия перейдёт, процессоры могут развиться на столько, что эти узкие места станут не актуальны.
    • 0
      Вижу, в комментариях многие не видят разницы между архитектурой 64-бита и ABI-бита. Речь идёт не об отказе от 64-битной архитектуре, а о том, чтобы по-умолчанию использовать 32-битные указатели и за счёт этого добиться более эффективного использования памяти. При этом никто не запрещает использовать 64-битные целые и прочие плюшки x64. Только работа со структурами данных более 2 Гб потребует приседаний в виде отдельного диспетчера памяти и специльного типа указателей.
    • 0
      Тьфу, в середине по ошибке в двух местах написал «загрязнение стека» вместо «загрязнения кэша». Там по смысле понятно.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.