пч фпу кривой и его лишь для обратной совместимости держат. видать, не стали мутить приличную хард реализацию, чтоб сохранить место на плашке под "широкие" функи проца
UbIvItS, Интересна сама последовательность в хард, те какой ряд использован чебышев ? Выше был пример и сказали что результат совпадает с хард, но я не верю. Вероятно товарищ тупо округлил значения и они сошлись. Тем более там дельфи, сомнительно там всё - вот выше не ясно что за синус, может вначале вычисляется косинус, сохраняется в те переменные. Та либа Интел по математике - почему используется fpu но не используются непосредственно готовые инструкции тот же fsin не понимаю.
хз, что там пользовали (можЬ в манах Ынтеля написано). но в общем и целом для харда лучше кордик юзать == схема более компактная и можно хорошо балансировать погрешность.
UbIvItS, Кордик" какой то алго ссылка на вики. Всё равно это полином, их много эффективность определяет как быстро сходится ряд. Чебышев быстрее чем Тейлор.
Я убрал fwait, результат тот же. По крайней мере, изменения чисел не заметны на глаз. Нет, это Тейлор, см. ниже. Смотри исходник, там же всё есть. Это делители: 1/4!, -1/6!, 1/8!, -1/10!, 1/12!, 1/14!, а 1/2! = 0.5 вшито в исходник. Я так понял, сделано в бинарном виде для повышения точности.
Jin X, Где сурец посмотреть выложи сюда, я дельфи не юзаю так что хз. Замечательно если можно посмотреть алго в сурке.
maalchemist, чистый синус лучше не замерять. не будет адекватного сравнению. напишите формулу где юзается синус. чтоб были другие инструкции но желательно чтоб отличалась только реализация синуса. Тогда будет более валидный тест. Но тут сами решайте нужно ли вам это ) просто мерить чистый синус - не валидно как с точки зрения общих алгоритмов работы конвеера процессора, так и в плане практических задач.
По поводу точности. Из 36000000 вычислений 1139430 несовпадений fpu и sse (3,165%), суммарно ошибка составляет 8.08899921015606E-0021. Под несовпадением я имею в виду разницу значений хотя бы в один бит. Больше, чем на единицу отличается 417 значений (если реинтерпрет-кастить их к uint64), больше, чем на 2 - 241, больше, чем на 3 - 175, больше, чем на 4 - 144, больше, чем на 8 - 87, больше, чем на 16 - 43, больше, чем на 32 - 22, больше, чем на 64 - 11, больше, чем на 128 - 6, больше, чем на 256 - 3, больше, чем на 512..8192 - 2, больше, чем на 16384 - 1, больше, чем на 32768 - 0. Причём, все значения, которые отличаются больше, чем на 1 (417 штук которые) – при аргументе близком к Пи (и кратным Пи, кроме 0). В частности, в диапазоне от 3.14156263286791 до 3.14162110139778. Т.е. в диапазоне от 0 до почти Пи ошибка составляет не более, чем на 1 (при рассмотрении чисел как uint64). Максимальная разница – при X = 3.14159265253101, получается fpu = 1.05878562617649E-0009 , sse = 1.05878562618053E-0009 (разница составляет 4.04449960507803E-0021, т.е. половину от всех ошибок). --- Сообщение объединено, 2 авг 2020 --- Вот же: https://wasm.in/threads/portirovanie-fpu-na-sse.33827/page-3#post-420686 Вот ещё pRemDouble до кучи: Код (Text): function pRemDouble(D: Double; var X, Y: Double): integer; var PiOf2H, PiOf2M, PiOf2L : Double; lowestPart: double; const PiOf2Hi : UInt64 = $3FF921FB54442D18; // Pi/2 : first 53bits PiOf2Mi : UInt64 = $3C91A62633145C04; // Pi/2 : Middle 53bits PiOf2Lo : UInt64 = $396707344A409382; // Pi/2 : Lowest 53bits ExpOffset1 = 54; // $3FF - $3C9 ExpOffset2 = 105; // $3FF - $396 procedure adddouble(const x: double; var rh, rl : double); inline; var hh, hl: double; temp : double; begin hh := x + rh; temp := hh - x; hl := (x - (hh - temp)) + (rh - temp); hl := hl + rl; rh := hh + hl; rl := hl - (rh - hh); end; procedure AddDouble2(const xh, xl: Double; var rh, rl : Double); inline; var hh, hl: Double; temp : double; begin hh := xh + rh; temp := hh - xh; hl := (xh - (hh - temp)) + (rh - temp); hl := hl + xl + rl; rh := hh + hl; rl := hl - (rh - hh); end; // 3*Double precision reduction procedure ReductionPi2n(n: integer); var X0, T0, T1, T2, T3: Double; begin PDoubleRec(@PiOf2H).Exp := n + $3FF; PDoubleRec(@PiOf2M).Exp := n + $3FF - ExpOffset1; PDoubleRec(@PiOf2L).Exp := n + $3FF - ExpOffset2; T0 := -PiOf2L; T1 := 0; AddDouble( lowestPart, T0, T1); AddDouble(-PiOf2M, T0, T1); AddDouble( Y, T0, T1); // T0&1 - partial sum T2 := T0; T3 := T1; AddDouble(-PiOf2H, T0, T1); AddDouble( X, T0, T1); // T0&1 - X&Y X0 := X; X := T0; Y := T1; T0 := -T0; T1 := -T1; AddDouble( X0, T0, T1); AddDouble(-PiOf2H, T0, T1); AddDouble2(T2,T3, T0, T1); lowestPart := T0; end; const nPi2High : UInt64 = $BFF921FB54400000; // Pi/2 : Upper 32bit. nPi2Low : UInt64 = $BDD0B4611A600000; // Pi/2 : Lower 32bit nPi2Small : UInt64 = $BBA3198A2E037073; // Pi - Pi2High - Pi2Low TwoOfPi : UInt64 = $3FE45F306DC9C883; // 2 / Pi TwoPow22 : UInt64 = $4150000000000000; // 2 ^ 22 TwoPow54 : UInt64 = $4350000000000000; // 2 ^ 54 var D2 : Double; Q: Int64; n: integer; hh, hl, pHi, pLow, pSmall, X2, Y2, temp : double; begin Result := 0; X := D; Y := 0; D2 := Abs(D); if D2 <= Pi / 4 then Exit else if D2 <= 5 * Pi/4 then begin if D2 <= 3 * Pi/4 then Q := 1 else Q := 2; if D < 0 then Q := -Q; pHi := Q * PDouble(@PiOf2Hi)^; pLow := Q * PDouble(@PiOf2Mi)^; pSmall := Q * PDouble(@PiOf2Lo)^; X2 := D - pHi; X := X2 - pLow; Y := - pLow - (X - X2); Y := Y - pSmall; end else if D2 < PDouble(@TwoPow22)^ then begin // Reduction for small/mid number (up to 2^22) Q := Trunc((D2 - Pi/4) * PDouble(@TwoOfPi)^) + 1; if D < 0 then Q := -Q; pHi := Q * PDouble(@nPi2High)^; pLow := Q * PDouble(@nPi2Low)^; pSmall := Q * PDouble(@nPi2Small)^; X2 := D + pHi; hh := pLow + X2; temp := hh - pLow; hl := (pLow - (hh - temp)) + (X2 - temp); X2 := hh + hl; Y2 := hl - (X2 - hh); hh := pSmall + X2; temp := hh - pSmall; hl := (pSmall - (hh - temp)) + (X2 - temp); hl := hl + Y2; X2 := hh + hl; Y2 := hl - (X2 - hh); X := X2; Y := Y2; end else begin // Reduction for large numbber between 2^22 to 2^53 n := PDoubleRec(@D2).Exp - $3FF; PUInt64(@PiOf2H)^ := PiOf2Hi; PUInt64(@PiOf2M)^ := PiOf2Mi; PUInt64(@PiOf2L)^ := PiOf2Lo; X := D2; Y := 0; lowestPart := 0; while n >= 3 do begin PDoubleRec(@PiOf2H).Exp := n + $3FF; if PiOf2H < X then ReductionPi2n(n); Dec(n); end; Q := 0; while n >= 0 do begin PDoubleRec(@PiOf2H).Exp := n + $3FF; if PiOf2H < X then begin Q := Q or (1 shl n); ReductionPi2n(n); end; Dec(n); end; if X > Pi/4 then begin Q := Q + 1; ReductionPi2n(0); end; if D < 0 then begin X := -X; Y := -Y; Q := -Q; end; end; Result := Q and 3; end; --- Сообщение объединено, 2 авг 2020 --- Ну не то чтоб ни о чём, но не сильно много. Но я думаю, что если алгоритм усовершенствовать, можно добиться лучшего результата (до 2х, думаю, всё же можно довести). А если пожертвовать точностью (в довольно многих случаях большая точность не нужна), но ещё больше. --- Сообщение объединено, 2 авг 2020 --- А вот, кстати, и фокус-покус. Написал на C++ Код (C++): #include <iostream> #include <windows.h> volatile double y; int main() { double x = 0, delta = 1.74532925199432957692369e-7; DWORD c = GetTickCount(); for (int i = 0; i < 36000000; ++i) { x = x + delta; y = std::sin(x); } c = GetTickCount() - c; std::cout << c; return 0; } и Код (C++): #include <iostream> #include <windows.h> volatile double y; double finit() { __asm { finit } } double fsin(double x) { __asm { fld qword ptr x fsin fstp st } } int main() { double x = 0, delta = 1.74532925199432957692369e-7; finit(); DWORD c = GetTickCount(); for (int i = 0; i < 36000000; ++i) { x = x + delta; y = fsin(x); } c = GetTickCount() - c; std::cout << c; return 0; } Компилю VC++ Первый код – 640 (x86) и 480 (x64). Второй – порядка 1170 (x86, в x64 асм не поддерживается). Так что, тут разница 1.8 - 2.4х. --- Сообщение объединено, 2 авг 2020 --- И ошибок тут 49438 из 36000000 (0,137 %). Разница больше, чем на 1, опять же, в 417 случаях ) Больше, чем на 16 – в 43-х случаях. Т.е. очень похожая ситуация, только общее число ошибок меньше. --- Сообщение объединено, 2 авг 2020 --- Можно ещё под шлангом проверить, но уже в лом что-то.
Jin X, Ну так ясно, хард использует подобный полином, больше элементов в ряде отсюда выше точность. Сравнение на разных архитектурах бессмысленно, там даже разрядность иная, размер регистров. > Но я думаю, что если алгоритм усовершенствовать, можно добиться лучшего результата Сними профайл на последовательности стековых fpu вычислений, вероятно на порядок будет быстрее, чем полиномы. Тк не будет паразитных выборок в память, а числа длинные в памяти. Я не проверял, но уверен что тайминг резко пойдёт на спад с каждой +1 операцией fpu. И если что то вычислить на пол сотни операций fpu(сложную математику), то от этих полиномов не останется ничего. Примитивная операция +30% - это никто не заметит.
Не совсем по теме. Переделка методов класса Fmatrix в движке XRay с помощью интринсиков. Спойлер Код (C++): // Multiply RES = A[4x3]*B[4x3] (no projection), faster than ordinary multiply ICF Fmatrix& mul_43 (const Fmatrix &A,const Fmatrix &B) { [indetn]// VERIFY ((this!=&A)&&(this!=&B)); /* m[0][0] = A.m[0][0] * B.m[0][0] + A.m[1][0] * B.m[0][1] + A.m[2][0] * B.m[0][2]; m[0][1] = A.m[0][1] * B.m[0][0] + A.m[1][1] * B.m[0][1] + A.m[2][1] * B.m[0][2]; m[0][2] = A.m[0][2] * B.m[0][0] + A.m[1][2] * B.m[0][1] + A.m[2][2] * B.m[0][2]; m[0][3] = 0; m[1][0] = A.m[0][0] * B.m[1][0] + A.m[1][0] * B.m[1][1] + A.m[2][0] * B.m[1][2]; m[1][1] = A.m[0][1] * B.m[1][0] + A.m[1][1] * B.m[1][1] + A.m[2][1] * B.m[1][2]; m[1][2] = A.m[0][2] * B.m[1][0] + A.m[1][2] * B.m[1][1] + A.m[2][2] * B.m[1][2]; m[1][3] = 0; m[2][0] = A.m[0][0] * B.m[2][0] + A.m[1][0] * B.m[2][1] + A.m[2][0] * B.m[2][2]; m[2][1] = A.m[0][1] * B.m[2][0] + A.m[1][1] * B.m[2][1] + A.m[2][1] * B.m[2][2]; m[2][2] = A.m[0][2] * B.m[2][0] + A.m[1][2] * B.m[2][1] + A.m[2][2] * B.m[2][2]; m[2][3] = 0; m[3][0] = A.m[0][0] * B.m[3][0] + A.m[1][0] * B.m[3][1] + A.m[2][0] * B.m[3][2] + A.m[3][0]; m[3][1] = A.m[0][1] * B.m[3][0] + A.m[1][1] * B.m[3][1] + A.m[2][1] * B.m[3][2] + A.m[3][1]; m[3][2] = A.m[0][2] * B.m[3][0] + A.m[1][2] * B.m[3][1] + A.m[2][2] * B.m[3][2] + A.m[3][2]; m[3][3] = 1;*/ register __m128 Ai = A.i.m; register __m128 Aj = A.j.m; register __m128 Ak = A.k.m; i.m = ADDPS(ADDPS(MULPS(SETPS1(B.i.x), Ai), MULPS(SETPS1(B.i.y), Aj)), MULPS(SETPS1(B.i.z), Ak)); j.m = ADDPS(ADDPS(MULPS(SETPS1(B.j.x), Ai), MULPS(SETPS1(B.j.y), Aj)), MULPS(SETPS1(B.j.z), Ak)); k.m = ADDPS(ADDPS(MULPS(SETPS1(B.k.x), Ai), MULPS(SETPS1(B.k.y), Aj)), MULPS(SETPS1(B.k.z), Ak)); c.m = ADDPS(ADDPS(ADDPS(MULPS(SETPS1(B.c.x), Ai), MULPS(SETPS1(B.c.y), Aj)), MULPS(SETPS1(B.c.z), Ak)), A.c.m); VERIFY(c._==1.f); return *this;[/indent] } Просто демонстрация оптимизации, код быстрей оригинала в 2.3 раза и где так же компактней, а значит быстрей будет загружаться. PS Дефайны! Спойлер Код (C++): #define MINPS _mm_min_ps #define MINSS _mm_min_ss #define MAXPS _mm_max_ps #define MAXSS _mm_max_ss #define ADDPS _mm_add_ps #define ADDSS _mm_add_ss #define SUBPS _mm_sub_ps #define SUBSS _mm_sub_ss #define MULPS _mm_mul_ps #define MULSS _mm_mul_ss #define DIVPS _mm_div_ps #define DIVSS _mm_div_ss #define XORPS _mm_xor_ps #define ANDPS _mm_and_ps #define ANDNOTPS _mm_andnot_ps #define ORPS _mm_or_ps #define SETPS1 _mm_set_ps1 #define MOVSS _mm_set_ss #define SHUFFLEPS _mm_shuffle_ps #define SQRTPS _mm_sqrt_ps #define SQRTSS _mm_sqrt_ss #define SETPS3(Z) _mm_shuffle_ps(_mm_set_ss(Z),_mm_set_ss(Z),_MM_SHUFFLE(3,0,0,0)) #define ABSPS(A) _mm_andnot_ps(SIGNMASK3, A) --- Сообщение объединено, 8 дек 2020 --- А вот макро инструкция SINPS Код (ASM): SINPS MACRO X:req ;=x-x^3/3!+x^5/5!-x^7/7! movups xmm0, X movaps xmm2, xmm0 movaps xmm3, xmm0 ;1 mulps xmm3, xmm3 ;=x*x mulps xmm2, xmm3 ;=x^3 movaps xmm1, xmm2 mulps xmm1, trigoFactorial3 subps xmm0, xmm1 ;2 mulps xmm2, xmm3 ;=x^5 movaps xmm1, xmm2 mulps xmm1, trigoFactorial5 addps xmm0, xmm1 ;3 mulps xmm2, xmm3 ;=x^7 mulps xmm2, trigoFactorial7 subps xmm0, xmm2 ENDM Проверил, работает! На сколько быстро пока не проверял, думаю быстрей fsin. ЗЫ Константы такие, да я их секцию конст поместил, чтобы ида мне готовый код с интринсиками выдала! Код (ASM): .const align 16 trigoFactorial3 real4 0.16666667, 0.16666667, 0.16666667, 0.16666667 ;=1/3! trigoFactorial5 real4 0.0083333333, 0.0083333333, 0.0083333333, 0.0083333333 ;=1/5! trigoFactorial7 real4 1.9841270e-4, 1.9841270e-4, 1.9841270e-4, 1.9841270e-4 ;=1/7!
Код (ASM): SINPS MACRO X:req ;=x-x^3/3!+x^5/5!-x^7/7! movups xmm0, X ; и здесь бы выровнять movaps xmm2, xmm0 movaps xmm3, xmm0 mulps xmm3, xmm3 ;=x*x ;1 mulps xmm2, xmm3 ;=x^3 mulps xmm2, 1div6 ;=x^3/2/3 subps xmm0, xmm2 ;2 mulps xmm2, xmm3 ;=x^5/2/3 mulps xmm2, 1div20 ;=x^5/2/3/4/5 addps xmm0, xmm2 ;3 mulps xmm2, xmm3 ;=x^7/2/3/4/5 mulps xmm2, 1div42 ;=x^7/2/3/4/5/6/7 subps xmm0, xmm2 ENDM
НетРегистрации, прикольно. Я тоже думал что с константами возможна дополнительная оптимизация, но не смог математически доказать правильность. Да, оказалось надо больше итераций, а так же ограничить х от -PI до PI, на что нужен дополнительный код, так что тут не всё просто. А с другой стороны, что нам запрещает предпринять специальные методы, чтобы угол ограничить -180..180, а если что пошло не так, сработает ассерт.
Intro, а как там с точностью результата? Можно ещё сделать макросы SINPS_HP для более точного расчёта (взять ещё 4 члена полинома, до 1/15!). А также SINPD, SINPD_HP. --- Сообщение объединено, 22 дек 2020 --- По скорости SINPS у меня получился в 25 раз быстрее --- Сообщение объединено, 22 дек 2020 --- (чем fsin) --- Сообщение объединено, 22 дек 2020 --- Вот такой код работает у меня в 1.7x быстрее (я его решил назвать LP, т.к. медленнее): Код (ASM): ; fasm MACRO SINPS_LP X* { ;=x-x^3/3!+x^5/5!-x^7/7! movups xmm0, X movaps xmm1, xword [_1div6] ;=1/2/3 movaps xmm2, xmm0 ;=x movaps xmm3, xmm0 mulps xmm3, xmm3 ;=x^2 movaps xmm4, xmm3 mulps xmm4, xmm4 ;=x^4 ;1 mulps xmm3, xmm0 ;=x^3 mulps xmm1, xmm3 ;=x^3/2/3 ;2 mulps xmm2, xmm4 ;=x^5 mulps xmm2, xword [_1div120] ;=x^5/2/3/4/5 addps xmm0, xmm2 ;x+x^5/5! ;3 mulps xmm3, xmm4 ;=x^7 mulps xmm3, xword [_1div5040] ;=x^7/2/3/4/5/6/7 addps xmm1, xmm3 ;=x^3/3!+x^7/7! subps xmm0, xmm1 } ... _1div6 dd 4 dup (0.1666666666667) _1div120 dd 4 dup (8.333333333333e-3) _1div5040 dd 4 dup (1.984126984127e-4) И более точные вычисления (медленнее в 2.1x, чем предыдущий SINPS_LP и в 1.3x медленнее, чем код от НетРегистрации): Код (Text): ; fasm MACRO SINPS X* { ;=x-x^3/3!+x^5/5!-x^7/7!+x^9/9!-x^11/11!+x^13/13!-x^15/15! movups xmm0, xword [X] ; и здесь бы выровнять movaps xmm1, xword [_1div6] ;=1/2/3 movaps xmm2, xmm0 ;=x movaps xmm3, xmm0 mulps xmm3, xmm3 ;=x^2 movaps xmm4, xmm3 mulps xmm4, xmm4 ;=x^4 ;1 mulps xmm3, xmm0 ;=x^3 mulps xmm1, xmm3 ;=x^3/2/3 ;2 mulps xmm2, xmm4 ;=x^5 mulps xmm2, xword [_1div120] ;=x^5/2/3/4/5 addps xmm0, xmm2 ;x+x^5/5! ;3 mulps xmm3, xmm4 ;=x^7 mulps xmm3, xword [_1div5040] ;=x^7/2/3/4/5/6/7 addps xmm1, xmm3 ;=x^3/3!+x^7/7! ;4 mulps xmm2, xmm4 ;=x^9/2/3/4/5 mulps xmm2, xword [_1div3024] ;=x^9/2/3/4/5/6/7/8/9 addps xmm0, xmm2 ;x+x^5/5!+x^9/9! ;5 mulps xmm3, xmm4 ;=x^11/2/3/4/5/6/7 mulps xmm3, xword [_1div7920] ;=x^11/2/3/4/5/6/7/8/9/10/11 addps xmm1, xmm3 ;=x^3/3!+x^7/7!+x^11/11! ;6 mulps xmm2, xmm4 ;=x^13/2/3/4/5/6/7/8/9 mulps xmm2, xword [_1div17160] ;=x^13/2/3/4/5/6/7/8/9/10/11/12/13 addps xmm0, xmm2 ;x+x^5/5!+x^9/9!+x^13/13! ;7 mulps xmm3, xmm4 ;=x^11/2/3/4/5/6/7/8/9/10/11 mulps xmm3, xword [_1div32760] ;=x^11/2/3/4/5/6/7/8/9/10/11/12/13/14/15 addps xmm1, xmm3 ;=x^3/3!+x^7/7!+x^11/11!+x^15/15! subps xmm0, xmm1 } ... _1div6 dd 4 dup (0.1666666666667) _1div120 dd 4 dup (8.333333333333e-3) _1div5040 dd 4 dup (1.984126984127e-4) _1div3024 dd 4 dup (3.306878306878e-4) _1div7920 dd 4 dup (1.262626262626e-4) _1div17160 dd 4 dup (5.827505827506e-5) _1div32760 dd 4 dup (3.052503052503e-5) Все вычисления верные, проверено. Но погрешности всё равно есть. Можно подумать ещё как сделать вычисления более точными и более быстрыми. К примеру, можно свернуть формулу вот так: --- Сообщение объединено, 22 дек 2020 --- Блин, отправил случайно... Ладно, без примера, уже плохо соображаю в 3 часа ночи. Вот тестовый код.
Обычная точность ~3байта, внутренняя SSE мне неизвестна, 1/15! ну вы поняли, что может быть при суммировании. Соответственно можно лишь попытаться улучшить точность, начиная суммирование с конца.
Можно ещё через умножение сделать. Т.е. не складывать x^5/5! + x^9/9! + x^13/13!, а (((x^4/13!+1/9!)*x^4+1/5!)*x^5. Как, собственно, это в Delphi примерно и делается. По поводу 25x – это я наврал, неправильно затестил. fsin медленнее в сравнении с моим sinps в 2.9x. Вот тестовый код вместе с ним. Спойлер: И вывод программы Код (Text): Success: both RDTSCP instruction and invariant TSC are supported. Testing SINPS (unoptimized)...... 60 ticks Testing SINPS.................... 36 ticks Testing SINPS_LP (unoptimized)... 27 ticks Testing SINPS_LP................. 18 ticks Testing FSIN..................... 105 ticks Press a key to exit...
О как! Вынес fld1 в начало кода (после finit), скорость fsin увеличилась до 66 тактов. И вообще, все цифры чуть уменьшились... почему-то Спойлер: Вывод Код (Text): Testing SINPS (unoptimized)...... 55 ticks Testing SINPS.................... 31 ticks Testing SINPS_LP (unoptimized)... 25 ticks Testing SINPS_LP................. 13 ticks Testing FSIN..................... 67 ticks Хотя, если оставить fld1 + fsin + fstp st0, либо добавить ещё одну пару fld1 + fstp st0, разницы почти не будет. Интересно, почему так?
Jin X, Так потому что это ошибка профайлера(системных таймеров), у него разрешение на много порядков ниже, а есчо он периодически вытисняется как задача. Замер должен быть в цикле на очень большом числе итераций. Иначе будет рандом. --- Сообщение объединено, 22 дек 2020 --- > Success: both RDTSCP instruction and invariant TSC are supported. Нет этот счётчик крайне медленный(есчо и плавает), не получится за одну итерацию, это в принципе невозможно. Процик не так тупо сделан, есть всякие предсказания конвееры и прочее, что он сам посчитать не может. Только если в цикле тайминг считать, тем самым исключить анстаб таймеров.
Так, там 256 циклов + 16 на разогрев. И из 256 половина отбрасывается (64 самых быстрых и 64 самых медленных). Минус холостой, разумеется. И я этот тест запускал много раз. Показывает одинаковые значения с разбегом 1-2%. А сейчас сделал 65536 (с разогревом 256), разбег стал ещё меньше (чаще всего числа всегда одинаковые). Но проблема осталась: когда fld/ftsp внутри функции, все тесты показывают чуть большее значение, а когда снаружи, все показывают чуть меньше. Хотя, они только в одном тесте присутствуют. Причём, все функции выравнены по 16 байтам, а этот тест последний, т.е. тестовые процедуры сдвинуться не могут. --- Сообщение объединено, 23 дек 2020 --- А вот сейчас я ещё и сами процедуры замера выравнил по 16 байтам. И результаты стали одинаковыми, кроме последнего теста. Как и должно быть. --- Сообщение объединено, 23 дек 2020 --- Вот. --- Сообщение объединено, 23 дек 2020 --- Почему плавает? У меня разница 4 такта (хотя понимаю, что может быть и больше). Но учитывая, что он ожидает завершение предыдущих операций (не надо вызывать cpuid, который действительно медленный), то его использование наоборот быстрее получается. Вообще, хотелось бы, чтобы спецы проинспектировали функции чтения счётчиков и поправили, если что не так: Код (ASM): ; Read TSC via RDTSC [for internal use] ; Returns: rax = current TSC counter value, ecx = 0 (processor id detection is not supported) ; Changes ebx !!! if used SpeedTestRDTSC SpeedTestRDTSC: xor eax,eax ; cpuid execution time may vary depending on eax value cpuid ; serialization xor ecx,ecx ; processor id (not supported) rdtsc shl rdx,32 or rax,rdx mfence ret end if ; used SpeedTestRDTSC ; Read TSC via RDTSCP [for internal use] ; Returns: rax = current TSC counter value, ecx = processor id if used SpeedTestRDTSCP SpeedTestRDTSCP: mfence rdtscp shl rdx,32 or rax,rdx mfence SpeedTestEmptyFunc: ret end if ; used SpeedTestRDTSCP --- Сообщение объединено, 23 дек 2020 --- Блин, mfence перед rdtscp тут не должно быть, случайно написал :-/