Size: a a a

WebAssembly — русскоговорящее сообщество

2021 March 14

AT

Alexander Tchitchigi... in WebAssembly — русскоговорящее сообщество
Богдан
У меня делитель не константа, точнее его можно сделать константой но я подумал что это сильно увеличит объем горячих данных - когда делаем сканлайн-растеризацию текущей пиксельной строчки экрана и делаем цикл по массиву линий читая их коэффициенты и вычисляем покрытие пикселя то на объеме > 1k линий на строку дополнительные 4 байта флоата для каждой линии дадут заметный расход по bandwidth и будет хуже использоваться l1-l2 кеш.  
У меня получается что нужно разделить одно целое число на второе и результатом их деления будет третье целое число (которое будет в промежутке 0-255 то есть цвет пикселя)
Если Вы по массиву линий идёте только в одну сторону и не возвращаетесь, то лишние 4 байта ни на чём сказаться не должны. Про memory prefetch Вы уже тоже прочитали?
источник

Б

Богдан in WebAssembly — русскоговорящее сообщество
Alexander Tchitchigin
Я ничего не понимаю: Вы постоянно смешиваете целочисленное и float деление, а это абсолютно разные операции в процессоре.
Можно и целочисленное деление, но я гуглил и вроде как i32/i64.div (я так понимаю это idiv в x86-64) не сильно быстрее флоатного деления, мне действительно нужно получить только целую часть результата деления целого числа которое в 0-255 раз больше второго (и соотвественно получить целый результат деления который будет в промежутке 0-255). То есть нужно получить только первые 8 бит результата. Например 736755/8437 = 87.324285... но мне нужно только значение 87 потому что конечный результат (цвет пикселя) кодируется одним байтом
источник

Б

Богдан in WebAssembly — русскоговорящее сообщество
Alexander Tchitchigin
Если Вы по массиву линий идёте только в одну сторону и не возвращаетесь, то лишние 4 байта ни на чём сказаться не должны. Про memory prefetch Вы уже тоже прочитали?
Да знаю я про префетч и про последовательное хранение/чтение данных в памяти (типа первое обращание грузит 64 байта в кеш а дальше мы считываем уже из кеша) но учитывая что вся линия будет кодироваться всего 8 байтами то лишние 4 байта для кеширования флоат-константы это 50% оверхеда по bandwidth и меньшей эффективности кеша
источник

DI

Dmitry Ilyin in WebAssembly — русскоговорящее сообщество
есть еще арифметика с фиксированной точкой, можно как вариант рассмотреть ее
источник

M

MaxGraey in WebAssembly — русскоговорящее сообщество
Богдан
Можно и целочисленное деление, но я гуглил и вроде как i32/i64.div (я так понимаю это idiv в x86-64) не сильно быстрее флоатного деления, мне действительно нужно получить только целую часть результата деления целого числа которое в 0-255 раз больше второго (и соотвественно получить целый результат деления который будет в промежутке 0-255). То есть нужно получить только первые 8 бит результата. Например 736755/8437 = 87.324285... но мне нужно только значение 87 потому что конечный результат (цвет пикселя) кодируется одним байтом
целочисленное деление и деление с плавающей точкой имеют совршенно разные алгоритмы и скорости. Целочисленное действительно медлненное для общего случая если только у вас не Zen2/Zen3 архитектура, с плавающей же точкой деление не сильно медленее умножения с плавающей точкой
источник

Б

Богдан in WebAssembly — русскоговорящее сообщество
MaxGraey
целочисленное деление и деление с плавающей точкой имеют совршенно разные алгоритмы и скорости. Целочисленное действительно медлненное для общего случая если только у вас не Zen2/Zen3 архитектура, с плавающей же точкой деление не сильно медленее умножения с плавающей точкой
Разве? Везде пишут что деление примерно на порядок медленнее чем умножение (например тут есть подробный ответ https://stackoverflow.com/questions/4125033/floating-point-division-vs-floating-point-multiplication). Может не особо сильно по latency но более значительно по throughput а еще я помню из курса по процессорам что float alu-блоков обычно меньше чем целочисленных и вместо параллельного выполнения инструкций (out-of-order) будут лишние паузы. В общем все советуют выносить деление в виде константы 1/x за пределы горячего цикла (чтобы внутри цикла умножать на заранее вычисленное 1/x значение)
источник

M

MaxGraey in WebAssembly — русскоговорящее сообщество
Богдан
Разве? Везде пишут что деление примерно на порядок медленнее чем умножение (например тут есть подробный ответ https://stackoverflow.com/questions/4125033/floating-point-division-vs-floating-point-multiplication). Может не особо сильно по latency но более значительно по throughput а еще я помню из курса по процессорам что float alu-блоков обычно меньше чем целочисленных и вместо параллельного выполнения инструкций (out-of-order) будут лишние паузы. В общем все советуют выносить деление в виде константы 1/x за пределы горячего цикла (чтобы внутри цикла умножать на заранее вычисленное 1/x значение)
Ну относительно.
Деление примерно в 5-6 раз медленее чем умножение для floating points
А вот с целочисленным аж в 15-40 раз

Кстати компилятор оптимизирует случаи вроде x / 2.0 в x * 0.5 но только если делитель является степенью двойки
источник

AT

Alexander Tchitchigi... in WebAssembly — русскоговорящее сообщество
Богдан
Да знаю я про префетч и про последовательное хранение/чтение данных в памяти (типа первое обращание грузит 64 байта в кеш а дальше мы считываем уже из кеша) но учитывая что вся линия будет кодироваться всего 8 байтами то лишние 4 байта для кеширования флоат-константы это 50% оверхеда по bandwidth и меньшей эффективности кеша
Вы bandwidth неправильно считаете. С пайплайнингом и OoO execution оно не так будет работать. 😊
источник

Б

Богдан in WebAssembly — русскоговорящее сообщество
Dmitry Ilyin
есть еще арифметика с фиксированной точкой, можно как вариант рассмотреть ее
Я думал про это, но мы все равно приходим к плавающей запятой. Вычисления с фиксированной точкой тут не подходят потому что ошибка зависит от того насколько большой делитель (точнее коэффициент 1/x) - для маленьких чисел нужно хранить больше цифр после запятой а для больших - меньше. Я так прикинул - там даже 16-битного флоата будет много и можно еще меньше хранить. Ну а потом такая мысль - раз нам нужно получить только первые 8 бит результата (число в промежутке 0-255) то может достаточно просто выполнить ручное деление в столбик до тех пор пока не получим первые 8 бит и это если это будет не сильно затратно (несклько шифтов и сложений) то это будет идеальное решение
источник

Б

Богдан in WebAssembly — русскоговорящее сообщество
Alexander Tchitchigin
Вы bandwidth неправильно считаете. С пайплайнингом и OoO execution оно не так будет работать. 😊
Ну если у нас затык в вычислениях (alu) то разницы по скорости действительно не будет. Но если вычислений наоборот немного то процессор начнет ждать загрузку из кеша или памяти и соотвественно чем более компактно уложим данные в памяти тем больше вычислений сможем проделать после загрузки данных и меньше будем ждать загрузки следующей порции данных
источник

AT

Alexander Tchitchigi... in WebAssembly — русскоговорящее сообщество
Богдан
Ну если у нас затык в вычислениях (alu) то разницы по скорости действительно не будет. Но если вычислений наоборот немного то процессор начнет ждать загрузку из кеша или памяти и соотвественно чем более компактно уложим данные в памяти тем больше вычислений сможем проделать после загрузки данных и меньше будем ждать загрузки следующей порции данных
Процессор в любом случае будет ждать память, поэтому разница будет не в разы, а на проценты.
источник

AT

Alexander Tchitchigi... in WebAssembly — русскоговорящее сообщество
Ну и ещё, чисто для ясности: из четырёх integer ALU на ядро, делить обычно умеет только один. Если FP ALU больше одного, то распараллеливаться будут как раз только FP деления (на одном ядре).
источник

Г

Георгий in WebAssembly — русскоговорящее сообщество
а скорость целочисленного деления от чего зависит?
источник

M

MaxGraey in WebAssembly — русскоговорящее сообщество
Георгий
а скорость целочисленного деления от чего зависит?
От шинины регистра и последнего и позиции дидирующей единицы для делителя и делимого. Но это только для архитектур отличных от Zen2/Zen3. В ней используется новый алгоритм который не зависит от этого
источник

Б

Богдан in WebAssembly — русскоговорящее сообщество
Кстати а никто не видел скоростного вычисления обычного квадратного корня? А то для 1/sqrt(x) есть так называемый "fast inverse square root" хак а для обычного корня ничего не гуглится
источник

Б

Богдан in WebAssembly — русскоговорящее сообщество
Еще один интересный вопрос - умеют ли процессоры оптимизировать тривиальные значения (1, -1 и 0) для медленных операций деления и вычисления квадратного корня ? Понятно что если это будут константы в коде то сам компилятор это поудаляет но что если это динамические значения из памяти которые известны только в рантайме? Есть ли в процессоре в каком-нибудь instruction decoder блоке проверка что если делитель равен 1 то ничего не делаем, если -1 то меняем знак а если 0 то подставляем Infinity-константу (ну и для всех остальных значений будет медленное выполнение деления). И то же самое с корнем - если sqrt(1) или sqrt(0) то подставляем соответственно 1 или 0
источник

MV

Mikhail Voronov in WebAssembly — русскоговорящее сообщество
Богдан
Народ, столкнулся тут с интересной темой. В интернете пишут что деление процессор выполняет на порядок медленнее чем умножение (плюс float alu-модулей обычно меньше чем целочисленных а значит будут stalls то есть процессоры будут ждать или хуже параллелить инструкции в своем out-of-order выполнении) Но что если мне нужно получить не все то 64-битное флоатное число а только первые 8 бит результата деления одного целого числа на второе? Я уверен тут можно сильно ускорить с помощью какого-то кастомного деления в столбик когда мы получаем только первые 8 бит (скорее всего это несколько шифтов с промежуточными инкрементами) Никто не встречал случайно подобный алгоритм?
а эти 8 бит должны быть точными или могут быть некоторым приближением?
источник

MV

Mikhail Voronov in WebAssembly — русскоговорящее сообщество
если второе, то можно воспользоваться теоремой об отображении и неподвижной точке и аппроксимировать y = sqrt(x) параболой и по ней сходиться кваратично к решению
источник

MV

Mikhail Voronov in WebAssembly — русскоговорящее сообщество
(в советском ядерном проекте так собственно и делали)
источник

MV

Mikhail Voronov in WebAssembly — русскоговорящее сообщество
Mikhail Voronov
если второе, то можно воспользоваться теоремой об отображении и неподвижной точке и аппроксимировать y = sqrt(x) параболой и по ней сходиться кваратично к решению
а тут при расчёте будет только умножение и вычитание без деления
источник