LINUX.ORG.RU

md5 (каждую строку в файле)


2

1

Имеется текстовый файл с 2 ГБ данных. Нужно каждую строку похэшить в md5 и сохранить в файл, вот что получилось.

#!/bin/bash
while read line; do echo -n $line|md5sum|awk '{print $1}'|tr '[a-z]' '[A-Z]' >> md5.txt; done < text.txt
Проблема в том что все это делается очень долго, есть у кого мысли как ускорить процесс?

запустить несколько задач для разных частей файла. ну же.

rikardoac ()

Налабай скрипт на твоём любимом ЯП. Вот кошмар на питоне:

ux32vd@~$ cat /etc/fstab | python -c "import sys, hashlib; [print(hashlib.md5(l.encode()).hexdigest()) for l in sys.stdin.readlines()]"
06ef8b428397714a11fc439d374a06ef
491ddc69c01bd03325e1be2e7c80d57b
552dacb15f2019c8f3f74c55befa242c
8abedcd4250d7be584fcc8a60d31d2dc
f730dd9678baa5b913b8ced264188670
cb7e83dc883333987d360eb20a162c4e
68b329da9893e34099c7d8ad5cb9c940
33d3601c96a726c0d49732d771c300d0
1e20bbf3d175483e55bf8b552b3b9ba7
68b329da9893e34099c7d8ad5cb9c940

true_admin ★★★★★ ()
Ответ на: комментарий от megabaks

упирается в последовательность действий ----> 1 поток

очень интересно услышать как ты предлагаешь параллелить построчное чтение текстового файла и синхронную же запись в другой файл

П.С. если упирается в процессор - вот скрипт на C (за корректность не ручаюсь):

~$ cat 1.c
#!/usr/bin/tcc -run -lcrypto

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <openssl/md5.h>

int main( int argc, char** argv ) {
    FILE *in  = fopen( argv[1], "rt" );
    FILE *out = fopen( argv[2], "wt" );

    char *line = NULL;
    size_t len = 0;
    ssize_t read;

    unsigned char md[ MD5_DIGEST_LENGTH ];
    char mdstr[ 34 ];
    mdstr[ 32 ] = '\n';
    mdstr[ 33 ] = 0;

    while( ( read = getline( &line, &len, in ) ) != -1 ) {
        MD5( line, len, md );

        int blen = 16;
        unsigned char *bin = md;
        char *str = mdstr;
        for( ; blen-- ; ++bin ) {
            *str++ = "0123456789abcdef"[ *bin >> 4 ];
            *str++ = "0123456789abcdef"[ *bin & 0x0F ];
        }

        fputs( mdstr, out );
    }

    fclose( out );
}

~$ ./1.c 1.txt md5.txt
wota ★★ ()
Ответ на: комментарий от true_admin

Оно ещё и память, небось, жрать будет т.к. генерирует список.

не, это я про то, что мой пример на С покошмаристее выглядит )

wota ★★ ()
Ответ на: комментарий от wota

Не ну, а что, разрезать файл на 10 штук(пронумеровать) и запустить по каждому твой сишник одновременно. Куски в порядке нумерации склеить опять.

Dron ★★★★★ ()
Ответ на: комментарий от wota

Ну так то да, но вот если за ранее будут готовы куски то нет, тем более если предположить что у него проц дофигаядерный.

Dron ★★★★★ ()
Ответ на: комментарий от wota

покошмаристее

Имхо, отличный (исключая отсутствие проверок). Если бы там стоял комментарий что цикл нужен для отформатирования в hex вообще было бы шикарно. Уверен, это можно сделать какой-нить библиотечной функцией, но что-то сходу не нашёл.

true_admin ★★★★★ ()
Ответ на: комментарий от Dron

чтение как раз распараллелить можно и без кусков - только это бесполезно и даже вредно, а запись - нет. т.к. ты не знаешь сколько в каждом куске строк будет, и значит тебе придется склеивать результаты - т.е. нагрузить дополнительно самое узкое место (IO)

wota ★★ ()
Ответ на: комментарий от wota

так ты гарантированно проиграешь по скорости

Почему? Я смотрю «openssl speed md5», для коротких сообщений имеет смысл разбить ввод на чанки и энкодить многопоточно.

Меня когда-то уверяли что «многопоточня компрессия это ересть» (имхо, что сжать, что md5 посчитать - один хрен), однако тесты показали что это не так.

true_admin ★★★★★ ()
Ответ на: комментарий от Dron

Тогда в tmpfs изначально его сунуть и там с ним работать.

зачем тут tmpfs? в память можно и самому сохранять, только если речь идет о гигабайтах - есть большая вероятность, что можно поставить систему колом или упасть во время исполнения

wota ★★ ()
Ответ на: комментарий от true_admin

Почему?

потому-что упрется в IO, а он его дополнительно еще хочет нагрузить, а в синтетических тестах - да, несомненно есть смысл параллелить, даже и не в синтетических, но там где нет постоянного чтения/записи с жесткого

wota ★★ ()
Ответ на: комментарий от true_admin

имхо, что сжать, что md5 посчитать - один хрен

нет, просто сравни сколько Мб/сек обрабатывает md5 и тот же bzip2, например

wota ★★ ()
Ответ на: комментарий от wota

просто сравни сколько Мб/сек обрабатывает md5

А, я смотрел чанки по 16байт, там скорость 50метров была. Если строка длиннее то будет быстрее.

true_admin ★★★★★ ()

Почему бы не позаботиться об этом в момент записи файла..

special-k ★★★ ()
Ответ на: комментарий от wota

Да ну, читаешь 4 строки, запускаешь 4 потока, как только самая первая строка обработана, записываешь её, читаешь следующую и скармливаешь освободившемуся потоку. Конечно, вариант не идеален, будут затыки, если одна строка весит гигабайт, а три последующие по килобайту, но в среднем почти постоянно будут работать 4 потока. На хаскеле можно было бы распараллелить готовую программу за минуту.

unC0Rr ★★★★★ ()
Ответ на: комментарий от unC0Rr

Да ну, читаешь 4 строки, запускаешь 4 потока, как только самая первая строка обработана, записываешь её, читаешь следующую и скармливаешь освободившемуся потоку.

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

Конечно, вариант не идеален, будут затыки, если одна строка весит гигабайт, а три последующие по килобайту, но в среднем почти постоянно будут работать 4 потока.

там где хватает одного потока - «постоянно будут работать 4 потока» не будет, разве что проц будет нагружен бесполезными проверками и действиями, ну или строки будут размером в несколько байт, но для таких считать md5 вообще нет смысла

На хаскеле можно было бы распараллелить готовую программу за минуту.

на плюсах - тоже, да и на С, и будет однозначно быстрее чем на хаскеле, но пока я не вижу смысла в этом, разве что кто-то сделает бенчмарк и покажет обратное, я у себя померял - обработка большого файла с логами упирается в скорость записи результата, а не в CPU, даже с tcc

wota ★★ ()
Ответ на: комментарий от wota
#!/usr/bin/tcc -run -lcrypto

#define _GNU_SOURCE
#include <stdio.h>

int main( int argc, char** argv ) {
    FILE *in  = fopen( argv[1], "r" );
    FILE *out = fopen( argv[2], "w" );

    int i;
    char *line = NULL;
    size_t len = 0;
    ssize_t read;
    unsigned char md[ 16 ];
	
    while( ( read = getline( &line, &len, in ) ) != -1 ) {
        MD5( line, len, md );

        for( i = 0 ; i < 16 ; ++i ) {
            fputc( "0123456789abcdef"[ md[ i ] >> 4 ], out );
            fputc( "0123456789abcdef"[ md[ i ] & 0x0F ], out );
        }

        fputc( '\n', out );
    }

    fclose( out );
}

вариант №2, чтоб не нужно было хедеры от openssl ставить, ну и покороче

wota ★★ ()
Последнее исправление: wota (всего исправлений: 1)
Ответ на: комментарий от true_admin

Оно ещё и память, небось, жрать будет т.к. генерирует список. Зато в одну строчку.

Поставьте () заместо [] в генераторе списка, и память жрать не будет;-)

Новый кот?

AIv ★★★★★ ()

А какая характерная длина строки во входном файле?

AIv ★★★★★ ()
Ответ на: комментарий от wota

Все прально. Но если файл уже отмапирован (или был недавно отмапирован) - картинка совсем другая будет.

AIv ★★★★★ ()
Ответ на: комментарий от wota

Если все упирается в IO, то смысла параллелить нет вообще. Если упираемся в одно CPU при вычислени хэша, то...

Длина md5-хэша константная? Создаем файл заведомо бОльшего размера, чем потребуется (это можно оценить или взять наихудший вариант - размер исходного файла * длина хеша / 2. Пополам, потому что в исходном файле в наихудшем случае будет половина переносов строк, для винды делим на три). В конце процесса мы отрежем лишнее. Конечно исходный и выходной файлы лучше взять в mmap.

Создаем потокобезопасную функцию next_string, которая возвращает очередную строку файла и ее номер. Создаем пул потоков, каждый из которых в цикле вызывает next_string, считает хеш и, зная ее номер, вписывает результат в выходной файл. В результате колбасим файл в много потоков без разбиений и склеиваний.

staseg ★★★★★ ()
Последнее исправление: staseg (всего исправлений: 2)
Ответ на: комментарий от AIv

Но если файл уже отмапирован (или был недавно отмапирован) - картинка совсем другая будет.

да, тогда мы упремся уже в функцию md5, но в любом случае 2Гб логов обработаются за вполне приемлемое время (чуть меньше 30сек на моем железе), конечно если там не будут строки в пару-тройку байт

wota ★★ ()
Ответ на: комментарий от wota

Оба варианта с tcc кривые

$ wc -l 1.txt
1 1.txt

$ md5sum 1.txt 
7c71ff4690e2b6e60efadc4131b52a70  1.txt

$ perl -p -M'Digest::MD5 qw(md5_hex)' -e '$_=md5_hex($_) . "\n"' 1.txt 
7c71ff4690e2b6e60efadc4131b52a70

$ ./md5_to_line.c 1.txt aaa.txt ; cat aaa.txt 
f8fcb1bdb57fbcfad51428c4cc021691
$ ./md5_to_line.c 1.txt aaa.txt ; cat aaa.txt 
f4b5b7d18f425213aadc195d2f8733a9
$ ./md5_to_line.c 1.txt aaa.txt ; cat aaa.txt 
9f7b9dfbd0179d2dd4f14eeb4c1cb242

$ gcc -O0 -lcrypto -o md5_to_line md5_to_line.c

$ ./md5_to_line 1.txt aaa.txt ; cat aaa.txt 
c2df5502cf0488c35be55fa315abe421
$ ./md5_to_line 1.txt aaa.txt ; cat aaa.txt 
c2df5502cf0488c35be55fa315abe421
$ ./md5_to_line 1.txt aaa.txt ; cat aaa.txt 
c2df5502cf0488c35be55fa315abe421

anonymous ()
Ответ на: комментарий от wota

Да на самом деле пофигу на каком ЯП это писать, главное что бы не на пайпах как ТС. На каждую строку у него стартует как мин ДВА ПРОЦЕССА - md5sum и awk. Когда я это изучал для гнуплота - ЕМНИП 0.1 сек на процесс уходит (на запуск). Ну скажем у gnuplot-а в system какие то костыли могут быть, но все равно... А мы тут многопоооточность, I/O... ;-)

AIv ★★★★★ ()
Ответ на: комментарий от staseg

исходном файле в наихудшем случае будет половина переносов строк

в наихудшем - там могут быть только переносы строк,ну и хотелось бы увидеть решение в коде

wota ★★ ()
Ответ на: комментарий от AIv

главное что бы не на пайпах

Это банально, я когда-то переписал один в один с шела на перл, (т.е. избавился от форков) и скрипт стал отрабатывать за 2сек вместо 3600 сек.

anonymous ()
Ответ на: комментарий от wota

в наихудшем - там могут быть только переносы строк

Упс, да, значит делить не надо.

ну и хотелось бы увидеть решение в коде

Я очень подробно описал алгоритм, код писать мне лень.

staseg ★★★★★ ()
Ответ на: комментарий от anonymous

Во! А дальше пришли специалисты по многопоточности и отиграли еще 1.8 сек;-)))

AIv ★★★★★ ()
Ответ на: комментарий от anonymous

Оба варианта с tcc кривые

точно, лажанул:

#!/usr/bin/tcc -run -lcrypto

#define _GNU_SOURCE
#include <stdio.h>

int main( int argc, char** argv ) {
    FILE *in  = fopen( argv[1], "r" );
    FILE *out = fopen( argv[2], "w" );

    int i;
    char *line = NULL;
    size_t len = 0;
    ssize_t read;
    unsigned char md[ 16 ];
	
    while( ( read = getline( &line, &len, in ) ) != -1 ) {
        MD5( line, read, md );

        for( i = 0 ; i < 16 ; ++i ) {
            fputc( "0123456789abcdef"[ md[ i ] >> 4 ], out );
            fputc( "0123456789abcdef"[ md[ i ] & 0x0F ], out );
        }

        fputc( '\n', out );
    }

    fclose( out );
}

вместо read написал len, заодно кстати еще и замедлил

wota ★★ ()
Ответ на: комментарий от staseg

Я очень подробно описал алгоритм

ты наверняка знаешь, что алгоритм-алгоритмом, а узкие места - дело особое, блокировки + ожидание, последовательная запись vs «рандомная» и т.д. - может внести значительные коррективы, потому и хотелось сравнить готовое решение

код писать мне лень

окай

wota ★★ ()
Ответ на: комментарий от AIv

Поставьте () заместо [] в генераторе списка, и память жрать не будет;-)

так получится же генератор — ленивые вычисления. Оно не будет работать пока кто-то по нему итерацию не произведёт.

Новый кот?

Кошка подруги. Фейс у неё жутко недовольный, но создание вполне дружелюбное.

true_admin ★★★★★ ()
Ответ на: комментарий от AIv

Можно, кстати, цикл через какой-нить map() сделать, но у меня сходу не получилось. Впрочем, и так ясно что питон перлу в однострочниках не конкурент :(

true_admin ★★★★★ ()
Ответ на: комментарий от true_admin

так получится же генератор — ленивые вычисления. Оно не будет работать пока кто-то по нему итерацию не произведёт.

Тьфу, да.

Кошка подруги.

А старый зверь где?;-)

Можно, кстати, цикл через какой-нить map() сделать, но у меня сходу не получилось.

Тогда уже через reduce - map тот же генератор, как мин засрет память списком из 100500 None. И у тебя еще sys.stdin.readlines() список строк файла создает. Надо вот так:

$ cat /etc/fstab | python -c "import sys, hashlib; reduce( lambda L, x: sys.stdout.write(x+'\n'), (hashlib.md5(l.encode()).hexdigest() for l in sys.stdin ), None )"
def134465af461d1e263204f35040fb5
552dacb15f2019c8f3f74c55befa242c
f8a9f3c95014225298fa50c31cb86c01
8ef5c5409f361857c71960aff22dad92
8c7bde9aa3683fc93d26d36b9cf9cae3
552dacb15f2019c8f3f74c55befa242c
089018fcd5fdbc97a9a7690b55473792
412dfe9914ed550e314fc5f005d9d6a2
3c8a01a657f3c1e1e97e48310d5124e0
06159f40cb9c63161611877fc532799d
5f5fa0647f81ebaca02e342da89448d1
97e39925e46cf716b2e7f5992c8fe0f2
0cb64e206634284dd9ba7b76670d19b4
862eb60d3bcd06432db254707e5f6b25
e3eb496ea143238432fd581b8269e9ca
46ec84276b89a593d808ac4fbb639f6d
dddf8a3712a7faf95a695c1555829876
1b71568f83bd07bb9965ff7870654b54

Впрочем, и так ясно что питон перлу в однострочниках не конкурент :(

Поскольку я перла не знаю, то для меня очень даже конкурент;-)

AIv ★★★★★ ()
Ответ на: комментарий от AIv

А в идеале так:

$ cat /etc/fstab | python -c "import sys, hashlib; reduce( lambda L, l: sys.stdout.write(hashlib.md5(l.encode()).hexdigest()+'\n'), sys.stdin, None )"
def134465af461d1e263204f35040fb5
552dacb15f2019c8f3f74c55befa242c
f8a9f3c95014225298fa50c31cb86c01
8ef5c5409f361857c71960aff22dad92
8c7bde9aa3683fc93d26d36b9cf9cae3
552dacb15f2019c8f3f74c55befa242c
089018fcd5fdbc97a9a7690b55473792
412dfe9914ed550e314fc5f005d9d6a2
3c8a01a657f3c1e1e97e48310d5124e0
06159f40cb9c63161611877fc532799d
5f5fa0647f81ebaca02e342da89448d1
97e39925e46cf716b2e7f5992c8fe0f2
0cb64e206634284dd9ba7b76670d19b4
862eb60d3bcd06432db254707e5f6b25
e3eb496ea143238432fd581b8269e9ca
46ec84276b89a593d808ac4fbb639f6d
dddf8a3712a7faf95a695c1555829876
1b71568f83bd07bb9965ff7870654b54

AIv ★★★★★ ()
Ответ на: комментарий от AIv
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "<string>", line 1, in <lambda>
UnicodeDecodeError: 'ascii' codec can't decode byte 0xef in position 0: ordinal not in range(128)
wota ★★ ()
perl -MDigest::MD5=md5_hex -nE 'say uc md5_hex $_' text.txt >md5.txt
arsi ★★★★★ ()
Ответ на: комментарий от AIv

да- так работает, в 4 раза где-то медленнее чем вариант на С, и раза в 2.5 чем вариант на perl, на перле еще и самое короткое решение

wota ★★ ()
Ответ на: комментарий от AIv

файл скромный: 33Мб - логи апача, ОЗУ - DDR3 1600 16Гб, насчет в разы больше ОЗУ - попробую, но попозже, сейчас надо отойти

wota ★★ ()
Ответ на: комментарий от wota

ещё можно увеличить буфер для out setvbuf(out,malloc(32*1024*1024),...) и избавиться от множества fputc, например генерить сразу всю md5 строку и выдавать её одним библиотечным вызовом;

MKuznetsov ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.