2014-10-07 4 views
1

어셈블리 언어 프로그래밍에 익숙하지 않고 숙련 된 어셈블리 언어 사용자에게 명백한 문제에 직면 해 있습니다. 나는 100 바이트 버퍼를 가지고 있고 n = 1에서 5까지 매 n 번째 바이트의 합을 찾고 결과를 5 개의 정수 배열에 저장할 필요가있다. 이렇게하려면 내 C++ 코드에서 인라인 어셈블리를 사용해야합니다. I는 다음 코드를 작성 : 결과 끝에 그래서C++의 인라인 어셈블리를 사용하는 버퍼의 합계

void main() 
{ 
    char *buffer = new char[100]; 
    int *result = new int[5](); 

    for (int i = 0; i < 100; i++) 
     buffer[i] = i; 

    char *start = buffer; 
    for (int k = 0; k < 20; k++) 
    { 
     for (int j = 0; j < 5; j++) 
     { 
     __asm 
      { 
       mov eax, start[j] 
       mov ebx, result[j] 
       add eax, ebx 
       mov result[j], eax 

      } 

     } 
     start += 5; 
    } 
} 

를 [0] [5], 버퍼 [0], 버퍼 버퍼의 합을 가져야한다 [10] ... [1] 것 및 결과 버퍼 [1], 버퍼 [6], 버퍼 [11] ...의 합계. 첫 번째 어셈블리 명령어 (mov eax, start [j])에서 액세스 위반 오류가 발생합니다. 누구든지 제가 한 실수를 찾아 낼 수 있습니까? 또한 누군가가 어셈블리 언어로 전체 루프 및 합계 부분을 작성하는 데 도움을 줄 수 있다면 큰 도움이 될 것입니다.

+0

정말 원하는가요? 그게 아니기 때문에, 매 n 번째 바이트의 합계로 이해할 수있을 것입니다. – dornhege

+0

어셈블리에서 수행 할 작업은 얼마입니까?어셈블리에 숫자 만 추가하고 있습니다. –

+0

이것은 내가하고 싶은 것을 단순화 한 것입니다. 지금은 어셈블리에서 루프와 합계를 쓸 수 있다면 행복 할 것입니다. 나는 정상적인 C++ 코드에 비해 성능상의 이점이별로 없다는 것을 이해하지만,이 개념을 사용하려는 실제 시나리오는 약간의 이점을 가져올 수 있습니다. – Rahul

답변

0

분명히 나는 ​​실제 의도를 알지 못하지만 분명히 "사용하려는 실제 시나리오 이 개념은 약간의 이점을 가져올 수 있습니다. "

사람이 i386 용 효율적인 어셈블러를 더 이상 쓸 수 없다는 말은 100 % 정확하지 않을 수도 있지만 거의 사실입니다. 파이프 라이닝 및 순서가 잘못된 실행에 익숙하다면 왜 이것이 그렇게 중요한지 이미 알 것입니다. 이것들에 익숙하지 않다면 이미 효율적인 어셈블러를 작성하는 방법을 모른다는 말입니다.

프로그램의 핫스팟을 위해 어셈블러를 보지 말아야한다는 것은 아닙니다. 그러나 당신이 할 수있는 가장 효율적인 C 코드를 작성하고 그것을 벤치마킹 한 후에 당신이 무언가를 더 잘 쓸 수 있는지 알아 내려고 노력해야한다. 할 수 없다면 놀라지 마십시오.

  • 작은 테스트 프로그램에서 일부 작은 루틴이 더 잘 수행되기 때문에 원래 프로그램에 다시 포함될 때 그렇게하는 것은 아닙니다.
  • 또는 모든 프로세서에서 성능이 향상 될 것입니다.
  • 또는 새 버전의 컴파일러.
  • 그리고 물론 asm을 사용한다는 것은 새로운 플랫폼 (x64와 같은)으로 옮겨가는 것은 아마도 asm이 다시 쓰여져서 작업하는 사람들이 당신의 이름을 저주하게 만드는 것을 의미합니다.

그렇다면 이러한 벤치마킹을 시도해 볼 수 있습니다. 내 이 더 좋을 것이라고 추측하지만 그것은 단지 추측입니다.

#include <stdio.h> 
#include <stdlib.h> 

#define MAXSIZE 100 
#define MAXTOT 5 

typedef unsigned char BYTE; 

int main() 
{ 
    BYTE *buffer = (BYTE *)malloc(MAXSIZE); 
    const BYTE *start = buffer; 
    unsigned int t0, t1, t2, t3, t4; 

    for (int i = 0; i < MAXSIZE; i++) 
     buffer[i] = i; 

    t0 = 0; 
    t1 = 0; 
    t2 = 0; 
    t3 = 0; 
    t4 = 0; 

    for (int j=0; j < (MAXSIZE/MAXTOT); j++) 
    { 
     t0 += start[0]; 
     t1 += start[1]; 
     t2 += start[2]; 
     t3 += start[3]; 
     t4 += start[4]; 

     start += MAXTOT; 
    } 

    printf("%u %u %u %u %u\n", t0, t1, t2, t3, t4); 

    free(buffer); 

    return 0; 
} 

루프는 (GCC -O2 사용) ASM에서 다음과 같다 :

L3: 
    movzbl (%edx), %edi 
    addl $5, %edx 
    addl %edi, 44(%esp) 
    movzbl -4(%edx), %edi 
    addl %edi, %ebx 
    movzbl -3(%edx), %edi 
    addl %edi, %eax 
    movzbl -2(%edx), %edi 
    addl %edi, %ecx 
    movzbl -1(%edx), %edi 
    addl %edi, %esi 
    cmpl 40(%esp), %edx 
    jne  L3 

이 오히려 (이것은 가능한 한 계산 기간 동안 레지스터에 "결과"만큼 유지 계속해서 기존 코드와 같이 메모리에 모두 읽기/쓰기). 더 적은 수의 루프는 또한 cmp 명령어가 적다는 것을 의미하며, 이것은 버퍼 대신 5를 사용하여 하나의 패스를 만든다. x64 용으로 컴파일하면 훨씬 더 쉽게 사용할 수있다. 왜냐하면 더 많은 레지스터가 있기 때문이다.

분명히 MAXTOT이 커지면이 부분이 떨어져 버립니다. 그러나 내가 볼 수있는 코드에 대해서만 언급 할 수 있으며, 5는 사용했던 코드입니다.

FWIW.