2012-11-14 1 views
1

내 Mac에서 fseek()/fwrite()의 쓰기 성능에 문제가 있습니다. 저는 4GB의 대용량 파일에서 작동하고 있으며 아래 테스트는 120MB의 작은 파일로 수행되었습니다. 디스크 이상한 fseek()/fwrite() 성능 MacOS에서

    • fopen() 새 파일이 제로 사용하여 파일 (소요 ~ 삼초)
    • 이 (4K 각각 30.000 블록) 작은 임의의 위치에 데이터 블록 쓰기를 채우기 다음과 같이 내 전략은

    전체 절차는 약 120 초 걸립니다.

    쓰기 전략 (내 질문 here 참조) 이미지 회전 알고리즘에 바인딩하고 누군가가 회전 문제에 대한 빠른 해결책을 제공하지 않는 한, 나는 fseek()을 사용하는 전략을 변경할 수 아니에요 다음 4K를 쓰고있다 이하로 설정하십시오.

    처음 몇 천인 fseek()/fwrite()은 성능이 뛰어나지 만 시스템 캐시가 채워지는 속도보다 빠 르게 빠 르게 성능이 떨어집니다. 아래 차트는 초당 fwrite() 초와 시간을 초 단위로 보여줍니다. 보시다시피, 7 초 후 fseek()/fwrite() 속도가 약. 초당 200 개이지만 프로세스가 끝날 때마다 초당 100에 도달 할 때까지 계속 진행됩니다. 프로세스의 중간에

    fwrite() per seconds vs time

    (2 ~ 3 회), OS가 나는 약이 그 시간 동안 나는 몇 초 걸려 내 콘솔 출력에서 ​​볼 수있는 디스크에 파일의 내용을 플러시하기로 결정 . 내 디스크에 5MB/s 쓰기 (그리 많지 않음). fclose() 후에 시스템이 전체 파일을 쓰는 것처럼 보입니다. 오랜 기간 동안 20 MB/s 디스크 활동이 나타납니다.

    5.000 fwrite()마다 fflush()을 사용하면 동작이 전혀 변경되지 않습니다. 플러시를 강요하기 위해 fclose()/fopen()을 입력하면 어떻게 든 전체적으로 속도가 빨라집니다. 10 %.

    나는 프로세스 (아래 스크린 샷)를 프로파일 링 했으므로 실제로는 항상 fwrite()fseek() 내부에 머무르며 둘 다 __write_nocancel()까지 드릴 다운 할 수 있습니다.

    Profiling the write function

    완전히 터무니없는

    내 입력 데이터는 완전히 내 버퍼에 맞는 때문에 나는 쓰기를 분리 할 필요없이 선형 내 회전 출력 데이터를 기록 할 수있어 경우를 상상해 요약 과정을 파편으로. 필자는 여전히 fseek()을 사용하여 쓰기 함수의 논리가 그런 식으로 동작하기 때문에 파일 포인터의 위치를 ​​지정하지만이 경우 파일 포인터는 이미 있던 위치와 동일한 위치로 설정됩니다. 성능에 아무런 영향이 없다고 기대할 수 있습니다. 틀린입니다.

    불합리한 점은 해당 특수한 경우에 fseek()에 대한 호출을 제거하면 내 기능이 2.7 초 내에서 120 초가 끝나는 것입니다.

    긴 머리말을 보낸 후 질문 : 동일한 위치를 검색하더라도 fseek()이 성능에 그렇게 큰 영향을주는 이유는 무엇입니까? 어떻게하면 속도를 높일 수 있습니까? (다른 전략이나 다른 함수 호출, 가능한 경우 캐싱을 사용 불가능하게 설정, 메모리 맵핑 된 액세스 등 ...)? 내가 이해하지 않기 때문에 시스템이 내 "최적화"어떻게 좀 잃었어요

    -(bool)writeRotatedRaw:(TIFF*)tiff toFile:(NSString*)strFile 
    { 
        if(!tiff) return NO; 
        if(!strFile) return NO; 
    
        NSLog(@"Starting to rotate '%@'...", strFile); 
    
        FILE *f = fopen([strFile UTF8String], "w"); 
        if(!f) 
        { 
         NSString *msg = [NSString stringWithFormat:@"Could not open '%@' for writing.", strFile]; 
         NSRunAlertPanel(@"Error", msg, @"OK", nil, nil); 
         return NO; 
        } 
    
    #define LINE_CACHE_SIZE (1024*1024*256) 
    
        int h = [tiff iImageHeight]; 
        int w = [tiff iImageWidth]; 
        int iWordSize = [tiff iBitsPerSample]/8; 
        int iBitsPerPixel = [tiff iBitsPerSample]; 
        int iLineSize = w*iWordSize; 
        int iLinesInCache = LINE_CACHE_SIZE/iLineSize; 
        int iLinesToGo = h, iLinesToRead; 
    
        NSLog(@"Creating temporary file"); 
        double time = CACurrentMediaTime(); 
        double lastTime = time; 
        unsigned char *dummy = calloc(iLineSize, 1); 
        for(int i=0; i<h; i++) fwrite(dummy, 1, iLineSize, f); 
        free(dummy); 
        fclose(f); 
        f = fopen([strFile UTF8String], "w"); 
        NSLog(@"Created temporary file (%.1f MB) in %.1f seconds", (float)iLineSize*(float)h/1024.0f/1024.0f, CACurrentMediaTime()-time); 
        fseek(f, 0, SEEK_SET); 
    
        lastTime = CACurrentMediaTime(); 
        time = CACurrentMediaTime(); 
        int y=0; 
        unsigned char *ucRotatedPixels = malloc(iLinesInCache*iWordSize); 
        unsigned short int *uRotatedPixels = (unsigned short int*)ucRotatedPixels; 
        unsigned char *ucLineCache = malloc(w*iWordSize*iLinesInCache); 
        unsigned short int *uLineCache = (unsigned short int*)ucLineCache; 
        unsigned char *uc; 
        unsigned int uSizeCounter=0, uMaxSize = iLineSize*h, numfwrites=0, lastwrites=0; 
        while(iLinesToGo>0) 
        { 
         iLinesToRead = iLinesToGo; 
         if(iLinesToRead>iLinesInCache) iLinesToRead = iLinesInCache; 
    
         for(int i=0; i<iLinesToRead; i++) 
         { 
          // read as much lines as fit into buffer 
          uc = [tiff getRawLine:y+i withBitsPerPixel:iBitsPerPixel]; 
          memcpy(ucLineCache+i*iLineSize, uc, iLineSize); 
         } 
    
         for(int x=0; x<w; x++) 
         { 
          if(iBitsPerPixel==8) 
          { 
           for(int i=0; i<iLinesToRead; i++) 
           { 
            ucRotatedPixels[iLinesToRead-i-1] = ucLineCache[i*w+x]; 
           } 
           fseek(f, w*x+(h-y-1), SEEK_SET); 
           fwrite(ucRotatedPixels, 1, iLinesToRead, f); 
           numfwrites++; 
           uSizeCounter += iLinesToRead; 
           if(CACurrentMediaTime()-lastTime>1.0) 
           { 
            lastTime = CACurrentMediaTime(); 
            NSLog(@"Progress: %.1f %%, x=%d, y=%d, iLinesToRead=%d\t%d", (float)uSizeCounter * 100.0f/(float)uMaxSize, x, y, iLinesToRead, numfwrites); 
           } 
          } 
          else 
          { 
           for(int i=0; i<iLinesToRead; i++) 
           { 
            uRotatedPixels[iLinesToRead-i-1] = uLineCache[i*w+x]; 
           } 
           fseek(f, (w*x+(h-y-1))*2, SEEK_SET); 
           fwrite(uRotatedPixels, 2, iLinesToRead, f); 
           uSizeCounter += iLinesToRead*2; 
           if(CACurrentMediaTime()-lastTime>1.0) 
           { 
            lastTime = CACurrentMediaTime(); 
            NSLog(@"Progress: %.1f %%, x=%d, y=%d, iLinesToRead=%d\t%d", (float)uSizeCounter * 100.0f/(float)uMaxSize, x, y, iLinesToRead, numfwrites); 
           } 
          } 
         } 
         y += iLinesInCache; 
         iLinesToGo -= iLinesToRead; 
        } 
    
        free(ucLineCache); 
        free(ucRotatedPixels); 
        fclose(f); 
    
        NSLog(@"Finished, %.1f s", (CACurrentMediaTime()-time)); 
    
        return YES; 
    } 
    

    : 참고로

    , 여기에 (디버그 출력을 많이 포함하는 최적화되지, 최대 정돈되지) 내 코드입니다 전화. 모든 입력을 부탁드립니다.

  • +1

    데이터가 더 이상 파일 시스템 캐시에 적합하지 않으면 디스크 쓰기 성능을 측정하기 시작합니다. 그리고 fseek()는 디스크 쓰기 헤드가 얼마나 빨리 움직일 수 있는지 알려줍니다. 어느 것이 * 매우 느립니다. OSX에 대해서는 충분히 알지 못하지만 일반적으로 64 비트 OS와 많은 RAM이 큰 캐시를 구입합니다. –

    +0

    "전체적으로 불합리한 요약"을 다시 읽으십시오 : fseek없이 30,000 개의 블록을 모두 선형 적으로 작성하면 전체 프로세스가 3 초 미만이됩니다. 만약 파일 포인터가 이미있는 위치와 같은 위치에 있다면 성능은 거의 40 배 정도 떨어집니다. 시스템이 디스크 머리를 후자의 변형으로 옮기려한다고 말하지 마십시오.이 동작은 첫 번째 * 몇 * 메가 바이트 및 캐시가 작을 수는 없습니다 (실제로 해당 머신에서 2GB의 사용 가능하지 않은 RAM이 있음을 감안할 때). 그리고 네, 내 OSX는 64 비트이고 위의 경우 충분한 8GB의 RAM이 있습니다. – nullp01nter

    답변

    0

    이 질문을 어떻게 든 닫으려면 직접 답변하고 해결책을 공유하겠습니다.

    fseek() 호출의 성능을 향상시킬 수 없었지만 잘 수행 할 수있는 해결 방법을 구현했습니다. 목표는 어떤 희생을 치르더라도 fseek()을 피하는 것이 었습니다. 데이터의 단편을 대상 파일의 다른 위치에 기록해야하지만 그 단편은 동일한 거리에 나타나고 그 단편 사이의 갭은 프로세스의 후반부에 작성된 다른 단편으로 채워질 것이므로 필자는 여러 개의 파일로 분할합니다. 프래그먼트 스트림이 생성 될 때까지 많은 파일에 기록한 다음 최종 단계에서 모든 임시 파일을 다시 열고 회전 및 데이터 블록을 대상 파일에 선형 적으로 씁니다. 이 성능은 좋으며 약에 도달합니다. 위에 주어진 예제의 경우 4 초입니다.