Pull to refresh
179
0.4

Человек

Send message
ваша ассемблерная реализация не является эквивалентом double-double, потому что вы работаете с 80-битными числами, а не 128-битными. Тут нечего сравнивать.
double-double — это невычисленная сумма двух 64-битных чисел, а не одно 128-битное. Какой формат используется для промежуточных вычислений — не имеет значения, пока они укладываются в заданные погрешности.
Нет, не аргумент. Вы упомянули задачу вычисления FFT. Но FFT — устойчивое преобразование, в нём не возникает больших чисел.
А ещё я упоминал кепстр, которой считается как FFT от логарифма FFT — поэтому при обратном преобразовании небольшие погрешности как раз и могут приводить к очень большим числам.

Поэтому преимущество от posit-ов вы можете получить за счёт того, что у них больше бит отводится мантиссе для рабочих диапазонов значений чисел.
Вполне может быть — возможное преимущество от posit-ов я не отрицаю.
Осталось понять только почему на HPC об этом никто не плачет и спокойно на GPU всё считают
Очевидно потому, что с такими задачами и алгоритмами не сталкивались — я же выше два конкретных примера привёл, и изучать тему тоже не от нечего делать начал. Требования к точности FFT принципиально отличаются в зависимости от того, используется оно для визуализации спектра или же для кепстральных преобразований. Для визуализации поворота вектора в 2D и single может быть избыточен.
Ваша «оптимизация» кода заключается лишь в одном: вы использовали аппаратную работу с 80-битными числами вместо программной работы со 128-битными.
Было бы неплохо кавычки в слове «оптимизация» обосновать пруфом на бенчмарк, в котором программная реализация 128-бит флоат будет работать быстрее. Таки концепция double-double не моя, моя лишь ассемблерная реализация с FPU.

Если же у вас есть необходимость работы только с 32- и 64-битными числами, то про FPU действительно стоит забыть.
Пример выше с переполнением до inf по-прежнему не аргумент? И входные, и выходные данные укладываются в 64 бит. Тут же половина комментариев по профиту posit-а упирает на то, что он в inf не вываливается.
Вот более приземлённый каноничный пример:
double z = 1e+200;
double zzz = sqrt(z*z + z*z);
cout << "default: " << zzz << endl;

__asm
{
	fld z
	fmul st,st
	fadd st,st
	fsqrt
	fstp zzz
}
cout << "FPU: " << zzz << endl;

Результат:
default: inf
FPU: 1.41421e+200
когда вместо HADDPD/HSUBPD/etc используется, зачем-то, 8087
Очевидно, зачем — чтобы не терять точность.

Которые можно было бы точно так же вычислить не гоняя данные через память и не задействуя 8087.
Можно было — только по времени подольше выйдет. Я же ссылку на оригинальный код зачем давал? К чему столько вычислений, подробно расписано например вот в этой работе — и в первую очередь стоит обратить внимание на разницу в реализации Two-Sum и Quick-Two-Sum, в котором первое слагаемое должно быть больше второго по модулю.

Впрочем, вполне возможно, что и я, и авторы оригинального кода что-то упустили — поэтому буду безмерно благодарен за конкретную более быструю реализацию.
И как, собственно, вы применяли FPU?
Вот так
add_dd_dd PROC uses esi edi dd1:PTR DWORD, dd2:PTR DWORD, dd3:PTR DWORD;[+]
local s1s:XMMWORD, s2s:XMMWORD

mov esi, dd1
mov edi, dd2
mov eax, dd3

movupd xmm0, XMMWORD PTR [esi];=a
movupd xmm1, XMMWORD PTR [edi];=b

movapd xmm3, xmm0;=sum
addpd xmm3, xmm1
movapd xmm4, xmm3;=bv
subpd xmm4, xmm0
movapd xmm5, xmm3;=av
subpd xmm5, xmm4
subpd xmm1,xmm4;=br
subpd xmm0,xmm5;=ar
addpd xmm0, xmm1;=err

movupd XMMWORD PTR s1s, xmm3
movupd XMMWORD PTR s2s, xmm0

fld REAL8 PTR s1s
fadd REAL8 PTR s1s+8
fadd REAL8 PTR s2s
fadd REAL8 PTR s2s+8
fstp REAL8 PTR [eax]

fld REAL8 PTR [eax]
fsubr REAL8 PTR s1s
fadd REAL8 PTR s1s+8
fadd REAL8 PTR s2s
fadd REAL8 PTR s2s+8
fstp REAL8 PTR [eax+8]

ret
add_dd_dd ENDP


mul_dd_dd PROC uses esi edi dd1:PTR DWORD, dd2:PTR DWORD, dd3:PTR DWORD
local t1:XMMWORD, t2:XMMWORD
mov esi, dd1
mov edi, dd2
mov eax, dd3
movupd xmm7, XMMWORD PTR _split64;=split
;
; product - x
fld REAL8 PTR [esi]
movsd xmm0, REAL8 PTR [esi];=a
fld REAL8 PTR [esi+8]; |a|a`|
movhpd xmm0, REAL8 PTR [edi];загрузка в старший разряд ;=b
fmul REAL8 PTR [edi]; |a|a`*b|
movsd xmm3, xmm0
fld REAL8 PTR [edi+8]; |a|a`*b|b`|
mulsd xmm3, REAL8 PTR [edi];=x=a*b
fmulp st(2),st(0); |a*b`|a`*b|
movsd REAL8 PTR [eax], xmm3
faddp st(1),st(0); |a*b`+a`*b|

; split
movapd xmm1,xmm0;=a, b
mulpd xmm1, xmm7;=ca, cb
movapd xmm2, xmm1;=ca, cb
subpd xmm2, xmm0;=abig, bbig
subpd xmm1, xmm2;=ahi, bhi
subpd xmm0, xmm1;=alo, blo

; перестановка чисел между двумя регистрами
movapd xmm2, xmm1;= ahi, bhi
unpcklpd xmm2, xmm0;= ahi, alo
unpckhpd xmm1, xmm0;= bhi, blo
; парное умножение
movapd xmm0, xmm2;= ahi, alo
mulpd xmm0, xmm1;= ahi*bhi, alo*blo
shufpd xmm1, xmm1, 1; перестановка чисел ;= blo, bhi
mulpd xmm1, xmm2;= ahi*blo, alo*bhi
; финальное вычитание
subsd xmm3, xmm0;err1
subsd xmm3, xmm1;err2
shufpd xmm0, xmm0, 1
shufpd xmm1, xmm1, 1
subsd xmm3, xmm1;err3
subsd xmm0, xmm3;y
movsd REAL8 PTR [eax+8], xmm0
; сложение с FPU
fadd REAL8 PTR [eax+8]
fstp REAL8 PTR [eax+8]

ret
mul_dd_dd ENDP

Понятно, что это всё микробенчмарки и в реальных вычислениях всё может быть не совсем так
В реальных вычислениях всё совершенно точно будет не так — потому что данные для вычислений из памяти надо сначала считать, а результаты — записать обратно. И один-единственный промах кэша ценой в 1000 тактов аннулирует все микро-оптимизации на корню. Достаточно взять чуть более сложный, чем линейное суммирование элементов массива алгоритм, и разницы в скорости между FPU-only и SSE/AVX уже не будет.
То есть получить выигрыш от использования смеси 8087 и SSE кода не получится — а вот гемор получить можно изрядный. Проще всего про 8087 просто забыть и всё.
Искренне не понимаю, откуда такое неприятие — точно также и про SSE можно забыть, потому что AVX есть. Я в частности успешно применял смесь FPU и SSE для оптимизации double-double арифметики — получив двойное ускорение на сложение и десятикратное на умножение (по сравнению с си++ кодом отсюда).

все известные мне люди, которые умеют что-то оптимизировать в один голос говорят: перед тем, как заниматься оптимизациями — убедитесь что у вас в программе нет 8087 инструкций. То есть вот это — абсолютно железное правило.
Я тоже люблю и (возможно) умею оптимизировать — одна их моих ассемблерных оптимизаций получила 40-кратное по сравнению с си, но вашей точки зрения категорически не разделяю. Вроде бы наоборот — максимальное оптимизация и достигается использованием всех доступных команд и ресурсов.
Ну и как вы собираетесь его делать на каких-нибудь ARM'ах?
А причём тут ARM-ы? Платформ всяких разных множество существует, в том числе и без поддержки плавающей точки вообще в принципе.

В том-то и дело, что 80-битная точность — это удел маргиналов. Статья вообще говорит о том, что и 64 бита — не так уж нужны.
Ну это только до тех пор, пока вы лично не столкнётесь с ситуацией, когда точности double катастрофически не хватает. Ещё один пример навскидку — аппроксимация методом наименьших квадратов.

Но если реально сильно приспичило — есть же __float128. Да, он «ручками» реализован, но за счёт того, что x87 сейчас реализуют «по остаточному принципу»… может и быстрее 80-битного типа оказаться…
А я слышал (источник не приведу, в какой-то из специализированной литературе по низкоуровневой оптимизации), что и FPU, и SSE используют один и тот же модуль вычислений. Как минимум эксперимент на доступных PC показал, что FPU и SSE не распараллеливаются, а в единичных операциях SSE ничуть не быстрее.
Поясните пожалуйста, а почему в вашей дискуссии FPU и SSE рассматриваются на равных, в то время как FPU поддерживает расширенную точность (80 бит), а SSE — нет? Это имеет критическое значение, в частности, при реализации FFT произвольного размера (бо́льших 32K) алгоритмом Блюстейна.
Усматриваю в вашей статье некоторые неточности.

1) «Белый шум» не равно «набор случайных данных». «Белый» значит равномерный спектр, «шум» значит нежелательный сигнал — и в его роли вполне могут выступать детерминированные сигналы — наводки от электросети или сигнал в соседней полосе частот (в радиоэфире)

2) как только в последовательности случайных чисел появятся закономерности — они перестанут быть случайными в смысле «недетерминированности» по определению.

3) в статистике можно посчитать вероятность чего угодно, вот только — сначала нужно доказать, что исходный набор данных случаен и независим. Одно дело — рассматривать идеальные математические модели в теории, и совсем другое — на практике, поскольку процессы, обеспечивающие случайные на вид данные — не случайны. Достаточно взять монету со смещённым центром тяжести — и всё, статистика поломается.

4) картинка в начале — не белый шум, а нечто, на него похожее, сгенерированное конкретным алгоритмом. И появление на ней геометрических объектов зависит не от подсчитанной вероятности, а от особенностей конкретно этого ГПСЧ. Если, например, посмотреть на шум по правилу 30 — то легко увидеть «статистические аномалии» в виде треугольников, причём повёрнутых строго в одну сторону и размерами не больше определённого:
image
Наивно ждать от него чёрных квадратов — но тесты на случайность этот ГПСЧ вполне проходит.
Насколько я знаю, задержку чисто в аналоге делать довольно проблематично. Её можно аппроксимировать через последовательность точно рассчитанных allpass-фильтров, либо использовать готовые микросхемки типа таких. Но не понимаю, к чему такие сложности, если эта задача элементарно решается программно.
И ещё вот (что кстати на PC-платформе не имеет смысла, потому что там есть FPU с 80-битной точностью для промежуточных вычислений).
Посмотрите, как организован внутренний цикл, тот, что выполняется 4096 раз: ни одного вызова функций sin() или cos(), хотя в других реализациях эти вызовы будут.
В других реализациях этих вызовов тоже нет — там используются предварительно подсчитанные табличные данные. Более того — трюк с последовательным поворотом вектора для вычисления FFT хорошо известен и в частности используется в книге «Numerical Recipes» (страница 612 в третьем издании).
Вы забыли упомянуть, что то, что фитотерапевтические препараты по сути не являются действительно гомеопатическими, тоже ещё не делает их действительно эффективными. Ну а то, что гомеопатия = мошенничество очевидно следует из понимания того, что препараты лечат не волшебным образом, а посредством цепочки химических реакций; и для возникновения химической реакции активного вещества нужно чуть больше, чем одна молекула.
Статья выглядит так, как будто автор не знаком с комплексными числами.
К слову о гомеопатиях. Гомеопатические препараты мне выписывали:

1) Стоматолог после имплантации зубов;
2) ЛОР для после-больничного долечивания на дому;
3) Уролог для решения пикантной чисто мужской проблемы.

Поэтому лично я чётко отделяю «научную медицину» от «практикующих врачей». И соответственно, точно также — «классические оздоровительные практики» от «народных целителей».
Совершенно точно. В этом можно убедиться простейшим экспериментом: возьмите моно-сигнал и в наушниках на один из каналов задайте задержку в 1-100 мс, не изменяя амплитуды. Звук будет ощущаться как сдвинутый в сторону, и чем сильнее задержка, чем дальше.

Information

Rating
2,258-th
Location
Россия
Works in
Registered
Activity