быстрая md5sum на миллионах строк в bash / ubuntu - PullRequest
4 голосов
/ 25 декабря 2009

Мне нужны суммы MD5 в 3 миллиона строк или около того в скрипте bash на Ubuntu. 3 миллиона строк -> 3 миллиона хэшей MD5. Тривиальная реализация занимает около 0,005 с на строку. Это более 4 часов. Какие существуют более быстрые альтернативы? Есть ли способ закачать группы строк в md5sum?

#time md5sum running 100 times on short strings
#each iteration is ~0.494s/100 = 0.005s
time (for i in {0..99}; do md5sum <(echo $i); done) > /dev/null

real    0m0.494s
user    0m0.120s
sys     0m0.356s

Хорошее решение будет включать скрипт bash / Perl, который берет список строк из stdin и выводит список их хэшей MD5.

Ответы [ 5 ]

6 голосов
/ 25 декабря 2009

Это не сложно сделать в C (или Perl или Python), используя любую из многих реализаций md5 - в основе md5 лежит хеш-функция, которая переходит от символьного вектора к символьному вектору.

Так что просто напишите внешнюю программу, которая читает ваши 3 миллиона строк, а затем передайте их одну за другой в реализацию md5 по вашему выбору. Таким образом, у вас будет один запуск программы, а не 3 миллиона, и это одно сэкономит ваше время.

FWIW в одном проекте Я использовал реализацию md5 (на C) Кристофа Девайна, также есть OpenSSL, и я уверен, что CPAN также будет иметь их несколько для Perl.

Редактировать: Хорошо, не удержался. Упомянутая мною реализация md5, например, внутри этого маленького тарбола . Возьмите файл md5.c и замените (# ifdef'ed out) main() внизу этим

int main( int argc, char *argv[] ) {
    FILE *f;
    int j;
    md5_context ctx;
    unsigned char buf[1000];
    unsigned char md5sum[16];

    if( ! ( f = fopen( argv[1], "rb" ) ) ) {
        perror( "fopen" );
        return( 1 );
    }

    while( fscanf(f, "%s", buf) == 1 ) {
        md5_starts( &ctx );
        md5_update( &ctx, buf, (uint32) strlen((char*)buf) );
        md5_finish( &ctx, md5sum );

        for( j = 0; j < 16; j++ ) {
            printf( "%02x", md5sum[j] );
        }
        printf( " <- %s\n", buf );
    }
    return( 0 );
}

создать простую автономную программу, например, в

/tmp$ gcc -Wall -O3 -o simple_md5 simple_md5.c

и тогда вы получите это:

# first, generate 300,000 numbers in a file (using 'little r', an R variant)
/tmp$ r -e'for (i in 1:300000) cat(i,"\n")' > foo.txt

# illustrate the output
/tmp$ ./simple_md5 foo.txt | head
c4ca4238a0b923820dcc509a6f75849b <- 1
c81e728d9d4c2f636f067f89cc14862c <- 2
eccbc87e4b5ce2fe28308fd9f2a7baf3 <- 3
a87ff679a2f3e71d9181a67b7542122c <- 4
e4da3b7fbbce2345d7772b0674a318d5 <- 5
1679091c5a880faf6fb5e6087eb1b2dc <- 6
8f14e45fceea167a5a36dedd4bea2543 <- 7
c9f0f895fb98ab9159f51fd0297e236d <- 8
45c48cce2e2d7fbdea1afc51c7c6ad26 <- 9
d3d9446802a44259755d38e6d163e820 <- 10

# let the program rip over it, suppressing stdout
/tmp$ time (./simple_md5 foo.txt > /dev/null)

real    0m1.023s
user    0m1.008s
sys     0m0.012s
/tmp$

Так что это примерно секунда для 300 000 (коротких) строк.

4 голосов
/ 25 декабря 2009
#~/sw/md5$ time (for i in {0..99}; do md5sum <(echo $i); done) > /dev/null

real    0m0.220s
user    0m0.084s
sys 0m0.160s
#~/sw/md5$ time (python test.py `for i in {0..99}; do echo $i; done`) > /dev/null

real    0m0.041s
user    0m0.024s
sys 0m0.012s

Код Python в пять раз быстрее для этих небольших выборок, для больших выборок разница намного больше из-за отсутствующих порождений. 1k сэмплов - от 0,033 до 2,3 с :) Сценарий:

#!/usr/bin/env python
import hashlib, sys

for arg in sys.argv[1:]:
  print hashlib.md5(arg).hexdigest()
4 голосов
/ 25 декабря 2009
perl -MDigest::MD5=md5_hex -lpe '$_ = md5_hex $_'
3 голосов
/ 25 декабря 2009

У меня нет машины, чтобы проверить ее прямо сейчас, но разве md5sum <<< "$i" быстрее, чем md5sum <(echo $i)? Синтаксис <<< позволит избежать накладных расходов на разветвление подпроцесса для echo и будет передавать $i непосредственно в md5sum на стандартном вводе.

1 голос
/ 25 декабря 2009

Чтобы повысить производительность, вам, вероятно, потребуется использовать другую программу или создать программу на C, которая вызывает один из общедоступных хэш-API md5.

Другим вариантом является одновременное создание нескольких вызовов md5, чтобы использовать преимущества нескольких ядер. Каждый цикл через вас может породить 8 вызовов, первые 7 используют & в конце (для обозначения асинхронного). Если у вас доступно 4-8 ядер, это может ускорить его в 8 раз.

...