2012-07-13 6 views
1

char buf[12];은 왼쪽에 공백이있는 오른쪽 정렬 부호없는 번호가 항상 있다고 가정합니다. 예 : _________329 (여기서 _은 공백을 나타냄). 나는 그것을 구문 분석 생각할 수있는 가장 빠른 방법은 다음과 같이 함께 :고정 길이의 char 배열을 부호있는 정수로 참조하는 패딩 된 ascii 숫자가있는 경우이를 변환하는 가장 빠른 방법은 무엇입니까?

while (*buf == ' ') buf++; 
atoi(buf); 

하지만 우리가 생각하지 않는 atoi 부호 알고 주어진 특히 atoi으로 빠른 방법이 있다면 궁금 해서요 ..

+0

'문자열'을 사용할 수 없습니까? –

+5

'atoi'는 선행 공백을 건너 뜁니다. 왜 당신의 루프가 그것보다 더 빠를까요? –

+0

@JamesKanze - 아마 그 숫자가 대개 6 자 미만이라는 것을 알고있을 것입니다. 그래서 문자열의 끝에서부터 시작하는 것이 더 빠릅니까? 또한 'leading spaces'는 아마도'''에 대한 테스트보다 확실히 느린'isspace()'에 대한 호출 일 것입니다. –

답변

6

첫 번째 문자가 "잠재적 인 기호"로 예약되어 있고 항상 "공백"이라고 가정합니다. 그렇지 않으면 char[12] 대신 char[11] 만 필요합니다. & 15 트릭 공간 처리와 동일하게 0과 ASCII (공간 = 32, 제로 = 48) 및 EBCDIC (공간 모두에서 작동한다고

unsigned parse(const char(&b)[12]) 
{ 
    return ((((((((((b[1] & 15)) 
      * 10 + (b[2] & 15)) 
      * 10 + (b[3] & 15)) 
      * 10 + (b[4] & 15)) 
      * 10 + (b[5] & 15)) 
      * 10 + (b[6] & 15)) 
      * 10 + (b[7] & 15)) 
      * 10 + (b[8] & 15)) 
      * 10 + (b[9] & 15)) 
      * 10 + (b[10]& 15); 
} 

주 = 48, 제로 어쨌든, 고정 크기는 수동 루프 언 롤링 허용 = 240). 나는 아직 다른 문자 인코딩을 체크 아웃하지 않았다.

실제로 이것은 atoi보다 빠르거나 느린가? 알아내는 유일한 방법은 측정하는 것입니다. 하지만 표준 함수를 사용하면 항상 가독성이 향상되므로 어쨌든 atoi으로 유지됩니다.

+0

나는 이것과 비슷한 것을했습니다. –

+1

그리고 실제로 어떤 속도를 얻었습니까? – fredoverflow

+0

예, 기본적으로 2x2 메모 매트릭스 방식입니다. –

0

이 코드가 빨라집니다 될 수있다

char *begin = strrchr(buf, ' '); 
atoi(begin ? begin : buf); 

는 빠른 플랫폼에 최적화 된 표준 함수 strrchr 가정.

1

먼저이 작업을 수행하고 있는지 자문 해보십시오. 'buf'가 큰 길이의 파일 인 경우 Schmiel the Painter algorithm으로 고통을 겪을 수 있으며 10 진수가 많으면 예를 들어 큰 숫자를 곱하면 문제가 발생할 수 있습니다. GMP (부호있는 정수 연산의 경우 mpz 섹션 참조)

둘째, 표준 라이브러리가 알지 못하는 것을 고려하십시오. Dmitry suggests을 사용하면 'strrchr'이라는 빠른 플랫폼을 사용하지만 문자열을 반복하는 문제를 해결하기 위해 strrchr이 수행 할 수있는 것은 없으며 strrchr에는 실제로 종료 문자를 검색하는 것과 같은 추가 제약 조건이 있습니다.

당신이 좋아하는 몇 가지를 알고있을

:

  • 당신의 숫자는 음수가 될하지 않습니다; 즉 atoi는 +/- 부호를 선택할 필요가 없습니다. 그러나 이것을 정확히 지적했는데, 아마도 이것은 타이밍의 중요한 요소가 아닙니다.
  • 숫자가 대부분 짧거나 대부분 길어서 문자열 시작 부분이나 끝 부분에 공백을 찾기 시작해야하는지 여부가 결정됩니다. 특히 strrchr은 문자열의 길이를 알지 못하므로 항상 (Newlib에서) 찾고있는 atoi의 구현과 마찬가지로 처음부터 읽습니다. 또한 예제 코드는 문자열의 처음부터 검색을 의미합니다.
  • 숫자는 항상 10 진수입니다. 그러면 약간의 수학 연산이 제거됩니다.
  • 숫자는 항상 부호없는 길이가됩니다. 네, 12 문자이기 때문에 이것은 보장됩니다,하지만 atoi는 이것을 모르고 오류를 처리하려고 시도 할 것입니다.또한 atoi()은 부호있는 정수를 반환하므로 1,000,000,000과 같은 13 비트 숫자가 필요한 경우에 대비해야합니다.
  • 다른 생각이 들지 않았습니다. 그러나 당신은 할 수 있습니다.

소스을 읽고 부터 시작해야합니다. 이 간단한 운동으로 많은 것을 얻을 수 있습니다! 나는 최근에 Newlib으로 작업 해 왔으며 다운로드하여 열어 놓았습니다. 그래서 제가 언급하겠습니다. 그러나 GNU의 glibc과 Windows가 사용하는 것은 다를 것입니다.

언뜻보기에 atoistrtol 또는 'string to long'(int와 long은 모두 내 플랫폼에서 32 비트이고 'long long'은 더 큰 것을 얻는 데 필요합니다.). 컴파일러는이를 직접 호출에 최적화 할 것이지만 사이클을 절약 할 수 있습니다. 표면 상 속도에 민감한 애플리케이션의 경우 strtol()을 바로 호출하면됩니다. 또는 오히려 strtoul으로 'string to unsigned long'으로 전화하십시오. 주목할만한 다른 것을 호출하지 않는 함수가 생겼으므로 이제 살펴 보겠습니다. 지금은 재진입 정보를 무시하십시오. 괄호로 조심하고, 일부 ifs 그들과 관련된 elses 부족 (이는 가난한 스타일의 IMO, 나는 괄호를 사방에 선호).

unsigned long _strtoul_r 
    (struct _reent *rptr, _CONST char *nptr, char **endptr, int base) 
{ 
    register const unsigned char *s = (const unsigned char *)nptr; 
    register unsigned long acc; 
    register int c; 
    register unsigned long cutoff; 
    register int neg = 0, any, cutlim; 

    /* 
    * See strtol for comments as to the logic used. 
    */ 
    do { 
    c = *s++; 
    } while (isspace(c)); 
    if (c == '-') { 
    neg = 1; 
    c = *s++; 
    } else if (c == '+') 
    c = *s++; 
    if ((base == 0 || base == 16) && 
    c == '0' && (*s == 'x' || *s == 'X')) { 
    c = s[1]; 
    s += 2; 
    base = 16; 
    } 
    if (base == 0) 
    base = c == '0' ? 8 : 10; 
    cutoff = (unsigned long)ULONG_MAX/(unsigned long)base; 
    cutlim = (unsigned long)ULONG_MAX % (unsigned long)base; 
    for (acc = 0, any = 0;; c = *s++) { 
    if (isdigit(c)) 
     c -= '0'; 
    else if (isalpha(c)) 
     c -= isupper(c) ? 'A' - 10 : 'a' - 10; 
    else 
     break; 
    if (c >= base) 
     break; 
    if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) 
    any = -1; 
    else { 
     any = 1; 
     acc *= base; 
     acc += c; 
    } 
    } 
    if (any < 0) { 
    acc = ULONG_MAX; 
    rptr->_errno = ERANGE; 
    } else if (neg) 
    acc = -acc; 
    if (endptr != 0) 
    *endptr = (char *) (any ? (char *)s - 1 : nptr); 
    return (acc); 
} 

함수 정의에서 시작하여 우리의 응용 프로그램이 단일 스레드 인 경우 제거 할 수있는 재진입 관행이 있음을 알 수 있습니다. 우리가 필요로하지 않는 문자열을 가리키는 포인터를 저장하는 char ** ptr 인자도 있습니다. 또한 길이 정의가 없기 때문에 문자열의 길이를 찾기 위해 널 문자를 검색해야합니다.

이 응용 프로그램에서 * s는 내 플랫폼에서 의미가 있지만 사용자의 것 같지 않은 레지스터로 정의됩니다. 우리가 필요로하지 않는 다른 정수도 정의되어 있습니다.

do/while 루프에는 공백, 가로 탭, 줄 바꿈, 세로 탭, 피드 및 캐리지 리턴 문자를 확인하는 isspace()이 호출됩니다. 공간 만 있으면됩니다. 또한 현의 앞에서 시작하여 뒤로 돌아갑니다. 우세하게 작은 숫자가 있다면 그것을 바꿔라.

그런 다음 기본 테스트를 수행합니다. 베이스는 0이 될 수 있으며,베이스 (사이클을 필요로 함)를 자동으로 감지 할 수 있으며, 8 또는 16이면 앞에있는 '0'또는 '0x'를 앞뒤로 입력 할 수 있습니다.

다음으로 'cutoff'및 'cutlim'변수를 만듭니다. 표면 상으로는 범위 검사가 필요 없기 때문에 이들을 필요로하지 않습니다.

마지막으로 for 루프를 종료합니다. 문자 유형과 숫자 값을 결정하는 if \ else if \ else 블록이 있으며 isdigit, isalphaisupper 함수가 있습니다. 이것들은 약간의 로케일 종속적 인 코드를 포함합니다; if/else if/else 블록 전체를 하나의 c -= 0 문으로 대체하는 10 진수 값을 가정 할 수 있습니다.

다음으로 오류 검사에 대한 자세한 내용은 if (c >= base)이며 ischeap은 유지 보수에 도움이 될 수 있습니다. C는 부호가 없으므로, 예를 들어 *가 '0', 0x30보다 작은 공백 (0x20) 인 경우이 값은 (부호가없는) (0x30 - 0x20) = 255 - 10으로 계산됩니다. 베이스 (10)보다 크다. 그것은 완벽하지는 않지만 꽤 좋고 값이 쌉니다.

다음으로 if (any... 블록을 검사하는 경계가 있습니다. 그런 다음 함수의 실제 고기 (acc *= base; acc += c;)가 표시됩니다.이것을 최적화하기 위해 할 수있는 방법은 거의 없지만, 바이너리 기반이 있다면이 값을 교대로 변환 할 수 있습니다. 다행히 Arduino ISR이라면 프로세서에 빠른 하드웨어 멀티 플라이어를 사용하는 것이 좋을 것입니다. 멀티 플라이 축적 (multiply-accumulate)과 같은 DSP 어셈블리 명령어를 살펴보면 속도 향상을 기대할 수 있습니다.

for 루프 이후에 우리가 무시할 수있는 음수의 오류 처리와 처리가 더 있습니다.

그래서, 요약, 당신은 그것을 많이하고 있다면 나는 당신의 특별한 경우를 처리 할 수있는 새로운 기능을 써서 :

atoi의들이 generic을 받아
unsigned long TwelveCharDecimalStringWithLeadingSpacestoul(char *nptr) 
{ 
    register const unsigned char *s = (const unsigned char *)nptr; 
    register unsigned long acc; 
    register int c, base = 10; 

    do { 
    c = *s++; 
    } while (c == ' '); 

    for (acc = 0;; c = *s++) { 
    c -= '0'; 
    if (c >= base) { 
     _errno = ERANGE; 
     acc = -1; 
     break; 
    } 
    acc *= base; 
    acc += c; 
    } 
    return (acc); 
} 

은 가정을 사용하는 당신 좀 더 빨라졌습니다.

unsigned long result = 0; 
char *begin = strrchr(buf, ' '); 
result = strtoul(buf, NULL, 10); 

if (result == 0 && errno == ERANGE) 
    // Handle error 

편집 :이 작업이 엄청 많이 일어난다, 또는 매우 빠르게 발생하는 않는 한, 당신은 훨씬 간단, 명확하게,보다 안전하고 유연하며 일반적으로 더 나은 형편이 아마 더 좋을 것 같아 나는 글쓰기를 마치고 FredOverflow has posted a better answer에 주목합니다. 루프를 실행하면 (필자는 그렇게하지 않았지만 필요하지는 않지만 필요한 경우 알려진 루프가 반복 될 수 있음) & 15이라는 깔끔한 트릭을 사용합니다. 그러나, 위의 함수는 일반적인 경우에 일부 표준 라이브러리 호출 속도를 높이는 방법에 대한 접근 방법을 보여줍니다.

+0

이것은 놀라운 답변입니다 ... 감사합니다. –