기계어 어셈블리어(Assembler)와 mmx, fpu 활용한 빠른(fast) memcpy 소스코드를 작성해봅니다.
개발 일지 |
2002/12/29 어셈을 이용하여 memcpy 제작함 mmx 와 fpu 의 속도에 의심이 들어 관련 자료 찾아봄 과연 mmx 기술은 언제 어느 상황에서 사용할 수 있는 것인가...... 아래와 같은 글이 사실이라면 대용량 복사작업을 제외하면 사용할 수 있는 곳은 거의 없다고 판단됨...... MMX는 FPU 레지스터를 이용하는 것인데 FPU를 안 쓴다니 무슨 뜻인지 모르겠습니다. 매뉴얼을 좀더 자세히 읽어보시길 바랍니다. 그리고 emms 명령이 100클럭이 소요된다는 근거는 어디에 있는 것인가요? 물론 수 킬로 바이트 정도의 작은 데이터를 복사하는데 MMX나 SSE를 쓰는 것은 어리석은 일임은 이미 증명되어 있습니다. :) 일반 i386 명령으로 1024바이트를 복사하는 데 15000클럭이 소요되고, MMX 명령으로 같은 바이트를 복사하는 데 25000클럭이 소요되었다고 하면 어느 쪽이 빠른 코드인지는 자명하지 않나요? 이게 의미 없는 통계라면 최적화 자체가 무의미합니다. 가령 8바이트를 한꺼번에 복사하는 movq같은 명령은 그 자체로 보면 일반적인 4바이트 복사 명령인 mov를 2번 쓰는 것보다 이론적으로는 최고 2배, 또는 적어도 몇 십%는 빠를 것 같은 예감이 들지만, 실제로는 거꾸로 수십 배나 느렸습니다(물론 또 특수한 경우에서는 몇 배 빠르다고 했지만 그런 경우는 아직 발견할 수 없었습니다). 이 현상을 가져오는 여러 가지 원인 중 제가 짐작할 수 있었던 것은 MMX 명령어의 긴 길이, 파이프라인에서의 문제, 캐시 라인, 셋업 시간 같은 것들뿐입니다. 그러나 프로세서 내부의 동작은 제조사의 기밀에 해당하는 문제이므로, 외부적으로는 분석하기가 매우 어려울 것입니다. |
소스코드 |
// aaa.cpp : Defines the entry point for the console application. // /* MMX는FPU 레지스터를이용하는것인데FPU를안쓴다니무슨뜻인지 모르겠습니다. 매뉴얼을좀더자세히읽어보시길바랍니다. 그리고emms 명령이100클럭이소요된다는근거는어디에있는것인가요? 물론수킬로 바이트정도의작은데이터를복사하는데MMX나SSE를쓰는것은어리석은 일임은이미증명되어있습니다. :) 일반i386 명령으로1024바이트를복사하는데15000클럭이소요되고, MMX 명령으로같은바이트를복사하는데25000클럭이소요되었다고 하면어느쪽이빠른코드인지는자명하지않나요? 이게의미없는 통계라면최적화자체가무의미합니다. 가령8바이트를한꺼번에복사하는movq같은 명령은그자체로보면일반적인4바이트복사명령인mov를2번쓰는 것보다이론적으로는최고2배, 또는적어도몇십%는빠를것같은예감이 들지만, 실제로는거꾸로수십배나느렸습니다(물론또특수한경우에서는 몇배빠르다고했지만그런경우는아직발견할수없었습니다). 이현상을 가져오는여러가지원인중제가짐작할수있었던것은MMX 명령어의긴 길이, 파이프라인에서의문제, 캐쉬라인, 셋업시간같은것들뿐입니다. 그러나프로세서내부의동작은제조사의기밀에해당하는문제이므로, 외부적으로는분석하기가매우어려울것입니다. http://www.kr.freebsd.org/ml/archive/hackers/2001/10/msg00023.shtml http://www.kr.freebsd.org/ml/archive/hackers/2001/10/msg00050.shtml */ #include "stdafx.h" #include "aaa.h" #include "mmsystem.h" #include "Global.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // The one and only application object CWinApp theApp; using namespace std; void memcpy3( void* dest, void* src, int num ) { if (num <= 0) return;
_asm { mov edi, dest; // 목적지저장 mov esi, src; // 소스저장 mov ecx, num; // 반복회수 mov edx, ecx; // 반복횟수복사(스텍->스텍이빠르다.) shr ecx, 1; // dword 복사하므로/4 해야하므로쉬프트한다. shr ecx, 1; // ... cld; // 복사순서설정 rep movsd; // DWORD 단위복사 mov ecx, edx; // 저장해둔크기위치복구 and ecx, 0x03; // 크기% 4 한값얻기(하위비트) rep movsb; // 바이트단위복사 } } void memcpy2( void* dest, void* src, int num ) { if (num <= 0) return;
_asm { mov edi, dest; // 목적지저장 mov esi, src; // 소스저장 mov ecx, num; // 반복회수 mov edx, ecx; // 반복횟수복사(스텍->스텍이빠르다.) shr ecx, 2; // dword 복사하므로/4 해야하므로쉬프트한다. cld; // 복사순서설정 rep movsd; // DWORD 단위복사 mov ecx, edx; // 저장해둔크기위치복구 and ecx, 0x03; // 크기% 4 한값얻기(하위비트) rep movsb; // 바이트단위복사 } } int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) { initGlobal(); int i,j; int count = 3000; int length = 50000; char* sz1[1000]; char* sz2[1000]; for( i=0; i<1000; i++ ) { sz1[i] = new char[ length ]; sz2[i] = new char[ length ]; for( j=0; j<length; j++ ) { sz1[i][j] = j; sz2[i][j] = j; } } DWORD start, end; start = timeGetTime(); for( i=0; i<count; i++ ) { memcpy3( sz2[i%1000], sz1[i%1000], length ); } end = timeGetTime() - start; printf( "memcpy3: %d\n", end ); start = timeGetTime(); for( i=0; i<count; i++ ) { memcpy2( sz2[i%1000], sz1[i%1000], length ); } end = timeGetTime() - start; printf( "memcpy2: %d\n", end ); start = timeGetTime(); for( i=0; i<count; i++ ) { memcpy( sz2[i%1000], sz1[i%1000], length ); } end = timeGetTime() - start; printf( "memcpy: %d\n", end ); // globals.h -- 그다지최적화되지않은MMX FPS 코드.... setFPUcpy(); start = timeGetTime(); for( i=0; i<count; i++ ) { fastcpy( sz2[i%1000], sz1[i%1000], length ); } end = timeGetTime() - start; printf( "setFPUcpy: %d\n", end ); setMMXcpy(); start = timeGetTime(); for( i=0; i<count; i++ ) { fastcpy( sz2[i%1000], sz1[i%1000], length ); } end = timeGetTime() - start; printf( "setMMXcpy: %d\n", end ); for( i=0; i<1000; i++ ) { delete[] sz1[i]; delete[] sz2[i]; } Sleep(1000); return 0; } |
'Assembly' 카테고리의 다른 글
mov와 lea 명령어 차이 (1) | 2011.02.27 |
---|---|
펌) 어셈 스트링과 배열 및 어셈으로 메모리 카피 (0) | 2009.08.16 |
어셈 명령어 정리 (1) | 2009.08.16 |