为什么MD5Sum这么快

时间:2013-09-05 19:13:32

标签: c++ performance md5 md5sum

我一直在研究C / C ++中的散列,并尝试在Linux中复制md5sum命令。在分析了源代码之后,似乎md5sum依赖于md5库的md5_stream。我已经将md5.h库中的md5_stream函数近似为下面的代码,它运行时间约为13-14秒。我试图直接调用md5_stream函数并得到~13-14秒。 md5sum在4秒内运行。 GNU人员为了提高代码的速度做了什么?

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;
}

编辑:Fedora 19 64位是默认的mkspec问题。

2 个答案:

答案 0 :(得分:3)

fread()很方便,但如果你关心性能,不要使用fread()。 fread()将从操作系统复制到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);
}

答案 1 :(得分:0)

在Qt mkspecs中,有关优化标志未正确设置的错误。