2011-02-15 4 views
11

SSE (2/3) 레지스터에 컴파일 타임 상수 float을로드하는 효율적인 방법을 알아 내려고하고있다. 이런 간단한 코드를 시도해 보았습니다.로드 상수가 SSE 레지스터에 뜬다

const __m128 x = { 1.0f, 2.0f, 3.0f, 4.0f }; 

그러나 메모리에서 4 개의 movss 명령을 생성합니다! 메모리 부족 (?!?!)

비록 이렇게 상기 스칼라로드

movss  xmm0,dword ptr [[email protected] (14048E534h)] 
movss  xmm1,dword ptr [[email protected] (14048E530h)] 
movaps  xmm6,xmm12 
shufps  xmm6,xmm12,0C6h 
movss  dword ptr [rsp],xmm0 
movss  xmm0,dword ptr [[email protected] (14048E52Ch)] 
movss  dword ptr [rsp+4],xmm1 
movss  xmm1,dword ptr [[email protected] (14048E528h)] 

..

float Align(16) myfloat4[4] = { 1.0f, 2.0f, 3.0f, 4.0f, }; // out in global scope 

가 발생한다. 나는 터치 메모리를 고르게하지 않는 방법이 될 그냥 바로 스타일의 지시와 함께 할 것입니다 자신의 상수가있는 경우

movaps  xmm5,xmmword ptr [::myarray4 (140512050h)] 

이상적으로는 좋은 것 (예를 들어, 명령어 자체로 컴파일 상수).

감사

이와 같은
+2

고성능 SSE/2 코드의 경우 GCC/ICC를 사용하는 것이 좋습니다. 이유에 대한 자세한 내용은 다음을 참조하십시오. - http://www.liranuna.com/sse-intrinsics-optimizations-in-popular-compilers/ – LiraNuna

답변

6

강제로로드하려면 (gcc) 시도해보십시오.

__attribute__((aligned(16))) float vec[4] = { 1.0f, 1.1f, 1.2f, 1.3f }; 
__m128 v = _mm_load_ps(vec); // edit by sor: removed the "&" cause its already an address 

Visual C++를 사용하는 경우 __declspec(align(16)) 적절한 제약을 요구하는 것.

내 시스템에서

, 이것은 (gcc -m32 -msse -O2 컴파일, 모든 클러 코드 여전히 결국 하나의 movaps을 유지에 더 최적화)는 다음 어셈블리 코드 (GCC/& T 구문 AT)을 작성하지 :

andl $-16, %esp 
    subl $16, %esp 
    movl $0x3f800000, (%esp) 
    movl $0x3f8ccccd, 4(%esp) 
    movl $0x3f99999a, 8(%esp) 
    movl $0x3fa66666, 12(%esp) 
    movaps (%esp), %xmm0 

stackspace를 할당하고 거기에 상수를 넣기 전에 stackpointer를 정렬합니다. 컴파일러에 따라 __attribute__((aligned))을 출력하지 않으면 잘못된 코드가 생성 될 수 있으므로주의해서 해체를 확인하십시오.

는 또한 :
당신이 코드에 상수를 넣어하는 방법을 을 요청했습니다 때문에, 단순히 float 배열의 static 규정과 위를보십시오.

movaps vec.7330, %xmm0 
    ... 
vec.7330: 
    .long 1065353216 
    .long 1066192077 
    .long 1067030938 
    .long 1067869798 
2

일반적으로 상수 전에 어떤 루프 또는 코드의 "핫"부분에로드 할 것이다, 그래서 성능이 중요한 것을 안됩니다. 그러나 루프 내에서 이런 종류의 일을 피할 수 없다면 먼저 _mm_set_ps을 시도하고 그 결과를 확인하십시오. 또한 더 나은 코드를 생성하기 때문에 gcc보다는 ICC를 시도하십시오.

+0

Visual Studio를 사용하고 _mm_set_ps가 더 많은 movss를 생성합니다. 나는 비주얼 스튜디오 컴파일러가 꽤 끔찍하다고 생각한다. – coderdave

+2

@coderdave : yes Visual Studio는 꽤 나쁜 SSE 코드를 생성합니다 - 모든 종류의 바보 같은 ABI 제한 및 다른 성가심을 가지고 있기 때문에 SSE에 사용하는 것이 고통 스럽습니다 - 가능하면 gcc 이상인 ICC 사용 –

3

먼저 어떤 최적화 수준에서 컴파일하고 있습니까? -O0이나 -O1에서 이런 종류의 코드 건을 보는 것은 드문 일이 아니지만, 대부분의 컴파일러에서 -O2 이상의 코드 건에서 볼 때 매우 놀랍습니다.

둘째, SSE에는 즉시로드가 없습니다. GPR에 즉시로드 할 수 있지만 그 값을 SSE로 옮길 수는 있지만 실제로로드하지 않고 다른 값을 생성 할 수는 없습니다. 0 또는 (int)-1과 같은 특정 값은 무시하고 논리 연산을 통해 생성 할 수 있습니다.

마지막으로 , 최적화가 켜져 있고 성능이 중요한 위치에 나쁜 코드가 생성 된 경우 컴파일러에 버그를 신고하십시오.

+0

내가 가장 확실히 컴파일 중입니다. -02 그래서 Visual Studio의 코드 생성이 좋지 않은 것 같습니다. 더 많은 조사를 해보면 이것이 합의이며 대부분의 사람들은 SSE에 VC를 사용하지 않고 어셈블리 나 다른 컴파일러를 사용하지 않습니다. – coderdave

+0

@coderdave : VS에 대해 버그를 신고하십시오. MS가 문제에 자원을 투자해야한다는 것을 알 수있는 유일한 방법은 사람들이 그것에 대해 불평 할 때입니다. –

+0

SSE는 즉각적인로드를하지 않지만 ('pxor'는 0을,'pcmpeq '는 1을 제외하고) 메모리에서로드합니다. 즉 _mm_load_ps()는 스택에 배열을 만드는 데 아무런 문제가 없습니다. 거기에서 SSE 레지스터를로드합니다. –

2

상수를 생성하는 것이 훨씬 간단 (빠르게) 네 개의 플로트 상수가 같은 경우 : 그 다음 어셈블리를 생성합니다. 예를 들어 1.f의 비트 패턴은 0x3f800000입니다. 한 가지 방법이 내가이 버전은 어떤 빨리 SSE2보다 경우 possitive 아니에요 SSE2

 register __m128i onef; 
     __asm__ ("pcmpeqb %0, %0" : "=x" (onef)); 
     onef = _mm_slli_epi32(onef, 25); 
     onef = _mm_srli_epi32(onef, 2); 

SSE4.1 또 다른 방법은,

 register uint32_t t = 0x3f800000; 
     register __m128 onef; 
     __asm__ ("pinsrd %0, %1, 0" : "=x" (onef) : "r" (t)); 
     onef = _mm_shuffle_epi32(onef, 0); 

주를 사용하여 생성 할 수 있습니다하지 않은 그것을 프로파일 링하고, 결과가 정확하다는 것을 테스트했다.

각 4 개의 부동 소수점의 값이 달라야하는 경우 각 상수를 생성하고 함께 섞거나 혼합 할 수 있습니다.

웨더 (waether) 또는 유용하지 않은 것은 캐시 미스가 가능한지 여부에 달려 있습니다. 그렇지 않으면 메모리에서 상수를로드하는 것이 더 빠릅니다. 이 트릭은 vmx/altivec에서 매우 유용하지만, 대부분의 PC에있는 큰 캐시는 sse에 덜 유용 할 수 있습니다.

Agner Fog 's Optimization Manual, 2 권, 13.4, http://www.agner.org/optimize/에 대한 좋은 토론이 있습니다.

위의 인라인 어셈블러 사용은 컴파일러 경고를 생성하지 않고 초기화되지 않은 변수를 사용할 수 있도록하는 gcc에서만 사용할 수 있습니다. vc를 사용하면 _mm_setzero_ps()로 변수를 초기화해야 할 수도 있고하지 않을 수도 있습니다. 그런 다음 옵티마이 저가이를 제거 할 수 있기를 바랍니다.

관련 문제