Здравствуйте, nbie, Вы писали:
N>Поясните еще раз эту мысль ("А из кеша в память льется струей" — к чему это). N>Я, конечно пойду читать про кеш, но я думал я заполняю память по указателю непосредственно.
Вы пишете в кеш. Это такая маленькая быстрая память, припаянная прямо к процессору. Процессор потом, по своему усмотрению и без вашего участия, перемещает данные из кеша в память — да еще и с промежуточным буферированием в контроллере памяти.
Из кеша в контроллер памяти данные уходят блоками размером с cache line (это минимальная единица обмена между кешом и системной памятью). У современных x86-х процессоров кеш-лайн имеет размер 64 байта. Контроллер же памяти старается склеить эти обращения так, чтобы набрать целую "строку": работа "строками" — это самый быстрый способ работы с памятью. Чему сейчас равна строка, я даже и не знаю.
Здравствуйте, Pzz, Вы писали:
N>>Поясните еще раз эту мысль ("А из кеша в память льется струей" — к чему это). N>>Я, конечно пойду читать про кеш, но я думал я заполняю память по указателю непосредственно.
Pzz>Вы пишете в кеш. Это такая маленькая быстрая память, припаянная прямо к процессору. Процессор потом, по своему усмотрению и без вашего участия, перемещает данные из кеша в память — да еще и с промежуточным буферированием в контроллере памяти.
Pzz>Из кеша в контроллер памяти данные уходят блоками размером с cache line (это минимальная единица обмена между кешом и системной памятью). У современных x86-х процессоров кеш-лайн имеет размер 64 байта. Контроллер же памяти старается склеить эти обращения так, чтобы набрать целую "строку": работа "строками" — это самый быстрый способ работы с памятью. Чему сейчас равна строка, я даже и не знаю.
На самом деле запись в кеш не обязательна для объединения множественных записей в одну транзакцию.
Конечно, в кеш писать можно очень быстро. И если ваша цель сделать быструю функцию memset, то смело пишите в кеш.
Вот только проблема тут в том, что такая функция будет при своей работе вытеснять из кеша другие полезные данные. В результате чего получается ситуация, когда функция memset работает быстро, но код после её вызова начинает внезапно тормозить — причина проста, быстрый memset выкинул из кеша нужные в дальнейшем данные, оставив в нём лишь много-много нулей.
И понятно, что хотелось бы не только оптимизировать memset, но и не замедлять работу окружающего кода (ведь в жизни важна суммарная производительность программы, а не отдельных её кусочков).
Для этого в x86 процессорах есть streamming-инструкции (семейство movnt*), которые служат для записи в память минуя кеш. Грубо говоря, у этих инструкций есть как бы собственный кеш, но который не пересекается с основным, и для которого даже не поддерживается неявная когерентность. То есть можно записывать те же 16 байт из sse регистнов в этот буфер, где они будут объединятся в транзакции, и после этого записываться в память строками, минуя опять же кеш. Если данных в память записывается много (очевидно, что если мы обнуляем количество байт сравнимое с размером L2 кеша, то это уже много), то имеет смысл именно писать через streamming-инструкции, ведь в этом случае кешированная версия memset никак не будет быстрее чем streamming-версия, ибо первая версия будет даже сама себя из кеша вытеснять, а вторая просто будет лить данные прямо в память.
D>>за счет откладывания уплаты стоимости на момент(ы) первого доступа к этой памяти
N>Очень интересно. Подобного в Windows нет? N>Могли бы сказать ключевые слова для поиска инф, чтобы понять смысл этих слов? ) Или разъяснить.
примерно по тем же принципам по которым работает своп.
Современные процессоры автоматически отображают виртуальные адреса в физические. Если код пытается обратиться по виртуальному адресу который ни на что не отображается, то происходит trap и управление передается ОС. ОС смотрит что такое, и может разрешить это обращение, настроив отображение для этого адреса (для этой страницы памяти)
Когда ты делаешь mmap на /dev/zero то тебе просто выдают указатель на неициализированную память (которая не отображена ни на какие реальные адреса). Когда ты пытаешься обратится к ней, то срабатывает ловушка и ОС выделяет страницу памяти заполненную нулями.
Здравствуйте, nbie, Вы писали:
N>Добрый день, коллеги. N>Подскажите самый быстрый способ обнулить блок памяти (1000000 байт)?
самый бйстрый способ его просто выкинуть и выделить новый через virtualalloc
там он гарантированно будет забит нулями и занимается этим system idle process
Здравствуйте, nbie, Вы писали:
N>Подскажите самый быстрый способ обнулить блок памяти (1000000 байт)?
Я бы начал с memset(), собрав программу под соответствующий процессор (а не под generic i386), и разрешив компилятору инлайнить такие функции. memset() не так уж наивно устроен. Потом я бы померил получившуюся скорость, сравнил бы ее с теоретическим пределом, и либо успокоился, либо сел бы думать дальше.
N>>Добрый день, коллеги. N>>Подскажите самый быстрый способ обнулить блок памяти (1000000 байт)? R>самый бйстрый способ его просто выкинуть и выделить новый через virtualalloc R>там он гарантированно будет забит нулями и занимается этим system idle process
VirtualAlloc то сам по себе может и окажется быстрее чем memset на уже выделенную память, но вот последующее первое обращение к этой памяти... Ой ой ой. Поясняю на живом примере:
Смеемся и плачем от счастья.. Потом думаем — ведь Virtual* теоретически подразумевают собой нехиый оверхед.. Идти в ядро, ковыряцца в таблицах страниц, дереве VAD процесса, Чота тут не так..
Слегка модифицируем тест:
Вот все и встало на свои места.. Итак винда похоже тоже практикует ленивую отдачу страниц, так что постоянный VirtualAlloc будет быстрее однократного malloc + многократного memset тока если не не планируете этой памятью пользоваться Справедливости ради, если делать многократные malloc/memset/free то оно все же медленее чем VM... Во всяком случае пока у винды не наступит жесткий голодняк свободных и уже обнуленных в бэкграундее страниц.
Как много веселых ребят, и все делают велосипед...
Здравствуйте, dilmah, Вы писали:
D>Когда ты делаешь mmap на /dev/zero то тебе просто выдают указатель на неициализированную память (которая не отображена ни на какие реальные адреса). Когда ты пытаешься обратится к ней, то срабатывает ловушка и ОС выделяет страницу памяти заполненную нулями.
Угу. Иными словами, к цене memset() добавляется цена обработки (аппаратного) исключения.
Здравствуйте, nbie, Вы писали:
N>Добрый день, коллеги. N>Подскажите самый быстрый способ обнулить блок памяти (1000000 байт)?
N>PS Кроме SSE инструкций (заполнять по 16 байт) ничего нет?
В микроволновку на 2-3 секунды. Обнулится по первому классу.
Добрый день, коллеги.
Подскажите самый быстрый способ обнулить блок памяти (1000000 байт)?
PS Кроме SSE инструкций (заполнять по 16 байт) ничего нет?
Re: Быстрый способ обнулить блок памяти?
От:
Аноним
Дата:
10.09.11 09:11
Оценка:
Здравствуйте, nbie, Вы писали:
N>Добрый день, коллеги. N>Подскажите самый быстрый способ обнулить блок памяти (1000000 байт)?
N>PS Кроме SSE инструкций (заполнять по 16 байт) ничего нет?
А что за ОС?
В Линукс можно выполнить mmap() нужного размера куска файла /dev/zero в память.
А>А что за ОС? А>В Линукс можно выполнить mmap() нужного размера куска файла /dev/zero в память.
Пардон, Windows.
PS Но если говорить про linux: за счет чего такая операция имеет выигрыш производительности? Разве работа функции не сводится к побайтовому копированию или блочному по 16?
N>PS Но если говорить про linux: за счет чего такая операция имеет выигрыш производительности? Разве работа функции не сводится к побайтовому копированию или блочному по 16?
за счет откладывания уплаты стоимости на момент(ы) первого доступа к этой памяти
N>>PS Но если говорить про linux: за счет чего такая операция имеет выигрыш производительности? Разве работа функции не сводится к побайтовому копированию или блочному по 16? D>за счет откладывания уплаты стоимости на момент(ы) первого доступа к этой памяти
Очень интересно. Подобного в Windows нет?
Могли бы сказать ключевые слова для поиска инф, чтобы понять смысл этих слов? ) Или разъяснить.
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, nbie, Вы писали:
N>>Подскажите самый быстрый способ обнулить блок памяти (1000000 байт)?
Pzz>Я бы начал с memset(), собрав программу под соответствующий процессор (а не под generic i386), и разрешив компилятору инлайнить такие функции. memset() не так уж наивно устроен. Потом я бы померил получившуюся скорость, сравнил бы ее с теоретическим пределом, и либо успокоился, либо сел бы думать дальше.
memset внутри себя вызывает SSE2 в определенных случаях. Случай ТС как раз такой. Поэтому имеет смысл попробовать SSE2 напрямую — обойтись без лишних проверок (а у ТС размер массива кратен 4). Будет ли эффект — трудно сказать.
Насчет инлайнить — можн поподробнее ? Трассировка в режиме релиз показывает, что она вызывается из библиотеки.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>memset внутри себя вызывает SSE2 в определенных случаях. Случай ТС как раз такой. Поэтому имеет смысл попробовать SSE2 напрямую — обойтись без лишних проверок (а у ТС размер массива кратен 4). Будет ли эффект — трудно сказать.
При таком размере накладные расходы от этих проверок — копейки. Я бы не стал тратить время на возню с SSE, если memset достаточно хорош.
PD>Насчет инлайнить — можн поподробнее ? Трассировка в режиме релиз показывает, что она вызывается из библиотеки.
N>>>Подскажите самый быстрый способ обнулить блок памяти (1000000 байт)?
Pzz>>Я бы начал с memset(), собрав программу под соответствующий процессор (а не под generic i386), и разрешив компилятору инлайнить такие функции. memset() не так уж наивно устроен. Потом я бы померил получившуюся скорость, сравнил бы ее с теоретическим пределом, и либо успокоился, либо сел бы думать дальше.
PD>memset внутри себя вызывает SSE2 в определенных случаях. Случай ТС как раз такой. Поэтому имеет смысл попробовать SSE2 напрямую — обойтись без лишних проверок (а у ТС размер массива кратен 4). Будет ли эффект — трудно сказать.
Присоединяюсь к вопросу. Если заполнение блоками по 16 байт — это максимум скорости, то проделать это самому в цикле разве не будет оптимальным?
PS Кстати, может кто сказать почему в SSE 16 максимум (?)? Почему не больше?
И, пардон, что такое "TC"?
Здравствуйте, nbie, Вы писали:
N>Присоединяюсь к вопросу. Если заполнение блоками по 16 байт — это максимум скорости, то проделать это самому в цикле разве не будет оптимальным?
Тот же memset, только без проверок на случай, если размер не кратен 16.
N>PS Кстати, может кто сказать почему в SSE 16 максимум (?)? Почему не больше?
16 * 8 = 128 — разрядность SSE
N>И, пардон, что такое "TC"?
Здравствуйте, nbie, Вы писали:
PD>>memset внутри себя вызывает SSE2 в определенных случаях. Случай ТС как раз такой. Поэтому имеет смысл попробовать SSE2 напрямую — обойтись без лишних проверок (а у ТС размер массива кратен 4). Будет ли эффект — трудно сказать.
N>Присоединяюсь к вопросу. Если заполнение блоками по 16 байт — это максимум скорости, то проделать это самому в цикле разве не будет оптимальным?
Это вы кеш заполняете блоками по 16 байт. А из кеша в память льется струей.
N>PS Кстати, может кто сказать почему в SSE 16 максимум (?)? Почему не больше?
Потому что самые большие програмно доступные регистры у x86 — это 16-байтные регистры SSE
N>И, пардон, что такое "TC"?
Здравствуйте, Pzz, Вы писали:
Pzz>Это от компилятора зависит. gcc инлайнит эти функции, если оптимизация включена. Насчет MSVC я не особенно копенгаген . Смотрите здесь: http://msdn.microsoft.com/en-us/library/26td21ds.aspx
Черт его знает. /Oi установил, Release, но все равно call и jmp в адреса 0x7xxxxxxx. Может, что-то не так делаю. А впрочем, ты прав — на миллион байт это все копейки.
Коллеги, все понял, кроме этого:
N> Если заполнение блоками по 16 байт — это максимум скорости, то проделать это самому в цикле разве не будет оптимальным? P> Это вы кеш заполняете блоками по 16 байт. А из кеша в память льется струей.
Поясните еще раз эту мысль ("А из кеша в память льется струей" — к чему это).
Я, конечно пойду читать про кеш, но я думал я заполняю память по указателю непосредственно.
N>Добрый день, коллеги. N>Подскажите самый быстрый способ обнулить блок памяти (1000000 байт)?
N>PS Кроме SSE инструкций (заполнять по 16 байт) ничего нет?
вот тут есть какие-то слова по этому поводу http://lwn.net/Articles/255364/
общая идея в том, что кеш использовать в этом случае не надо — это дополнительные затраты на чтение кешлайна, модификацию и запись назад.
кстат про sse, если у gcc задать уровень оптимизации -O3, то он даже для
for(i = 0; i<много; i++)
buffer[i] = 0;
будет использовать sse инструкции, и я думаю в этих вопросах можно положиться на libc и gcc, ну или их виндовые аналоги.
O>Вот все и встало на свои места.. Итак винда похоже тоже практикует ленивую отдачу страниц
валлок будет все время выдавать разные блоки памяти.
Ты всего лишь сравнил скорость зануления 1мб блока в кэше процессора vs 1мб блок в оперативке.
Понятно что в кэше-то их занулять намного быстрее.
O>>Вот все и встало на свои места.. Итак винда похоже тоже практикует ленивую отдачу страниц R>валлок будет все время выдавать разные блоки памяти. R>Ты всего лишь сравнил скорость зануления 1мб блока в кэше процессора vs 1мб блок в оперативке. R>Понятно что в кэше-то их занулять намного быстрее.
Даже если и изза кэша, все равно медленно ведь
Как много веселых ребят, и все делают велосипед...