Ответ 1
Память обычно организована на 4 тыс. страниц (хотя там также поддерживаются более крупные размеры). Виртуальное адресное пространство, которое видит ваша программа, может быть смежным, но это не обязательно в физической памяти. OS, которая поддерживает сопоставление виртуальных и физических адресов (на карте страниц), обычно пытается сохранить вместе физические страницы, но это не всегда возможно, и они могут быть разбиты (особенно при длительном использовании, когда они могут быть заменены иногда).
Когда ваш поток памяти пересекает границу страницы 4k, CPU необходимо остановить и перейти к новому переводу - если он уже просмотрел страницу, он может быть кэширован в TLB, а доступ оптимизирован как самый быстрый, но если это первый доступ (или если у вас слишком много страниц для TLB, на которые нужно удержаться), CPU должен будет остановить доступ к памяти и начать прохождение страницы через записи карты страниц - это относительно долго, как каждый уровень на самом деле является памятью, которая читается сама по себе (на виртуальных машинах она еще больше, так как каждому уровню может потребоваться полный перерыв на хосте).
У вашей функции memcpy может возникнуть другая проблема - при первом распределении памяти ОС просто создаст страницы в pagemap, но помечает их как неактивные и немодифицированные из-за внутренних оптимизаций. Первый доступ может не только вызывать прохождение страницы, но, возможно, также помогает сообщать ОС, что страница будет использоваться (и хранится для страниц целевого буфера), что приведет к дорогостоящему переходу на какой-либо обработчик ОС.
Чтобы устранить этот шум, выделите буферы один раз, выполните несколько повторений копии и рассчитайте время амортизации. Это, с другой стороны, даст вам "теплую" производительность (т.е. После прогревания кешей), поэтому вы увидите, что размеры кеша отражаются на ваших графиках. Если вы хотите получить "холодный" эффект, не страдая от задержек подкачки, вам может понадобиться очистить кеши между итерацией (просто убедитесь, что вы не успели это сделать)
ИЗМЕНИТЬ
Перечитайте вопрос, и вы, кажется, делаете правильное измерение. Проблема с моим объяснением заключается в том, что после 4k*i
она должна постепенно увеличиваться, так как на каждой такой каплей вы платите штраф снова, но затем должны наслаждаться бесплатной поездкой до следующих 4k. Это не объясняет, почему есть такие "всплески", и после них скорость возвращается к норме.
Я думаю, что вы столкнулись с подобной проблемой с критическим вопросом шага, связанным с вашим вопросом, - когда ваш размер буфера - хороший раунд 4k, оба буфера будут выравниваться с одними и теми же наборами в кеше и трэш друг друга. Ваш L1 - 32k, поэтому сначала это не похоже на проблему, но при условии, что данные L1 имеют 8 способов, это на самом деле обход вокруг 4k для одних и тех же наборов, и у вас есть 2 * 4k блоки с одинаковым выравниванием (предполагая, что распределение было сделано смежно), поэтому они перекрываются на одних и тех же наборах. Достаточно того, что LRU работает не так, как вы ожидаете, и у вас будут конфликты.
Чтобы проверить это, я попытался malloc фиктивный буфер между pbuff_1 и pbuff_2, сделайте его 2k большим и надеемся, что он сломает выравнивание.
EDIT2:
Хорошо, так как это работает, пора разработать немного. Предположим, вы назначили два массива 4k в диапазонах 0x1000-0x1fff
и 0x2000-0x2fff
. set 0 в вашем L1 будет содержать строки в 0x1000 и 0x2000, набор 1 будет содержать 0x1040 и 0x2040 и т.д. При этих размерах у вас еще нет проблем с ударом, все они могут сосуществовать без переполнения ассоциативности кеша. Тем не менее, каждый раз, когда вы выполняете итерацию, у вас есть загрузка и хранилище, доступ к одному набору - я предполагаю, что это может привести к конфликту в HW. Хуже того, вам понадобится несколько итераций для копирования одной строки, что означает, что у вас есть загруженность 8 нагрузок + 8 магазинов (меньше, если вы векторизовать, но все же много), все направлены на тот же самый плохой набор, я довольно уверен, что там есть куча коллизий.
Я также вижу, что руководство по оптимизации Intel может что-то сказать об этом (см. 3.6.8.2):
4-Кбайтное сглаживание памяти происходит, когда код обращается к двум различным памяти с 4-килобайтным смещением между ними. 4-Кбайт ситуация с псевдонимом может проявляться в режиме копирования памяти, где адреса исходного буфера и буфер назначения сохраняют постоянное смещение, а постоянное смещение оказывается кратным приращение байта от одной итерации к следующей.
...
нагрузки должны ждать, пока магазины не будут уволены, прежде чем они смогут Продолжать. Например, при смещении 16 нагрузка следующей итерации равна 4-килобайтный алиасированный текущий итерационный магазин, поэтому цикл должен ждать пока операция хранилища не завершится, делая весь цикл сериализованная. Количество времени, необходимого для ожидания, уменьшается с увеличением смещение до смещения 96 разрешает проблему (поскольку нет ожидающих магазины к моменту загрузки с тем же адресом).