bomzzz

Platinum Member | Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору если я из 512 кбайт, забью 128 байт процессор не обидится Добавлено: Цитата: Предварительная загрузка(Prefetching) Следующая оптимизация более тонкая. В добавление к SIMD инструкциям, SSE предоставляет инструкции, которые дают вам возможность управления обменом данными с кэшем. Intel SSE учебник предоставляет полное описание этих инструкций, но нас сейчас интересует только одна - prefetchnta инструкция. Она позволяет загружать(pre-load) адрес в специальную не временную(non-temporal) область L1 кэша, которая гарантирует, не заменять любые данные, загруженные в кэш. Мы используем prefetchnta чтобы указать, что нам нужно загрузить данные по определенному адресу в кэш, по которому можно читать (или писать). Именно так нам и надо обращаться с потоками входных и выходных векторов - нам надо, чтобы вектор находился в кэше пока мы читаем из него (или замписываем). После нам надо, чтобы его НЕ БЫЛО, поэтому у нас есть много пространства в кэше для остальных векторов. Вставим пару prefetchnta инструкций в BatchMultiply - одна для входного потока, и вторая - для выходного: // BatchMultiply4 -- Модификация BatchMultiply3, использующая // SSE prefetching инструкции для ускорения обработки больших // наборов входных данных. // // Выполнение: 21 цикл/вектор void BatchMultiply4(Matrix4f &m, Vector4f *vin, Vector4f *vout, int len) { // транспонировать матрицу в xmm4-7 m.TransposeIntoXMM(); static const int vecSize = sizeof(Vector4f); __asm { mov esi, vin mov edi, vout mov ecx, len BM4_START: // загрузить следуюший входной вктор в xmm0, и передвинуть // входной указатель . Загрузить остальные(upcoming) векторы // в кэш. movaps xmm0, [esi] prefetchnta [esi+0x30] // записать y в xmm1, z в xmm2, и w в xmm3 (оставляя // x в xmm0). movaps xmm1, xmm0 add esi, vecSize movaps xmm2, xmm0 add edi, vecSize movaps xmm3, xmm0 prefetchnta [edi+0x30] shufps xmm0, xmm0, 0x00 shufps xmm1, xmm1, 0x55 shufps xmm2, xmm2, 0xAA shufps xmm3, xmm3, 0xFF // умножить xmm0-3 на соответствующие столбцы матрицы // (hiding a pointer increment between the multiplies) mulps xmm0, xmm4 mulps xmm1, xmm5 mulps xmm2, xmm6 mulps xmm3, xmm7 // суммировать результаты в xmm1 addps xmm1, xmm0 addps xmm2, xmm3 addps xmm1, xmm2 // записать результаты в vout и продолжить // выполнение цикла movaps [edi-0x10], xmm1 dec ecx jnz BM4_START } } Во-первых, выгода от использования загрузки в кэш не сильно видна - мы сэкономили всго один цикл! Но, как только мы начнем использовать большие потоки векторов (примерно 10000), эта версия функции будет работать заметно быстрее, чем предыдущая. Можете поэкспериментировать, чтобы найти подходящее значние смещения prefetching'а, которое будет наилучшим для вашего приложения (для Pentium 3 48 байт подходит лучше всего). Еще может быть полезным использование movntps инструкции, используемой для записи содержимого SSE регистров напрямую в память минуя кэш (т.о. избегая загрязнения кэша). Более полезную информацию смотрите в учебнике Intel. Чтобы увидеть результат использования prefetching'а, запустите simdtest.exe программу с -f флагом. Этот флаг очищеят кэш перед каждым тестом. Вы увидите, prefetching может удвоить скорость вашего кода в наихудшем случае. Это легко запоминающийся способ оптимизации, при том, что prefetching команды могут быть использованы даже в не-SIMD коде! |
| Всего записей: 13343 | Зарегистр. 13-01-2008 | Отправлено: 19:03 28-08-2012 | Исправлено: bomzzz, 19:09 28-08-2012 |
|