저는 6 비트에서 8 비트의 32 비트 정수를 32 비트 실수로 변환해야하는 경우가 종종 있습니다. 필자는 델파이 코드를 사용자 정의 어셈블러 코드로 대체했으며 FPU 변환은 항상 빠르며 일부 컴퓨터에서는 SSE 변환보다 빠릅니다. 변환하는 동안 스케일링 (그래서 곱셈)이있을 필요가SSE : 대량 정수 변환 + FPE보다 SSE가 더 느리게 곱합니까?
program Project1;
{$R *.res}
uses
windows,dialogs,sysutils;
type
piiii=^tiiii;
tiiii=record i1,i2,i3,i4:longint; end;
pssss=^tssss;
tssss=record s1,s2,s3,s4:single; end;
var
convert_value:single=13579.02468;
function convert_x87(adata:longint):single;
asm
mov [esp-4],eax
fild longint([esp-4])
fmul [convert_value]
end;
procedure convert_sse(afrom,ato,aconv:pointer);
asm
CVTDQ2PS xmm0,[eax]
mulps xmm0,[ecx]
movaps [edx],xmm0
end;
procedure get_mem(var p1,p2:pointer);
begin
getmem(p1,31);
p2:=pointer((longint(p1)+15) and (not 15));
end;
var
a,b,c,d:cardinal;
z:single;
i:piiii;
s1,s2:pssss;
w1,w2,w3:pointer;
begin
b:=gettickcount;
a:=0;
repeat
z:=convert_x87(a);
inc(a);
until a=0;
c:=gettickcount-b;
get_mem(pointer(w1),pointer(i));
get_mem(pointer(w2),pointer(s1));
get_mem(pointer(w3),pointer(s2));
s1.s1:=convert_value;
s1.s2:=convert_value;
s1.s3:=convert_value;
s1.s4:=convert_value;
b:=gettickcount;
i.i1:=0;
i.i2:=1;
i.i3:=2;
i.i4:=3;
repeat
convert_sse(i,s2,s1);
inc(i.i1,4);
inc(i.i2,4);
inc(i.i3,4);
inc(i.i4,4);
until i.i1=0;
d:=gettickcount-b;
freemem(w1);
freemem(w2);
freemem(w3);
showmessage('FPU:'+inttostr(c)+'/SSE:'+inttostr(d));
end.
이 하나가 이유입니다 : 여기에 보여 일부 코드입니다. 사용 된 값은 내가 선택한 임의의 값이지만 사용 된 값에 관계없이 결과는 동일합니다. 또한 FPU와 SSE 사이의 라운딩에는 매우 작은 차이점이 있지만이 경우 중요하지 않습니다.
그러나이 코드를 실행하면 FPU 경로가 SSE 경로보다 느리지 않으며 의미가없는 것을 볼 수 있습니다. 누구나 무슨 일이 일어나고 있는지 알 겠어?
편집 : 여기 어셈블러에서 루프와 다른 소스 코드입니다. 결과는 정말 흥미 롭습니다. 증가 지침이 주석하는 경우, SSE 버전은 눈에 띄는만큼 FPU 버전보다 빠르지 만 증가 지침은 다음 포함 된 경우 그들은 거의 같은 속도입니다
program Project1;
{$R *.res}
uses
windows,dialogs,sysutils;
type
piiii=^tiiii;
tiiii=record i1,i2,i3,i4:longint; end;
pssss=^tssss;
tssss=record s1,s2,s3,s4:single; end;
var
convert_value:single=13579.02468;
procedure test_convert_x87;
asm
// init test data
push ebx
xor ebx,ebx
mov [esp-4],$98765432
// convert and multiply 1 int32 to 1 single
@next_loop:
// inc [esp-4]
fild longint([esp-4])
fmul [convert_value]
fstp single([esp-8])
// loop
dec ebx
jnz @next_loop
pop ebx
end;
procedure test_convert_sse(afrom,ato,aconv:pointer);
asm
// init test data
push ebx
xor ebx,ebx
mov [eax+0],$98765432
mov [eax+4],$98765432
mov [eax+8],$98765432
mov [eax+12],$98765432
// convert and multiply 4 int32 to 4 single
@next_loop:
// inc [eax+0]
// inc [eax+4]
// inc [eax+8]
// inc [eax+12]
cvtdq2ps xmm0,[eax]
mulps xmm0,[ecx]
movaps [edx],xmm0
// loop
sub ebx,4
jnz @next_loop
pop ebx
end;
procedure get_mem(var p1,p2:pointer);
begin
getmem(p1,31);
p2:=pointer((longint(p1)+15) and (not 15));
end;
var
b,c,d:cardinal;
i:piiii;
s1,s2:pssss;
w1,w2,w3:pointer;
begin
b:=gettickcount;
test_convert_x87;
c:=gettickcount-b;
get_mem(pointer(w1),pointer(i));
get_mem(pointer(w2),pointer(s1));
get_mem(pointer(w3),pointer(s2));
s1.s1:=convert_value;
s1.s2:=convert_value;
s1.s3:=convert_value;
s1.s4:=convert_value;
b:=gettickcount;
test_convert_sse(i,s2,s1);
d:=gettickcount-b;
freemem(w1);
freemem(w2);
freemem(w3);
showmessage('FPU:'+inttostr(c)+'/SSE:'+inttostr(d));
end.
빠르고 느린 버전으로 생성 된 어셈블리 코드를 게시하십시오. 이렇게하면 소수의 사람들이 파스칼을 사용하고 있고 당신의 szenario를 쉽게 다시 만들 수 있기 때문에 범람자를 찾기가 훨씬 쉬워집니다. –
안녕하세요. 관심을 가져 주셔서 감사합니다. 게시 된 소스에서 convert_x87 및 convert_sse라는 함수가 어셈블러에 있는지 확인하면 붙여 넣기 할 수 있습니다. 타이밍 부분 만 델파이에 있습니다. – Marladu
convert_sse라는 함수는 한 번에 4 convert + multiply를 수행하며 4 회 호출 한 번에 1을 수행하는 convert_fpu 함수와 속도가 같거나 느립니다.convert_sse 함수에서 사용 된 명령어는 CVTDQ2PS mulps movaps입니다.이 태스크에 대한 올바른 SIMD 명령어가 아닙니까? – Marladu