2016-06-09 1 views
6

Я где-то читал, что перед выполнением невыложенной загрузки или сохранения рядом с границей страницы (например, с использованием /_mm_storeu_si128 intrinsics) код должен сначала проверить, весь ли вектор (в данном случае 16 байт) принадлежит к той же странице и переключается на не-векторные инструкции, если нет. Я понимаю, что это необходимо для предотвращения coredump, если следующая страница не принадлежит процессу.SSE: несвязанная нагрузка и хранение, которая пересекает границу страницы

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

Env: Linux, x86_64, НКА

ответ

7

Page строки расщепляется плохо влияет на производительность, но не влияет на правильность выровненных доступов. Достаточно убедиться, что вы не читали за конец буфера, когда знаете длину раньше времени.


Для корректности, вам часто приходится беспокоиться об этом при реализации что-то вроде strlen, где ваш цикл останавливается, когда вы найдете значение дозорного. Это значение может быть в любой позиции внутри вашего вектора, так что просто выполнение 16B невысоких нагрузок будет считываться за конец массива. Если завершающий 0 находится в последнем байте одной страницы, а следующая страница не читается, а указатель текущей позиции не выравнивается, то нагрузка, которая включает в себя байт 0, также будет содержать байты с нечитаемой страницы, поэтому он будет виден ,

Одним из решений является выполнение скаляра до тех пор, пока указатель не будет выровнен, а затем выровнят векторы. Выровненная нагрузка всегда полностью исходит из одной страницы, а также из одной строки кеша. Поэтому, несмотря на то, что вы прочтете несколько байтов за конец строки, вы гарантированно не получите ошибку. Valgrind может быть несчастлив, но, но стандартная библиотека strlen реализация использует это.

Вместо скаляра до выровненного указателя вы можете сделать неравнозначный вектор с начала строки (если это не пересекает линию страницы), а затем выполнить выравнивание нагрузок. Первая выровненная нагрузка будет перекрывать первую невыровненную нагрузку, но это вполне нормально для функции типа strlen, которая не заботится о том, видит ли она одни и те же данные дважды.


Возможно, стоит избегать расщепления страниц по соображениям производительности. Даже если вы знаете, что ваш указатель src неверен, часто быстрее разрешается разделение кэша на аппаратное обеспечение. Но до Skylake, разбиение страниц имеет дополнительную задержку ~ 100 с. (Down to 5c in Skylake). Если у вас есть несколько указателей, которые могут быть выровнены по-разному относительно друг друга, вы не всегда можете просто использовать пролог, чтобы выровнять ваш src. (Например, c[i] = a[i] + b[i] и c выравнивается, но b не является.)

В этом случае, возможно, стоит использовать ветку, чтобы сделать выровненные нагрузки до и после раскола страницы, и объединить их с palignr.

Отражающий неверный прогноз (~ 15c) дешевле, чем латентность с разбивкой по страницам, но задерживает все (а не только нагрузку). Таким образом, он может также не быть в зависимости от аппаратного обеспечения и соотношения вычислений в доступе к памяти.


Если вы пишете функцию, которая обычно вызывается с совмещенными указателями, то имеет смысл просто использовать невыровненные инструкции загрузки/сохранения. Любой пролог для обнаружения несоосности - это дополнительные накладные расходы для уже выровненного случая, а на современном оборудовании (Nehalem и новее) неодинаковые нагрузки на адрес, которые оказываются выровненными во время выполнения, имеют идентичную производительность для согласованных инструкций загрузки. (Но вам необходимо, чтобы AVX для неуравновешенных нагрузок складывался в другие команды в качестве операндов памяти, например vpxor xmm0, xmm1, [rsi])

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

(Если неровные входы являются общими, то является стоит использовать пролог, чтобы выровнять указатель ввода, особенно если вы используете AVX. Последовательные 3 AVX нагрузки будет кэшировать строки разделить каждую другую нагрузку.)

Для получения дополнительной информации и других ссылок в теги wiki.

+0

@ ZheyuanLi: Да, мне любопытно, что это за изменение дизайна. Skylake также может выполнять две параллельные страницы для решения двух пропусков TLB. Эти два факта могут быть связаны. –

+0

Спасибо !. Я также не понимал, что межстраничный доступ может иметь такую ​​высокую стоимость. Так что это определенно нужно искать. –

+1

BTW, Valgrind имеют опцию --partial-load-ok = yes, которая может скрыть проблемы с недопустимым чтением, вызванные векторными нагрузками, когда загруженные данные проходят в конец буфера. –

Смежные вопросы