Армянское радио Нас спрашивают:
Как измениться производительность intrinsic варианта на Core-i7, если поменять
_mm_dp_ps на _mm256_dp_ps
_mm_blend_ps на _mm_blend256_psТо-есть насколько вырастить производительность если мы совсем на AVX переедем и будет обрабатывать по 8 float за проход? А то слухи разные ходят... от 0% до 200% роста.
Отвечаем:
Если игнорировать возможную нечетность размера данных, то код получается таким:
- ALIGN1(32) float matX2[3][8] ALIGN2(32) = {
- { 0.17f,0.22f,0.33f,0.44f,0.17f,0.22f,0.33f,0.44f},
- {0.55f,0.66f,0.77f,0.88f,0.55f,0.66f,0.77f,0.88f},
- {1.01f,1.02f,1.03f,1.04f,1.01f,1.02f,1.03f,1.04f}
- } ;
- void dotp_avx(float data[], int sz)
- {
- int i;
- __m256 m0 = _mm256_load_ps(matX2[0]);
- __m256 m1 = _mm256_load_ps(matX2[1]);
- __m256 m2 = _mm256_load_ps(matX2[2]);
- for(i=0;i<sz/2;i++)
- {
- __m256 d0 = _mm256_load_ps(&data[i*8]);
- _mm_prefetch((const char*)&data[i*8+16],_MM_HINT_NTA);
- __m256 r0 = _mm256_dp_ps(d0,m0,0xf1);
- __m256 t0 = _mm256_dp_ps(d0,m1,0xff);
- __m256 t1 = _mm256_dp_ps(d0,m2,0xff);
- r0 = _mm256_blend_ps(r0,t0,0x22);
- r0 = _mm256_blend_ps(r0,t1,0x44);
- _mm256_stream_ps(&data[i*8],r0);
- }
- }
Да, для удобства родились такие вот макросы, используемые выше:
- #if defined(_MSC_VER) || defined(__INTEL_COMPILER)
- #define ALIGN1(v) __declspec(align(v))
- #define ALIGN2(v)
- #else /* GCC */
- #define ALIGN1(v)
- #define ALIGN2(v) __attribute__((aligned(v)))
- #endif
Результаты
- Без _mm_prefetch: 625 Mpix/sec
- С _mm_prefetch: 637 Mpix/sec
Другими словами, в 1.36 раза быстрее, чем было с SSE4.1 на Intel C++ и в 1.14 раза быстрее, чем для MSVC.
Comments
а разве они уже умеют 1 блоком делать операцию над 256 бит в
а разве они уже умеют 1 блоком делать операцию над 256 бит вектором за раз ?
Вроде ж писали, что регистры по 256, но каждую половинку по факту обрабатывает свой блок.
Ну так а мне, по счастью, и не надо 256 бит за раз, у меня с
Ну так а мне, по счастью, и не надо 256 бит за раз, у меня скалярное умножение двух векторов длиной 4
И _mm256_dp_ps() делает именно это - умножает зараз две половинки.
На моём i7 2670QM (2GHz) этот код выдаёт 475 MPix/sec если д
На моём i7 2670QM (2GHz) этот код выдаёт 475 MPix/sec если данные в L2, 450 MPix/sec если данные в L3, и 310 MPix/sec если данные в памяти
Попробуйте погонять вот этот код (компилировать Nasm'ом) <co
Попробуйте погонять вот этот код (компилировать Nasm'ом)
Попробую, но пока не понимаю что он делает, тем паче что пер
Попробую, но пока не понимаю что он делает, тем паче что передача параметров через регистр с инфраструктурой C++ как-то плохо совместима, ну да придумаю что-нибудь.
Calling convention стандартная для Windows x64. В ней первые
Calling convention стандартная для Windows x64. В ней первые четыре параметра передаются в регистрах
Я что про это хочу сказать (пройдя в отладчике и поняв) 1)
Я что про это хочу сказать (пройдя в отладчике и поняв)
1) Идея отличная, работать должно быстро.
Но
2) То что мы 16 входных значений упаковали в 12 выходных - это для каких-то применений хорошо, а для последующей обработки в FP - категорически неудобно. Поэтому в моих реализациях 4-й компонент выхода обнулялся и это было спецально.
3) Конкретно в этом коде кажется есть ошибка (проверил дважды, вроде в _mm я все правильно перенес) от которой в ymm4 на выходе неправильно.
2) Сделать код, который будет выдавать 16 выходных значений
2) Сделать код, который будет выдавать 16 выходных значений гораздо проще, но я оптимизировал код из этого поста, который выдаёт три компоненты
3) Что-то я не вижу ошибки. Комментарии к коду добавил здесь
Не, тот код меняет 3 компоненты, а 4-ю просто оставляет как
Не, тот код меняет 3 компоненты, а 4-ю просто оставляет как есть. В том коде просто нету внешнего цикла, который img+=4
А вот тут он есть, ну да не суть.
Что касается ошибки, то я просто накормил повторяющимися входными данными (1,2,3,4) и ожидал увидеть повторяющиеся выходные, а в ymm4 было другое. Возможно, я просто неаккуратно перенес (хотя проверил).
Хрен с ним, идея что можно горизонтально складывать для скалярного произведения - понятна, а остальное неважно.
Ок, вот <a href="http://pastebin.com/rbpdgEyz">новая версия<
Ок, вот новая версия для 4-х компонентных пискелей на выходе. Теперь
банановыйещё быстрееАга, пощупаю. А действительно работать в два потока "с нача
Ага, пощупаю.
А действительно работать в два потока "с начала до середины" и "с середины до конца" - быстрее?
На моём ноуте быстрее, хотя ненамного (8700 MB/s vs 8500 MB/
На моём ноуте быстрее, хотя ненамного (8700 MB/s vs 8500 MB/s) и я не уверен, что разница статистически значима. В общем случае это зависит от множества параметров (сколько массивов данных читает/пишет программа, сколько буферов загрузки и записи есть в процессоре, сколько cache misses может одновременно обрабатывать контроллер кэша, сколько открытых DRAM страниц может держать контроллер памяти, и т.д.). Я как раз делаю ресёрч на эту тему
Ого! 1032Mpix/sec (16.5gb/sec) Это не развернутый вариант,
Ого!
1032Mpix/sec (16.5gb/sec)
Это не развернутый вариант, а по 8 значений (один регистр) за раз, in place (пишем откуда читали) и без префетча. Для сравнения с другими.
И не ассемблер, а intrinsics, хотя разницы быть не должно.
Не, я в первый раз неправильно померял (в инварианте цикла о
Не, я в первый раз неправильно померял (в инварианте цикла ошибся).
725Mpix/sec. Быстрее чем все что раньше, но и только.
Post new comment