Оптимизация. Как еще улучшить внутренние циклы? Масштабирование CSR-спрайтов (4 стр)
Страницы:
1 …
3 4 5 6 …
16 Следующая »
MrShoor
> по мудацки было делать, чтобы операция присвоения возврващала ссылку
в си были распространены конструкции инициализации вида
int x, y, z;
x = y = z = 0;
SereG
> Почему, очень удобно:
>
> if (Unit* unit = dynamic_cast<Unit*>(obj))
> {
> unit->...
> }
А это пример мудацкого кода, который ничего бы не потерял, если бы был написан так:
Unit* unit = dynamic_cast<Unit*>(obj)
if (unit)
{
unit->...
}
#!
> int x, y, z;
> x = y = z = 0;
И еще один пример мудацкого кода.
MrShoor
> А это пример мудацкого кода, который ничего бы не потерял, если бы был написан так:
> Unit* unit = dynamic_cast<Unit*>(obj)
> if (unit)
> {
> unit->...
> }
Лишние фигурные скобки.
for (auto obj : objects)
if (Unit* unit = dynamic_cast<Unit*>(obj))
unit->...
SereG
> Лишние фигурные скобки.
>
> for (auto obj : objects)
> if (Unit* unit = dynamic_cast<Unit*>(obj))
> unit->...
О, и еще один пример мудацкого кода. Безотносительно возможности присваивать внутри if-а можно было написать:
for (auto obj : objects) {
if (foo) {
bar...
}
}
Но нет, написано по мудацки без фигурных скобок. Это же так тяжело, нажать пару клавиш.
MrShoor
> Шо там, уже можно писать hlsl в jetbrains?
В райдере можно, но не проверял, подчеркнет ли.
И вообще, речь же шла не о шейдерных языках. Я такой код видел в c++/c#.
#50 (Правка: 21:38)
21:10, 26 сен 2022
В жирном if продублирован последний or.
#51 (Правка: 2:08)
0:40, 27 сен 2022
entryway, спасибо, что заметили! Подправлю в первом посте только это место. Чтобы видеть потом разницу с уже оптимизированным вариантом. А более оптимизированный на данный момент вариант такой:
+ Показать
− Скрыть
procedure TFastImageProc.FilNTColorScaleDown; {$ifdef Linux}[local];{$endif} {$region -fold}
type
TRGBA=packed record
r,g,b,a: byte;
end;
PRGBA=^TRGBA;
var
nt_pix_intr_cnt_arr_ptr : PInteger;
nt_pix_intr_sht_arr_ptr : PInteger;
nt_pix_intr_ccl_arr_ptr : PInteger;
first_row_pix_ptr : PInteger;
bmp_bkgnd_ptr2 : PInteger;
scl_mul_x,scl_mul_y,d1_,d2_,s1_,x,y,v1,v2,v3,r1,r2,r3 : integer;
px,py,s0,s1,g1,g2,alpha,alpha_inv,alpha_inv_mul_s10000,d1,d2,alpha_sqr,k1,k2,r4,r5: double;
row_parity : boolean;
label
l1;
begin
with fast_image_data_ptr0^ do
begin
d1 :=bmp_ftimg_left*(1-scl_mul.x);
d2 :=bmp_ftimg_top *(1-scl_mul.y);
d1_ :=Trunc($0010000* d1 );
d2_ :=Trunc($0010000* d2 );
scl_mul_x :=Trunc($0010000* scl_mul.x);
scl_mul_y :=Trunc($0010000* scl_mul.y);
r1 :=scl_mul_x*( bmp_ftimg_left)+d1_;
r2 :=scl_mul_y*(nt_pix_arr_row_mrg_top+bmp_ftimg_top )+d2_;
r4 :=scl_mul.x*( bmp_ftimg_left)+d1 ;
r5 :=scl_mul.y*(nt_pix_arr_row_mrg_top+bmp_ftimg_top )+d2 ;
first_row_pix_ptr :=Unaligned(@bmp_bkgnd_ptr [(nt_pix_arr_row_mrg_top+bmp_ftimg_top)*bmp_bkgnd_width+bmp_ftimg_left]);
bmp_bkgnd_ptr :=Unaligned(@bmp_bkgnd_ptr [000000000000000000000000000000000000000000000000000000000000000000000]);
nt_pix_intr_cnt_arr_ptr:=Unaligned(@nt_pix_intr_cnt_arr[nt_pix_arr_row_mrg_top+0000000000000000000000000000000000000000000000]);
nt_pix_intr_sht_arr_ptr:=Unaligned(@nt_pix_intr_sht_arr[000000000000000000000000000000000000000000000000000000000000000000000]);
nt_pix_intr_ccl_arr_ptr:=Unaligned( nt_pix_intr_arr_ptr);
if (scl_mul.x<0.15) and (scl_mul.y<0.15) then
for y:=0 to bmp_ftimg_height-nt_pix_arr_row_mrg_top-nt_pix_arr_row_mrg_btm-1 do
begin
row_parity:=not row_parity;
r3 :=((scl_mul_y*y+r2)>>16)*bmp_bkgnd_width;
...
Inc (nt_pix_intr_cnt_arr_ptr);
end
else
if ((scl_mul.x<0.2) and
(scl_mul.y<0.2)) or
((scl_mul.x>0.5-0.01) and
(scl_mul.x<0.5+0.01) and
(scl_mul.y>0.5-0.01) and
(scl_mul.y<0.5+0.01)) or
((scl_mul.x>0.5-0.01) and
(scl_mul.x<0.5+0.01) and
(scl_mul.y>1.0-0.01) and
(scl_mul.y<1.0+0.01)) then
begin
l1:
for y:=0 to bmp_ftimg_height-nt_pix_arr_row_mrg_top-nt_pix_arr_row_mrg_btm-1 do
begin
r3:=((scl_mul_y*y+r2)>>16)*bmp_bkgnd_width;
for x:=0 to nt_pix_intr_cnt_arr_ptr^-1 do
begin
(bmp_bkgnd_ptr+((scl_mul_x*nt_pix_intr_sht_arr_ptr^+r1)>>16)+r3)^:=
nt_pix_intr_ccl_arr_ptr^;
Inc (nt_pix_intr_sht_arr_ptr);
Inc (nt_pix_intr_ccl_arr_ptr);
end;
Inc (nt_pix_intr_cnt_arr_ptr);
end;
Exit;
end
else
if (scl_mul.x<0.25) and (scl_mul.y<0.25) then
begin
alpha :=exp(((scl_mul.x+scl_mul.y)/2)*ln(0.95))-0.5;
alpha_inv :=1/alpha;
alpha_inv_mul_s10000:= alpha_inv*65536;
alpha_sqr := alpha *alpha-0.25;
v3 :=r2;
for y:=0 to bmp_ftimg_height-nt_pix_arr_row_mrg_top-nt_pix_arr_row_mrg_btm-1 do
begin
row_parity:=not row_parity;
v2 :=v3>>16;
g2 :=scl_mul.y*y+r5;
for x:=0 to nt_pix_intr_cnt_arr_ptr^-1 do
begin
if Odd(nt_pix_intr_sht_arr_ptr-first_row_pix_ptr) and row_parity then
begin
...
end;
Inc (nt_pix_intr_sht_arr_ptr);
Inc (nt_pix_intr_ccl_arr_ptr);
end;
Inc (nt_pix_intr_cnt_arr_ptr);
Inc(v3,scl_mul_y);
end;
end
else
if (scl_mul.x<0.75) and (scl_mul.y<0.75) then
begin
alpha :=exp(((scl_mul.x+scl_mul.y)/2)*ln(0.95));
alpha_inv :=1/alpha;
alpha_inv_mul_s10000:= alpha_inv*65536;
alpha_sqr := alpha *alpha-0.25;
v3 :=r2;
for y:=0 to bmp_ftimg_height-nt_pix_arr_row_mrg_top-nt_pix_arr_row_mrg_btm-1 do
begin
v2:=v3>>16;
g2:=scl_mul.y*y+r5;
for x:=0 to nt_pix_intr_cnt_arr_ptr^-1 do
begin
v1 :=((scl_mul_x*nt_pix_intr_sht_arr_ptr^+r1)>>16);
g1 := scl_mul.x*nt_pix_intr_sht_arr_ptr^+r4;
px :=(Min(v1+1,g1+scl_mul.x)-Max(v1,g1));
py :=(Min(v2+1,g2+scl_mul.y)-Max(v2,g2));
bmp_bkgnd_ptr2 :=bmp_bkgnd_ptr+v1+v2*bmp_bkgnd_width;
s0 :=px *py;
k1 :=alpha*px;
k2 :=alpha*py;
if (s0>0.0) then
begin
s1_ :=Trunc(alpha_inv_mul_s10000*s0);
(bmp_bkgnd_ptr2+00000000000000000)^:=(PRGBA(bmp_bkgnd_ptr2)^.r+(s1_*(PRGBA(nt_pix_intr_ccl_arr_ptr)^.r-PRGBA(bmp_bkgnd_ptr2)^.r))>>16)<<00+
(PRGBA(bmp_bkgnd_ptr2)^.g+(s1_*(PRGBA(nt_pix_intr_ccl_arr_ptr)^.g-PRGBA(bmp_bkgnd_ptr2)^.g))>>16)<<08+
(PRGBA(bmp_bkgnd_ptr2)^.b+(s1_*(PRGBA(nt_pix_intr_ccl_arr_ptr)^.b-PRGBA(bmp_bkgnd_ptr2)^.b))>>16)<<16;
end;
s1 :=k2-s0;
if (s1<0.25) then
begin
s1_ :=Trunc(alpha_inv_mul_s10000*s1);
(bmp_bkgnd_ptr2+000000000000000+1)^:=(PRGBA(bmp_bkgnd_ptr2+1)^.r+(s1_*(PRGBA(nt_pix_intr_ccl_arr_ptr)^.r-PRGBA(bmp_bkgnd_ptr2+1)^.r))>>16)<<00+
(PRGBA(bmp_bkgnd_ptr2+1)^.g+(s1_*(PRGBA(nt_pix_intr_ccl_arr_ptr)^.g-PRGBA(bmp_bkgnd_ptr2+1)^.g))>>16)<<08+
(PRGBA(bmp_bkgnd_ptr2+1)^.b+(s1_*(PRGBA(nt_pix_intr_ccl_arr_ptr)^.b-PRGBA(bmp_bkgnd_ptr2+1)^.b))>>16)<<16;
end
else
(bmp_bkgnd_ptr2+000000000000000+1)^:=nt_pix_intr_ccl_arr_ptr^;
if (k1+s1>alpha_sqr) then
(bmp_bkgnd_ptr2+bmp_bkgnd_width+1)^:=nt_pix_intr_ccl_arr_ptr^
else
if (k1-s0>000000.25) then
(bmp_bkgnd_ptr2+bmp_bkgnd_width+0)^:=nt_pix_intr_ccl_arr_ptr^;
Inc (nt_pix_intr_sht_arr_ptr);
Inc (nt_pix_intr_ccl_arr_ptr);
end;
Inc (nt_pix_intr_cnt_arr_ptr);
Inc(v3,scl_mul_y);
end;
end
else
if (scl_mul.x<1.0) and (scl_mul.y<1.0) then
goto l1;
end;
end; {$endregion}
#52 (Правка: 0:58)
0:43, 27 сен 2022
Скрины производительности:
+ Показать
Оптимизации вроде пустяковые, но почти 22 % разницы.
Какие еще проблемные места вижу. Взятие целой части в math-модуле стандартной runtime-библиотеки имеет довольно таки длинный код(с у четом того, что эта операция берется каждый пиксель, ну или каждый "нечетный", если брать более мелкие уровни масштабирования):
+ Показать
− Скрыть
function fpc_trunc_real(d : valreal) : int64; compilerproc;
var
aExp, shiftCount : smallint;
aSig : int64;
z : int64;
a: float64;
begin
a:=float64(d);
aSig:=(int64(a.high and $000fffff) shl 32) or longword(a.low);
aExp:=(a.high shr 20) and $7FF;
if aExp<>0 then
aSig:=aSig or $0010000000000000;
shiftCount:= aExp-$433;
if 0<=shiftCount then
begin
if aExp>=$43e then
begin
if (a.high<>longint($C3E00000)) or (a.low<>0) then
begin
fpe_helper(zero/zero);
if (longint(a.high)>=0) or ((aExp=$7FF) and
(aSig<>$0010000000000000 )) then
begin
result:=$7FFFFFFFFFFFFFFF;
exit;
end;
end;
result:=$8000000000000000;
exit;
end;
z:=aSig shl shiftCount;
end
else
begin
if aExp<$3fe then
begin
result:=0;
exit;
end;
z:=aSig shr -shiftCount;
{
if (aSig shl (shiftCount and 63))<>0 then
float_exception_flags |= float_flag_inexact;
}
end;
if longint(a.high)<0 then
z:=-z;
result:=z;
end;
Верю, что тот математический модуль не дураки писали, так что если тут и удастся что-то соптимизировать, то это будет какой-то намного более изощренный метод. В идеале, если избавиться от целой части в обеих ветках в каждом внутреннем цикле, то получается еще -40 мс. Вобщем, если ничего не придумаю, то забью на этот участок кода.
Ну и само собой, напрашивается дальнейшая векторизация умножений в выражениях вида
(PRGBA(bmp_bkgnd_ptr2+1)^.r+(s1_*(PRGBA(nt_pix_intr_ccl_arr_ptr)^.r-PRGBA(bmp_bkgnd_ptr2+1)^.r))>>16)<<00+
(PRGBA(bmp_bkgnd_ptr2+1)^.g+(s1_*(PRGBA(nt_pix_intr_ccl_arr_ptr)^.g-PRGBA(bmp_bkgnd_ptr2+1)^.g))>>16)<<08+
(PRGBA(bmp_bkgnd_ptr2+1)^.b+(s1_*(PRGBA(nt_pix_intr_ccl_arr_ptr)^.b-PRGBA(bmp_bkgnd_ptr2+1)^.b))>>16)<<16;
Но это уже конечно на будущее.
P.S. Как уже заметил товарищ Skvoznjak, строки с множеством нулей и подобные "извращения" служат для более удобного мультистрочного редактирования в случае чего, типа так:

ArtProg
> Trunc
ты уверен что они тебе нужны? Ты знаешь сколько это времени занимает? Особенно в цикле.
#55 (Правка: 3:27)
3:01, 27 сен 2022
Это если вобще удалить строки во внутренних циклах с целыми частями. То есть какая-то альтернатива все-таки нужна, иначе последующие вычисления будут некорректны. В самом первом, совсем неоптимизированном, варианте в последнем внутреннем цикле их было 8. Они нужны, чтобы избавиться от чисел с плавающей точкой. Это я еще просто откинул ветки, которые считали площади частей оригинального пикселя, попадающих на два нижних пикселя приемника и без них качество почти не пострадало. Но дальше уже шло существенное ухудшение качества, вплоть до того, что проще уже было использовать самый первый цикл(тот, что повыше кучи if-ов). На этом идеи пока что кончились. Не отказался бы даже от ассемблерного варианта для взятия целой части.
UPD. Появилась еще одна идея насчет упрощения функции Trunc. Поскольку, как нетрудно показать, что
alpha_inv_mul_s10000*s0<65536=2^16
и
alpha_inv_mul_s10000*s1<65536=2^16
, причем sup A-inf A<2^15(проверял только для 0.25<=scale multiplier.x<0.75 и 0.25<=scale multiplier.y<0.75 одновременно), где A - множество аргументов, то может быть возможно изготовить специальную, с учетом такого множества A, lookup table с каким нибудь шагом. Надо еще подумать над этим вобщем.
попробуй вместо Trunc подставить (- 0.5) в формулы. Может прокатить.
Увы, но так тоже глючит
+ Показать
Правда без целой части кажется также не обойтись(если конечно правильно посчитал):
(bmp_bkgnd_ptr2+000000000000000+1)^:=Trunc((PRGBA(bmp_bkgnd_ptr2+1)^.r+(-0.5{s1_}*(PRGBA(nt_pix_intr_ccl_arr_ptr)^.r-PRGBA(bmp_bkgnd_ptr2+1)^.r))/65536{>>16}) {<<00}+
(PRGBA(bmp_bkgnd_ptr2+1)^.g+(-0.5{s1_}*(PRGBA(nt_pix_intr_ccl_arr_ptr)^.g-PRGBA(bmp_bkgnd_ptr2+1)^.g))/65536{>>16})*256 {<<08}+
(PRGBA(bmp_bkgnd_ptr2+1)^.b+(-0.5{s1_}*(PRGBA(nt_pix_intr_ccl_arr_ptr)^.b-PRGBA(bmp_bkgnd_ptr2+1)^.b))/65536{>>16})*65536{<<16});
#58 (Правка: 4:25)
4:22, 27 сен 2022
если у тебя цель — поупражняться в CPU-рендеринге в качестве хобби, то здорово, флаг в руки, барабан на шею. только не надо придумывать оправдания, почему это может иметь реальный практический смысл. твоя текущая гига-оптимизированная версия рендерит 1000 спрайтов за 280ms. GPU твою тысячу спрайтов отрендерит за время, которое ты даже нормально измерить не сможешь, потому что оно будет меньше погрешности измерения. архитектура gpu специально спроектирована для таких задач и она с ними справляется несравнимо быстрее cpu(не в полтора или два раза, а в тысячи раз) вообще без каких бы то ни было оптимизаций. объективно если тебе хочется реализовать самый быстрый в мире растеризатор, то начинать надо с брутфорсного gpu растеризатора и оптимизировать дальше оттуда.
#59 (Правка: 4:28)
4:25, 27 сен 2022
А в платформерах оно нужно - большее количество спрайтов?
Насчет GPU у меня есть свои соображения, но пока не закончу с софтачом, врядли перейду на GPU. В крайнем случае многопоточка.