2013-09-05 4 views
2

저는 C/C++에서 해시를 연구 중이며 Linux에서 md5sum 명령을 복제하려고했습니다. 소스 코드를 분석 한 결과, md5sum은 md5 라이브러리의 md5_stream에 의존하는 것으로 보입니다. md5_stream 함수를 md5.h 라이브러리의 근사치로 아래 코드에 입력하면 ~ 13-14 초 후에 실행됩니다. 나는 직접 md5_stream 함수를 호출하려고 노력했으며 ~ 13-14 초를 가졌다. md5sum은 4 초 후에 실행됩니다. 코드에서 속도를 내기 위해 GNU 사람들이 한 일은 무엇입니까?MD5Sum이 왜 그렇게 빠른 이유는 무엇입니까?

md5.h/md5.c 코드는 CoreUtils 소스 코드에서 사용할 수 있습니다.

#include <QtCore/QCoreApplication> 
#include <QtCore/QDebug> 

#include <iostream> 
#include <iomanip> 
#include <fstream> 
#include "md5.h" 

#define BLOCKSIZE 32784 

int main() 
{ 
    FILE *fpinput, *fpoutput; 

    if ((fpinput = fopen("/dev/sdb", "rb")) == 0) { 
     throw std::runtime_error("input file doesn't exist"); 
    } 

    struct md5_ctx ctx; 
    size_t sum; 

    char *buffer = (char*)malloc (BLOCKSIZE + 72); 
    unsigned char *resblock = (unsigned char*)malloc (16); 
    if (!buffer) 
     return 1; 

    md5_init_ctx (&ctx); 
    size_t n; 
    sum = 0; 

    while (!ferror(fpinput) && !feof(fpinput)) { 
     n = fread (buffer + sum, 1, BLOCKSIZE - sum, fpinput); 
     if (n == 0){ 
      break; 
     } 
     sum += n; 

     if (sum == BLOCKSIZE) { 
      md5_process_block (buffer, BLOCKSIZE, &ctx); 
      sum = 0; 
     } 
    } 

    if (n == 0 && ferror (fpinput)) { 
     free (buffer); 
     return 1; 
    } 

    /* Process any remaining bytes. */ 
    if (sum > 0){ 
     md5_process_bytes (buffer, sum, &ctx); 
    } 

    /* Construct result in desired memory. */ 
    md5_finish_ctx (&ctx, resblock); 
    free (buffer); 

    for (int x = 0; x < 16; ++x){ 
     std::cout << std::setfill('0') << std::setw(2) << std::hex << static_cast<uint16_t>(resblock[x]); 
     std::cout << " "; 
    } 
    std::cout << std::endl; 
    free(resblock); 
    return 0; 
} 

EDIT : Fedora 19 64 비트에서 기본 mkspec 문제가 있었습니까?

+1

코드를 빌드 할 때 디버그 모드로 구축하고 있습니까? 어떤 최적화 수준을 사용하고 있습니까? – Tim

+3

gnuutils ... 소스를 사용할 수 있으며 직접 볼 수 있습니다. –

+0

팀 : 현재 Qt를 구축 중입니다. 기본적으로 Qt 빌드 환경은 -O2 빌드 플래그를 추가합니다. 릴리스 모드에서 컴파일 및 테스트하므로 라이브러리 및 후크 디버깅이 방해되어서는 안됩니다. MarcB : md5sum 소스 코드를 보면 GNUUtils라는 것을 알 수 있습니다. 나는 그들이 어떻게 그들의 위치를 ​​최적화했는지에 대한 통찰력을 찾고있다. – MagikWorx

답변

0

Qt mkspecs에서 최적화 플래그가 올바르게 설정되지 않았다는 오류가있었습니다.

3

fread()는 편리하지만 성능을 신경 쓰는 경우 fread()를 사용하지 마십시오. fread()는 OS에서 libc 버퍼로 복사 한 다음 버퍼로 복사합니다. 이 여분의 복사 비용 CPU주기 및 캐시.

여분의 복사본을 피하려면 open()을 사용하고 read()을 사용하십시오. read() 호출이 블록 크기의 배수이지만 CPU 캐시 크기보다 작아야합니다.

최상의 성능을 위해 mmap() 디스크를 RAM에 직접 매핑하십시오.

아래 코드를 시도하면 속도가 빨라집니다.

// compile gcc mmap_md5.c -lgcrypt 
#include <sys/mman.h> 
#include <sys/stat.h> 
#include <fcntl.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <gcrypt.h> 
#include <linux/fs.h> // ioctl 

#define handle_error(msg) \ 
do { perror(msg); exit(EXIT_FAILURE); } while (0) 


int main(int argc, char *argv[]) 
    { 
     char *addr; 
     int fd; 
     struct stat sb; 
     off_t offset, pa_offset; 
     size_t length; 
     ssize_t s; 
     unsigned char digest[16]; 
     char digest_ascii[32+1] = {0,}; 
     int digest_length = gcry_md_get_algo_dlen (GCRY_MD_MD5); 
     int i; 

     if (argc < 3 || argc > 4) { 
      fprintf(stderr, "%s file offset [length]\n", argv[0]); 
      exit(EXIT_FAILURE); 
     } 
     fd = open(argv[1], O_RDONLY); 
     if (fd == -1) 
      handle_error("open"); 
     if (fstat(fd, &sb) == -1)   /* To obtain file size */ 
      handle_error("fstat"); 
     offset = atoi(argv[2]); 
     pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1); 


     if (sb.st_mode | S_IFBLK) { 
      // block device. use ioctl to find length 
      ioctl(fd, BLKGETSIZE64, &length); 

     } else { 
      /* offset for mmap() must be page aligned */ 
      if (offset >= sb.st_size) { 
       fprintf(stderr, "offset is past end of file size=%zd, offset=%d\n", sb.st_size, (int) offset); 
       exit(EXIT_FAILURE); 
      } 
      if (argc == 4) { 
       length = atoi(argv[3]); 
       if (offset + length > sb.st_size) 
        length = sb.st_size - offset; 
       /* Canaqt display bytes past end of file */ 
      } else { /* No length arg ==> display to end of file */ 
       length = sb.st_size - offset; 
      } 
     } 
     printf("length= %zd\n", length); 
     addr = mmap(NULL, length + offset - pa_offset, PROT_READ, 
           MAP_PRIVATE, fd, pa_offset); 
     if (addr == MAP_FAILED) 
      handle_error("mmap"); 


     gcry_md_hash_buffer(GCRY_MD_MD5, digest, addr + offset - pa_offset, length); 

     for (i=0; i < digest_length; i++) { 
      sprintf(digest_ascii+(i*2), "%02x", digest[i]); 
     } 
     printf("hash=%s\n", digest_ascii); 

     exit(EXIT_SUCCESS); 
} 
+0

실제로 성능 향상을 측정 했습니까? 어떤 유형의 파일 시스템입니까? 순차 파일 액세스의 경우 mmap이 버퍼 된 읽기보다 훨씬 뛰어난 성능을 발휘할 수 있는지 궁금합니다. 필자는 순차적 fread가 대다수의 파일 시스템에서 언 버퍼 드 (unbuffered) 읽기보다 더 나은 성능을 발휘할 것으로 기대합니다. –

+0

원래 포스터는/dev/sdb 블록 장치를 검사하여 파일 시스템 유형이 부적절했습니다. 파일 시스템은 블록 장치 위에 있습니다. 광산 대 md5sum의 성능을 테스트 한 결과 거의 동일합니다. 예가 md5sum보다 약 5 % 느리지 만 원래 포스터보다 3 배 이상 느리지는 않습니다. 당신이 mmap에 대한 더 많은 논쟁을 원한다면. http://stackoverflow.com/questions/45972/mmap-vs-reading-blocks – karl

+0

슬프게도,이 경우에는 컴파일 문제였습니다. 코드와 통찰력에 감사드립니다. 나는 확실히 연결 한 기사를 체크 아웃 할 것이다. – MagikWorx

관련 문제