страница с ошибками maskmovdqu / _mm_maskmoveu_si128 - как избежать? - PullRequest
0 голосов
/ 11 октября 2019

У меня есть функция, которая выводит структурированные данные. Данные представляют собой Vec4 / Vec3 / Vec2 / float-структуры, поэтому максимальный размер составляет 16 байт на структуру. Теперь может случиться так, что поток читается, начиная с структуры. Простое решение: загрузить структуру, построить маску хранилища, уменьшить указатель данных назначения на количество байтов в нашей структуре, которое вызов хочет начать читать.

Представьте, что текущий тип элемента - это Vec2, в этом мы находимся на 4 байтаструктура:

xmm0 = 00000000-00000000-dadadada-dadadada
xmm1 = 00000000-00000000-ffffffff-00000000
result_data_ptr = 13450000
-> RDI = 1344fffc
maskmovdqu xmm0, xmm1

=> результат - исключение ошибки страницы.

Есть ли способ обнаружить ошибку этой страницы? Память предыдущей страницы даже не будет затронута ...

Ответы [ 2 ]

2 голосов
/ 11 октября 2019

maskmovdqu не выполняет подавление ошибок, в отличие от хранилищ с масками AVX vmaskmovps или AVX512. Это решило бы вашу проблему, хотя, возможно, все еще не самым эффективным способом.

Как указано в справочном руководстве Intel по ISA , с маской из всех нулей (поэтому в памяти ничего не сохраняется) Исключения, связанные с адресацией памяти и сбоями страниц, могут все еще сигнализироваться (зависит от реализации).

С ненулевой маской, я предполагаю, что она гарантированно вызывает сбой страницыесли 16 байтов включает в себя любые не записываемые страницы. Или, может быть, некоторые реализации делают маску для подавления сбоев, даже когда происходит некоторое сохранение (нули на неотображенной странице, но ненулевые в других местах)


В любом случае, это не слишком быстрая инструкция на реальных процессорах.

maskmovdqu мог бы быть хорошим иногда на одноядерном Pentium 4 (или не на IDK), и / или его предшественник MMX мог быть полезен на заказном Pentium. Хранилища с маскированным обходом кеша гораздо менее полезны на современных процессорах, где L3 является нормальным обратным остановом, а кэши большие. Возможно, более важно то, что между одним ядром и контроллером (-ами) памяти больше оборудования, потому что все должно работать правильно, даже если другое ядро ​​ делало перезагрузить эту память в какой-то момент, так что частичная запись может бытьдаже менее эффективно.

Как правило, это ужасный выбор, если вы действительно храните всего 8 или 12 байт. (По сути, это то же самое, что хранилище NT, которое не записывает полную строку). Особенно, если вы используете несколько узких хранилищ, чтобы собирать фрагменты данных и помещать их в один непрерывный поток. Я бы не предположил, что несколько перекрывающихся maskmovdqu хранилищ приведут к единственному эффективному хранилищу всей строки кэша, как только вы в конечном итоге завершите одну, даже если маски означают, что ни один байт фактически не записан дважды.

L1d-кеш отлично подходит для буферизации нескольких небольших записей в строку кеша до того, как это будет сделано;используйте эти обычные хранилища, если только вы не можете делать несколько хранилищ NT почти вплотную.

Чтобы сохранить 8 верхних байтов регистра XMM, используйте movhps.

Запись в кэштакже позволяет делать перекрывающиеся магазины, например movdqu. Таким образом, вы можете объединить несколько 12-байтовых объектов, перетасовывая их каждый в конец регистра XMM (или загружая их таким образом), а затем использовать movdqu store в [rdi], [rdi+12], [rdi+24] и т. д. 4-байтовое перекрытие вполне нормально;объединение в буфере хранилища может поглотить его еще до того, как он зафиксируется в кеше L1d, или, если нет, кеш L1d все еще будет довольно быстрым.


В начале записи большого массива, если вы этого не сделаетеЗнайте выравнивание, которое вы можете сделать без выравнивания movdqu из первых 16 байтов вашего вывода. Затем создайте первое 16-байтовое выровненное хранилище, возможно, перекрывающееся с этим. Если ваш общий размер вывода всегда> = 16 байт, эта стратегия не требует большого количества ветвлений, чтобы позволить вам выровнять хранилища для большей части. В конце вы можете сделать то же самое с последним потенциально невыровненным вектором, который может частично перекрывать последний выровненный вектор. (Или, если массив выровнен , то перекрытия нет, и он тоже выровнен. movdqu такой же быстрый, как movdqa, если адрес выровнен, на современных процессорах.)

0 голосов
/ 11 октября 2019

Ну, так как кажется, что нет хорошего способа предсказать ошибку страницы, я пошел другим путем. Это прямое решение:

Сначала мы используем таблицу для смещения результата в соответствии с bytes_inside. Затем мы узнаем, сколько байтов должно быть записано. Поскольку нужно записать не более 15 байтов, это работает в 4 этапа. Мы просто проверяем биты bytes_to_write - если бит «8» (т. Е. Бит 3) установлен, мы используем movq. бит 2 требует movd, бит 1 a pextrw и бит 0 a pextrb. После каждого сохранения указатель данных увеличивается соответственно, а регистр данных соответственно смещается.

Регистры:

  • r10: result_data_ptr
  • r11: bytes_inside
  • xmm0.word [6]: размер элемента данных
  • xmm2: наш элемент данных
  • shuf_inside: таблица данных для поворота регистра xmm с помощью pshufb (только psrldq)позволяет мгновенный подсчет байтов-смещений)
.DATA
ALIGN 16
shuf_inside   byte 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0
              byte 2,3,4,5,6,7,8,9,10,11,12,13,14,15,0,1
              byte 3,4,5,6,7,8,9,10,11,12,13,14,15,0,1,2
              byte 4,5,6,7,8,9,10,11,12,13,14,15,0,1,2,3
              byte 5,6,7,8,9,10,11,12,13,14,15,0,1,2,3,4
              byte 6,7,8,9,10,11,12,13,14,15,0,1,2,3,4,5
              byte 7,8,9,10,11,12,13,14,15,0,1,2,3,4,5,6
              byte 8,9,10,11,12,13,14,15,0,1,2,3,4,5,6,7
              byte 9,10,11,12,13,14,15,0,1,2,3,4,5,6,7,8
              byte 10,11,12,13,14,15,0,1,2,3,4,5,6,7,8,9
              byte 11,12,13,14,15,0,1,2,3,4,5,6,7,8,9,10
              byte 12,13,14,15,0,1,2,3,4,5,6,7,8,9,10,11
              byte 13,14,15,0,1,2,3,4,5,6,7,8,9,10,11,12
              byte 14,15,0,1,2,3,4,5,6,7,8,9,10,11,12,13
              byte 15,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14
.CODE
[...]
        lea             rax,        [ shuf_inside ]
        shl             r11,        4
        pshufb          xmm2,       [ rax + r11 - 16 ]
        shr             r11,        4
        pextrw          rax,        xmm0,       6           ;reducedStrideWithPadding - i.e. size of item
        sub             rax,        r11                     ;bytes_to_write
        ;
        test            rax,        8
        jz              lessThan8
        movq            qword ptr [r10], xmm2
        psrldq          xmm2,       8
        add             r10,        8
        lessThan8:
        test            rax,        4
        jz              lessThan4
        movd            dword ptr [r10], xmm2
        psrldq          xmm2,       4
        add             r10,        4
        lessThan4:
        test            rax,        2
        jz              lessThan2
        pextrw          word ptr [r10], xmm2, 0
        psrldq          xmm2,       2
        add             r10,        2
        lessThan2:
        test            rax,        1
        jz              lessThan1
        pextrb          byte ptr [r10], xmm2, 0
        lessThan1:
...