Хобрук: Ваш путь к мастерству в программировании

Threads vs fork wordlist чтение и хеширование

Почему в этом случае fork лучше, чем threads? С файлом в 139 мб (rockyou.txt) форк составляет 0,5 секунды, с тем же файлом и тем же словом в конце файла это 3 секунды с потоком (измеряется как часами(), так и обычным секундомером, потоки занимают намного больше времени чем форк)
Программа читает каждую строку списка слов, хеширует ее и сравнивает с дайджестом.
Это с форком

void wordlistFork(char digest[], char hashtype[], FILE *wordlist,int numberOfFork){
    int i;
    clock_t t;
    fseek(wordlist, 0L, SEEK_END);
    long fileLength = ftell(wordlist);
    fseek(wordlist, 0L, SEEK_SET);
    for(i=0;i<numberOfFork;i++){
        int pid = fork();
    if(pid==0){
            char line[512];
            long initialOffset = correctOffset(lengthOfFile*i/numberOfFork,wordlist);
            long finalOffset = correctOffset(lengthOfFile*(i+1)/numberOfFork,wordlist);
            fseek(wordlist, initialOffset, SEEK_SET);
            t = clock();
            while (initialOffset < finalOffset) {
                fscanf(wordlist,"%s\n",line);
                char tempLine[512] = {0};
                strncpy(tempLine,line,strlen(line));
                if (strcmp(hash(tempLine,hashType), digest) == 0) {
                    printf("Child %d: Trovato! hash %s %s -> %s\n",i, tipohash, digest, linea);
                    t = clock() -t;
                    double time_taken = ((double)t)/CLOCKS_PER_SEC;
                    printf("Time elapsed: %f seconds\n",time_taken);
                    system("killall hashcrack");
                    return;
                }
                initialOffset = ftell(wordlist);
            }
            printf("Child %d Found nothing.\n",i);
            exit(0);
        }
    }
    for(i=0;i<numberOfFork;i++)
        wait(NULL);
}    

это с нитками

for(i=0;i<numberOfThreads;i++)
    pthread_create(&threads[i], NULL, (void *(*)(void *)) crack, (void *)(intptr_t) i);
for(i=0;i<numberOfThreads;i++)
    pthread_join(threads[i],NULL);  

void *crack(const int *args){
    int threadID = (int)(intptr_t)args;
    char line[512];
    FILE *wordlist = fopen(Tpath,"r");
    long initialOffset = correctOffset(fileLength*threadID/numberOfThreads,wordlist);
    long finalOffset = correctOffset(fileLength*(threadID+1)/numberOfThreads,wordlist);
    fseek(wordlist, initialOffset, SEEK_SET);
    while (initialOffset < FinalOffset) {
        fscanf(wordlist,"%s\n",line);
        char tempLine[512] = {0};
        strncpy(tempLine,line,strlen(line));
        if (strcmp(hash(tempLine,hashType), Tdigest) == 0) {
            printf("Thread %d: Found! hash %s %s -> %s\n",threadID, hashType, Tdigest, line);
            system("killall hashcrack");
            exit(0);
        }
        initialOffset = ftell(wordlist);
    }
    printf("Thread %d: found nothing.\n",threadID);
    return NULL;
}
10.09.2020

  • Совет: вместо мазохистского преобразования одной и той же переменной снова и снова, снова и снова, просто объявите локальную переменную с этим типом или, что еще лучше, измените тип аргумента, чтобы он соответствовал тому, что он есть. 10.09.2020
  • @tadman Проблема в том, что пока я думаю, что ничего не переделываю. О какой линии вы говорите? 10.09.2020
  • (struct params *)args появляется слишком много раз. Этот код превращается в беспорядок и шум из-за таких привычек. Поскольку вы ограничены сигнатурой функции потока, просто объявите struct params* args = args_v и измените аргумент на void* v_args. Вуаля, переделывать больше не нужно! 10.09.2020
  • @tadman Я поступил лучше: я использовал глобальные переменные вместо того, чтобы передавать их в структуру и выполнять 1000 приведения. Но результат точно такой же, 0,5 секунды с fork и 3 секунды с threads, чтобы найти одно и то же слово в том же файле в той же позиции, используя 6 fork или 6 thread в процессоре с 12 виртуальными ядрами без SIMD. По крайней мере, теперь код стал красивее ахахах, но я не могу понять суть вопроса 11.09.2020
  • @tadman Я также обновил код, если у вас есть другие предложения, я внимательно слушаю 11.09.2020
  • ОТ: это хороший пример того, почему strncpy почти никогда не бывает прав: strncpy(tempLine,line,strlen(line)). Предположительно, вы делаете это для защиты от переполнения tempLine, но вы не ограничиваете копию до sizeof tempLine, поэтому, если line слишком длинное, оно будет переполнено. Так что никакой защиты. Но что еще хуже, вы указываете strncpy записывать не более strlen(line) байтов, что означает, что он никогда не запишет NUL в конце. Таким образом, вы не только не защищаете от переполнения буфера, но и открываете возможность незавершенной строки. 11.09.2020
  • Вы можете исправить это, используя strncpy(tempLine, line, sizeof(tempLine)-1). Но посмотрите, что происходит: инициализация tempLine пишет 512 нулей. Затем копия записывает несколько байтов полезных данных и заполняет остальную часть tempLine 0, потому что это то, что делает strncpy. Результат: написано почти 1кб нулей, когда нужно было написать ровно один. Возможно, это не самая большая неэффективность в мире, но она определенно не способствует вашей производительности. 11.09.2020
  • @rici Я пробовал с отладчиком, он идентичен 11.09.2020
  • @rici Может быть, если я попытаюсь использовать указатель вместо массива, это будет лучше, поэтому я смогу создать массив размерности прочитанной строки +1 во время выполнения, но я не хочу рисковать переполнением кучи или чем-то в этом роде. 11.09.2020
  • Я хочу сказать, что strncpy не помогает избежать переполнения. Это не то, что вы можете проверить с помощью пары тестов в отладчике. По крайней мере, вам нужно использовать правильные тестовые случаи. 11.09.2020
  • Версия fork не открывает файл повторно, поэтому все процессы используют один и тот же указатель файла (но отдельные библиотечные буферы), поэтому после того, как начальные блоки читаются из файла библиотекой C, все процессы ищут один и тот же раздел файла. 14.09.2020

Ответы:


1

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

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

10.09.2020
  • Мой код обеспечивает определенный порядок, есть fseek, я разбиваю файл на n частей, а затем перемещаю поиск в часть определенного потока/вилки 10.09.2020
  • @helloworld Ваш код не обеспечивает никакого порядка. У каждого потока или процесса есть определенный фрагмент файла, который он будет искать, и эти потоки/процессы выполняются без определенного порядка, который обеспечивает ваш код. Файл может быть просканирован от начала до конца или от конца к началу. Ваш код не обеспечивает его соблюдение. 10.09.2020
  • Я не понимаю, я делю файл на куски и читаю от начального смещения к конечному смещению, а не наоборот, но ладно, может быть, ОС может решить читать от конечного смещения к начальному, я не т знаю. Но есть ли что-то, что я могу сделать или нет? Чтобы проверить, что вы говорите, или чтобы темы работали лучше 10.09.2020
  • @helloworld Вы не читаете от начального смещения до конечного. Вы отправляете группу отдельных рабочих процессов, каждый из которых начинается с другого места в файле. Что касается того, можете ли вы что-то сделать, я не понимаю, как я могу ответить на этот вопрос, не зная, что вы пытаетесь сделать. Ваш код ищет что-то случайным образом — сколько времени это займет, полностью зависит от удачи. Вы можете искать в принудительном порядке, но это не сделает код лучше. В чем проблема? Ваш вывод о том, что один метод быстрее другого, неверен, так в чем проблема? 10.09.2020
  • Проблема в том, что, возможно, я что-то неправильно делаю с pthread. У меня 6 ядер, каждое ядро ​​имеет фрагмент файла, и с инструкцией while (initialOffset ‹ finalOffset) я говорю, что каждый поток/форк должен читать от initialOffset до finalOffset фрагмента, поэтому порядок должен быть таким же или, по крайней мере, не так сильно отличается во времени. Инструкции выполняются параллельно, поэтому нет разницы между тем, какой поток выполняется первым. Почему вы говорите, что они начинаются с другого места в файле, если я делаю fseek? Но это нормально, я тебе доверяю 10.09.2020
  • Кроме того, если я блокирую в начале функции и разблокирую в конце, она печатается по порядку, так что это не случайно 10.09.2020
  • Хорошо, я выполнил профилировщик производительности, в случае с потоком много накладных расходов, поэтому я думаю, что проблема в этом. Потоки первых двух строк: Образцы: 90 000 циклов событий, Количество событий (прибл.): 65258804164 Детская самостоятельная команда Символ общего объекта + 57,69% 0,00% hashcrack [неизвестно] [k] 00000000000000000 ◆ + 52,55 % 5,60% hashcrack libc-2.31.so [.] __vsprintf_interna▒ fork: Примеры: 13K 32,72% hashcrack libc-2.31.so [.] __vfprintf_internal 7,80% hashcrack libc-2.31.so [.] _IO_default_xsputn 10.09.2020
  • Инструкции выполняются параллельно, поэтому нет разницы между тем, какой поток выполняется первым.. Действительно? Итак, какая позиция файла читается первой на диске? 11.09.2020
  • Нет причин, по которым с потоками диск должен читаться в конце, а с вилками в начале, в том числе потому, что я пытался, и это дает точно такие же результаты с другим диском. Профилировщик говорит, что с потоками больше накладных расходов, чем на вилку, но обычно на создание процесса больше накладных расходов, чем на поток. Вот чего я не понимаю 11.09.2020
  • Нет никаких причин, по которым с потоками диск должен читаться в конце, а с разветвлениями — в начале. Точно. Нет причин, по которым код должен выполняться в каком-то определенном порядке, и производительность критически зависит от порядка, в котором выполняется код. 11.09.2020
  • Новые материалы

    Решения DBA Metrix
    DBA Metrix Solutions предоставляет удаленного администратора базы данных (DBA), который несет ответственность за внедрение, обслуживание, настройку, восстановление базы данных, а также другие..

    Начало работы с Блум
    Обзор и Codelab для генерации текста с помощью Bloom Оглавление Что такое Блум? Некоторые предостережения Настройка среды Скачивание предварительно обученного токенизатора и модели..

    Создание кнопочного меню с использованием HTML, CSS и JavaScript
    Вы будете создавать кнопочное меню, которое имеет состояние наведения, а также позволяет вам выбирать кнопку при нажатии на нее. Финальный проект можно увидеть в этом Codepen . Шаг 1..

    Внедрите OAuth в свои веб-приложения для повышения безопасности
    OAuth — это широко распространенный стандарт авторизации, который позволяет приложениям получать доступ к ресурсам от имени пользователя, не раскрывая его пароль. Это позволяет пользователям..

    Классы в JavaScript
    class является образцом java Script Object. Конструкция «class» позволяет определять классы на основе прототипов с чистым, красивым синтаксисом. // define class Human class Human {..

    Как свинг-трейдеры могут использовать ИИ для больших выигрышей
    По мере того как все больше и больше профессиональных трейдеров и активных розничных трейдеров узнают о возможностях, которые предоставляет искусственный интеллект и машинное обучение для улучшения..

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