Но мне стало интересно, смогу ли я этот эффект поймать в реальности. Реальность, как обычно, оказалась чуть сложнее теории, но в поединке мозга и компьютера мозг опять победил: вот этот код при базовых (тщательно подобранных) настройках дает в синхронном режиме (
-Dasync=false
) (690 +/- 1%) нс/сообщение, а в асинхронном (560 +/- 8%) нс/сообщение.Хотелось бы в общих чертах понять, о чем говорит иностранецчто там происходит?
Нужно подобрать некоторую условную memory intensive задачу, и разбить ее на две фазы, каждая из которых обращается к своему куску данных. Я взял кусок данных в виде достаточно большого (сопоставимого с размером L1) массива double[]
. Сама "фаза обработки сообщения" состоит из нескольких чтений/записей в ячейки этого массива. "Несколько" выбрано равным 128, и эти 128 ячеек выбираются из всего массива по псевдослучайному алгоритму (типа ЛКГ: I = (A*I) mod C
). Сначала первая фаза делает это над своим массивом, потом вторая фаза делает ровно то же самое над своим — и так для каждого сообщения, коих int index = message.index; for( int i = 0; i < OPS_PER_MESSAGE; i++ ) { index = Math.abs( index * offset ) % DATA_SIZE; array[index] += amount; }
Интересный вопрос №1: если псевдослучайное скакание по массиву заменить на проходку с фиксированным шагом, то разница в производительности практически пропадает. Скорее даже, с небольшим перевесом, почти на грани погрешности, начинает выигрывать синхронный вариант. Почему так? Интересный вопрос №2: если отставить в сторону рассмотренный вариант — какие еще эффекты могут обеспечить преимущество "распараллеленному" варианту последовательного кода? P.S. Да, кстати — мы (Дойче банк) набираем людей. Кажется, 3-4 вакансии у нас сейчас открыты. Можно прямо мне и писать — а то у меня еще много интересных идей для собеседования есть :)
Ответ на вопрос No 1: включается префетчинг. Дело в том, что если мы случайно бродим по массиву, то вероятность кэш-мисса зависит от размера кэша.
ОтветитьУдалитьЕсли же включается префетчинг, то кэш-миссы практически отсутствуют.
Ага, точно. А что будет, если менять размер шага -- например, если взять шаг 1, 8, 256?
УдалитьЯ думаю, что если взять шаг 1 и 8, то префетчинг включится, т.к. в это случае каждый раз будет загружаться та же или следующая кэш-линия, а в случае 256 не включится т.к. будет загружаться не следующая кэш-линия, а с пропуском. Впрочем, я слышал, что бывают префетчеры, которые распознают постоянный шаг, так что все зависит от конкретного железа.
ОтветитьУдалитьЧто касается второго вопроса, я могу представить себе ситуацию, когда на втором процессоре крутится тред, который делит ресурсы с нашей задачей.
Да, в принципе верно. Вот как раз интеловский префетчер распознает сканирование памяти с постоянным шагом до 2048 байт (кажется). Так что с шагом до 256*double префетчинг будет работать.
УдалитьА вот совсем любопытный вопрос(на этот раз я сам пока ответа не знаю): почему с некоторыми шагами (например 73, 81, 97, 105, 113, 121, 127, 129...) асинхронный вариант работает в разы медленнее (в 5-8 раз), чем с остальными? Синхронный же вариант, похоже, такого эффекта не демонстрирует.