32-1024-битная векторная арифметика с фиксированной точкой с AVX-2

Для генератора Мандельброта я хочу использовать арифметику с фиксированной точкой от 32 до, возможно, 1024 бит при увеличении масштаба.

Теперь нормальные SSE или AVX здесь не помогают из-за отсутствия добавления с переносом, а выполнение обычной целочисленной арифметики происходит быстрее. Но в моем случае у меня есть буквально миллионы пикселей, которые все нужно вычислить. Итак, у меня есть огромный вектор значений, которые все должны повторять одну и ту же итеративную формулу снова и снова миллион раз.

Поэтому я не собираюсь выполнять добавление / суб / муль с фиксированной точкой для отдельных значений, а делаю это для огромных векторов. Я надеюсь, что для таких векторных операций AVX / AVX2 все еще можно будет использовать для повышения производительности, несмотря на отсутствие встроенного добавления с переносом.

Кто-нибудь знает библиотеку для арифметики с фиксированной запятой на векторах или какой-то пример кода, как эмулировать добавление с переносом AVX / AVX2.


person Goswin von Brederlow    schedule 12.11.2019    source источник


Ответы (2)


Расширенная точность FP дает больше битов за такт (поскольку double пропускная способность FMA равна 2 / такт против 32x32 => 64-битных при 1 или 2 / такт на процессорах Intel); рассмотрите возможность использования тех же приемов, которые Prime95 использует с FMA для целочисленной математики. С осторожностью можно использовать оборудование FPU для работы с точными числами.


Для вашего фактического вопроса: поскольку вы хотите сделать то же самое для нескольких пикселей параллельно, возможно, вы захотите выполнить перенос между соответствующими элементами в отдельных векторах, поэтому один __m256i содержит 64-битные фрагменты из 4 отдельных больших целых чисел, а не 4 фрагмента такое же целое число.

С этой стратегией давление регистров является проблемой для очень широких целых чисел. Возможно, вы можете с пользой перейти к отсутствию распространения переноса за 4-й или 6-й вектор фрагментов или что-то в этом роде, используя vpmovmskb в результате сравнения для генерации переноса после каждого добавления. Беззнаковое добавление имеет выполнение a+b < a (беззнаковое сравнение)

Но AVX2 имеет только целочисленные сравнения со знаком (больше чем), а не беззнаковые. А с переносом (a+b+c_in) == a возможно с b = carry_in = 0 или с b = 0xFFF ... и carry_in = 1, поэтому создание переноса непросто.

Чтобы решить обе эти проблемы, рассмотрите возможность использования фрагментов с ручным переносом в 60-битный или 62-битный или что-то в этом роде, чтобы они гарантированно были подписаны положительно, и поэтому выполнение из добавления появляется в старших битах полного 64-битного кода. битовый элемент. (Где вы можете vpsrlq ymm, 62 извлечь его для добавления в вектор следующих более высоких фрагментов.)

Возможно, здесь будут работать даже 63-битные фрагменты, поэтому перенос отображается в самом верхнем бите, и vmovmskpd может проверить, произвел ли какой-либо элемент перенос. В противном случае vptest сможет сделать это с правильной маской.


Это удобный вариант мозгового штурма; У меня нет планов расширять его до подробного ответа. Если кто-то хочет написать реальный код на основе этого, опубликуйте свой собственный ответ, чтобы мы могли проголосовать за него (если это вообще окажется полезной идеей).

person Peter Cordes    schedule 12.11.2019
comment
Я действительно думал об использовании 64-битных блоков из 4 отдельных биг-целых чисел. Вы должны выполнить перенос по пульсации, чтобы 4 фрагмента одного и того же целого числа не распараллеливались. - person Goswin von Brederlow; 12.11.2019
comment
Бигнумы малого и среднего размера сосут SIMD. С AVX512-IFMA они отстой немного меньше, но все равно отстой. Ничего хорошего, пока вы не попадете в страну БПФ. - person Mysticial; 13.11.2019
comment
@Mysticial: Хм, да, add и sub выглядят как безубыточные с AVX2 для нескольких bignum параллельно. Необходимость распространения переноса вручную делает стоимость добавления, вероятно, в 2 раза vpaddq (a + b + перенос) + vpsrlq (генерировать перенос) + vpand (удалить перенос из оригинала). Но это дает вам не более 63 бит на конечность по сравнению с однократным adc для 64 бит на единицу. Чередование скалярных цепочек деплоя с помощью adc позволяет выполнять чередование OoO exec. С AVX512 вы получаете в два раза больше работы на вектор, но 4 / такт adc против 2 / такт 512-битных векторных вещей больно. Тем не менее, регистр давления - вещь для скалярных - person Peter Cordes; 13.11.2019
comment
И это в лучшем случае. Умножение ужасно по сравнению со скалярным, где mul или mulx производят 128 бит продукта за цикл с одним мупом, оставляя остальную внутреннюю полосу пропускания свободной для adc частей. - person Peter Cordes; 13.11.2019
comment
(Гадкий) трюк в том, чтобы довести представление о неполном слове до крайности. Вместо 60 или 62, которые вы предлагаете, вы полностью опускаетесь до чего-то ниже 52 бита и помещаете все в DP-float. Затем вы можете использовать оборудование FMA, чтобы эффективно получить весь результат умножения размером в слово. Уменьшение числа битов ниже 52 позволит вам игнорировать / откладывать перенос при сложении + вычитании, включая те, которые необходимы внутри большого умножения. AVX512-IFMA позволяет вам оставаться на целочисленной стороне с ровно 52-битными словами, так как теперь вы можете использовать все 64-битное целое число для переполнения. - person Mysticial; 13.11.2019

Просто для удовольствия, не утверждая, что это действительно будет полезно, вы можете извлечь бит переноса дополнения, просто посмотрев на верхние биты входных и выходных значений.

unsigned result = a + b + last_carry;  // add a, b and (optionally last carry)

unsigned carry = (a & b)  // carry if both a AND b have the upper bit set
                 |        // OR
                 ((a ^ b) // upper bits of a and b are different AND
                  & ~r);  // AND upper bit of the result is not set
carry >>= sizeof(unsigned)*8 - 1; // shift the upper bit to the lower bit

С SSE2 / AVX2 это может быть реализовано с двумя добавками, 4 логическими операциями и одним сдвигом, но работает для произвольных (поддерживаемых) целочисленных размеров (uint8, uint16, uint32, uint64). С AVX2 вам понадобится 7uops, чтобы получить 4 64-битных дополнения с выносом и выносом.

Тем более, что умножение 64x64-->128 также невозможно (но потребует 4 32x32-->64 произведения - и некоторых дополнений или 3 32x32-->64 произведений и даже больше добавлений, а также обработка особых случаев), вы, вероятно, не будете более эффективны, чем с mul и adc ( возможно, если только давление в регистре не является вашим узким местом).

Как предположили Питер и Мистикал, работа с конечностями меньшего размера (все еще хранящимися в 64-битном формате) может быть полезной. С одной стороны, с помощью некоторых уловок можно использовать FMA для 52x52-->104 продуктов. Кроме того, вы можете сложить до 2 ^ k-1 чисел из 64-k бит, прежде чем вам понадобится переносить верхние биты предыдущих конечностей.

person chtz    schedule 28.11.2019