Оптимальный способ хранения двойных SSE2/AVX/AVX512 как плавающих с использованием встроенных функций
Мне часто нужно использовать double по соображениям точности, но я хочу сохранить результаты как плавающие. Какой оптимальный способ? Я в настоящее время использую:
SSE2: _mm_store_sd((double*)dst, _mm_castps_pd(_mm_cvtpd_ps(xmm)));
AVX: _mm_storeu_ps(dst, _mm256_cvtpd_ps(ymm));
AVX512: _mm256_storeu_ps(dst, _mm512_cvtpd_ps(zmm));
Есть идеи по улучшению?
1 ответ
Преобразование из упакованного двойного в упакованное плавание доступно только в сужающейся форме, но не в версии, которая принимает 2 вектора двойного и упаковывает в 1 вектор плавания. Так что да, присущие [v]cvtpd2ps
ваш единственный вариант. Эти инструкции декодируют до 2 мопов на современном Intel; один для порта (ов) FMA и один для порта в случайном порядке. ( https://agner.org/optimize/)
Сохранение результата является простым, некоторая форма _mm_store/storeu
это то, что вы хотите.
Для 128-битных векторов (в результате 2x float
= 64 бита), у вас нет целого 128-битного вектора результатов. Вы можете перетасовать два вместе в 128-битный вектор, но с пропускной способностью FP, равной 1 за такт на Intel, начиная с Sandybridge, вероятно, лучше всего хранить их оба отдельно.
Ты хочешь movlps
вместо movsd
хранить младшие 64 бита float
вектор; Он сохраняет один байт инструкции, а для встроенной функции C требуется меньше приведения. Но, к сожалению, это занимает __m64*
вместо float*
так что вам все еще нужен один актерский состав:
_mm_storel_pi((__m64*)dst, _mm_cvtpd_ps(xmm) );
Но для загрузки, вы определенно хотите movsd
чтобы избежать ложной зависимости от старого значения. movlps
нагрузки сливаются в реестр; movsd
загружает ноль-удлинить. На самом деле, cvtps2pd xmm, qword [mem]
позаботится об этом за вас, если вы можете заставить компилятор испускать это из встроенных функций.
Это может быть трудно сделать это безопасно, по аналогичным причинам pmovzxbw xmm, qword [mem]
: компиляторам не удается сложить загрузку qword в операнд памяти для pmovzx/sx: ( загрузка 8 символов из памяти в переменную __m256 как упакованные числа с плавающей запятой одинарной точности)