2013-04-07 1 views
2

두 개의 큰 파일 (8GB 근처)을 하나로 합칩니다. 나는 그것만이 가능한만큼 그것을 최적화하려고 노력한다.C fprintf/fscanf 큰 파일의 속도 최적화

void merge() { 

    char *array[17]= {"q.out","b.out"}; // names of input files 
    FILE *finpt1 = fopen(array[0],"r"), *finpt2 = fopen (array[1],"r"), 
            *foutp = fopen("final_.out","w"); 

    u_int32_t a,b; 

    fscanf(finpt1, "%u", &a); 
    fscanf(finpt2, "%u", &b); 
    int EOF1_my = 0, EOF2_my = 0; 

    while (true) { 
    if (a>b) { 
     fprintf(foutp,"%u\n", b); 
     if (fscanf(finpt2, "%u", &b) == EOF) { EOF2_my = EOF; break; } 
     } else { 
     fprintf(foutp,"%u\n", a); 
     if (fscanf(finpt1, "%u", &a) == EOF) { EOF1_my = EOF; break; } 
     } 
    } 

    if (EOF1_my == EOF) { 
     while (fscanf(finpt2, "%u", &a) != EOF) 
     fprintf(foutp, "%u\n", a); 
    } else if (EOF2_my == EOF) { 
     while (fscanf(finpt1, "%u", &b) != EOF) 
      fprintf(foutp,"%u\n", b); 
    } 

    fclose(finpt1); fclose(finpt2); fclose(foutp); 
} 

나는 printf와 여러 번 호출하면 상당한 자원을 소비하는 의심 (I 로깅 내 프로그램은 원칙적으로하지 않고보다 더 크게 느리게 작동하는 것으로 나타났습니다). 그리고 나는 대부분의 시간이 포맷팅 문자열 (버퍼링이 사용되기 때문에 파일에 쓰지 않음)을 사용한다고 생각합니다.

그래서 나 자신이 메모리에 출력하고 문자열을 작성하는 것이 더 나은지 궁금합니다. fprintf ("% s", string);와 같이 fprintf 함수에 호소하기 위해 파일에 10000 개의 기호를 추가합니다.

나는 fscanf에 관해 동일한 의구심을 가지고 있습니다. 아마도 다른 기능을 사용해야할까요?

모든 의견을 환영합니다. 미리 감사드립니다.

고정 BUG sfstewman에
감사합니다 (질문에 대한 의견주의). 멋지다. 테스트를 시작하지 않거나 결코 가능하지 않을 때까지 내가 알지 못하는 정말 귀중한 정보이다.
코드를 보내 주셔서 감사합니다. 그러나 어쨌든 나에게 재미있는 일없이 나를 남겨 둘 준비가되어있는 코드를 제공합니다.
그건 내 케이크 조각이야!
아이디어는 훨씬 더 가치가 있습니다. 이제는 사전 비교가 무엇인지 알고 있습니다.

+0

버퍼링 작업을 직접하고 싶지 않으면'stdio.h '('man setbuffer')의 표준 버퍼를 확대하면됩니다. –

+0

디스크 입출력은 항상 fprintf()/fscanf()보다 비용이 많이 듭니다.CPU 부하를 확인, 아마'될 것 <10 %' – wildplasser

+1

'fprintf' 버퍼링이며, 출력은 당신이 무슨 일을하는지에 대한 중요한 작업입니다, 그래서 당신이 해지고, 출력 비용을 줄일 수 있다는 것을 의심한다. 전환 비용을 줄일 수 있습니다. 파일의 구조는 무엇입니까? 각 번호는 별도의 줄에 있으며 숫자는 여러 공백 형식으로 구분됩니까? – sfstewman

답변

2

입력 사항은 모두 부호없는 숫자입니다. 즉, 숫자 비교 대신 사전 식 비교를 사용할 수 있습니다.

부호없는 숫자의 문자열에 대한 사전 비교를하려면 먼저 문자열의 길이를 비교하십시오 (짧은 문자열은 작은 숫자 임). 길이가 같으면 strcmp은 더 작은 숫자를 가진 문자열을 나타냅니다. 당신이 숫자 사이의 구분 기호로 줄 바꿈을 사용하는 경우

, 당신은 fscanffprintf에 서식의 비용을 제거, 읽기/쓰기로 fgetsfputs를 사용할 수 있습니다. 이렇게하면 숫자 사이의 모든 변환이 제거됩니다. fgets에 의해 반환 된 문자열의 끝에있는 개행 문자는 모든 숫자에서 일정하며 사전 편집 비교에는 영향을 미치지 않습니다. (더 크게 테스트 세트에,

% time ./merge1 
./merge1 0.89s user 0.08s system 99% cpu 0.974 total 
% time ./merge2 
./merge2 0.18s user 0.08s system 98% cpu 0.264 total 

그리고 :

내가 줄 바꿈으로 구분 된 임의의 부호없는 숫자의 두 9.5M 파일을 생성 및 비교 타이밍 실행 (merge1이 위의 코드이며, merge2은 다음과 같습니다) 0에서 2^30-1) 사이의 임의의 숫자의 두 537M 파일 :

% time ./merge1 
./merge1 51.22s user 4.57s system 54% cpu 1:41.68 total 
% time ./merge2 
./merge2 11.13s user 4.68s system 18% cpu 1:26.81 total 

이 수치 변환이 75 %처럼 시간의 -80 %를 뭔가를하고 있음을 시사한다. 이것이 충분히 빠르지 않다면, 자신의 버퍼링을 수행하고 strchr을 사용하여 구분 기호를 검색하고 메모리 매핑 파일을 사용하여이를 더 최적화 할 수 있습니다.

#include <stdio.h> 
#include <string.h> 

void merge() 
{ 
    char *array[17]= {"q.out","b.out"}; // names of input files 
    FILE *finpt1 = fopen(array[0],"r"), *finpt2 = fopen (array[1],"r"), 
            *foutp = fopen("final_.out","w"); 

    char buf1[32]; 
    char buf2[32]; 

    memset(buf1,0,sizeof(buf1)); 
    memset(buf2,0,sizeof(buf2)); 

    int EOF1_my = (fgets(buf1, sizeof(buf1), finpt1) == NULL); 
    int EOF2_my = (fgets(buf2, sizeof(buf2), finpt2) == NULL); 

    size_t l1 = strlen(buf1); 
    size_t l2 = strlen(buf2); 

    if (!EOF1_my && !EOF2_my) 
    { 
    for(;;) 
    { 
     /* unsigned numbers, so use a lexographic comparison */ 
     int diff = (l1 == l2) ? strcmp(buf1,buf2) : l1-l2; 

     if (diff < 0) 
     { 
     fputs(buf1, foutp); 
     memset(buf1,0,sizeof(buf1)); 
     EOF1_my = (fgets(buf1, sizeof(buf1), finpt1) == NULL); 
     if (EOF1_my) break; 
     l1 = strlen(buf1); 
     } 
     else 
     { 
     fputs(buf2, foutp); 
     memset(buf2,0,sizeof(buf2)); 
     EOF2_my = (fgets(buf2, sizeof(buf2), finpt2) == NULL); 
     if (EOF2_my) break; 
     l2 = strlen(buf2); 
     } 
    } 
    } 

    FILE* frest = NULL; 
    if (!EOF1_my || !EOF2_my) 
    { 
    if (!EOF1_my) 
    { 
     fputs(buf1, foutp); 
     frest = finpt1; 
    } 
    else 
    { 
     fputs(buf2, foutp); 
     frest = finpt2; 
    } 

    memset(buf1,0,sizeof(buf1)); 

    while(fgets(buf1,sizeof(buf1),frest) != NULL) 
    { 
     fputs(buf1,foutp); 
    } 
    } 

    fclose(finpt1); fclose(finpt2); fclose(foutp); 
} 

int main() 
{ 
    merge(); 
    return 0; 
} 
+0

2 * 9M은 아마도 OS의 디스크 버퍼 캐시에 맞을 것입니다. 따라서 쓰기를 위해 하나의 I/O 만 필요합니다. 실제 테스트에는 Cacha를 떨어 뜨리거나 더 큰 테스트 세트를 사용하십시오. – wildplasser

+0

@wildplasser 2 * 537M의 데이터에서 결과는 비슷합니다. – sfstewman

+0

또한 다른 순서로 실행하십시오 (먼저 ./merge2, ./merge1). BTW : 모든 memset() 필요하지 않습니다. – wildplasser