Используйте максимальный процессор для решения перестановок 10 в кратчайшие сроки - PullRequest
0 голосов
/ 28 марта 2020

Я использую эту программу, написанную на C, чтобы определить перестановки размера 10 обычного алфавита. Когда я запускаю программу, она использует только 36% моего процессора 3GHz, оставляя 50% свободного. Он также использует только 7 МБ моей 8 ГБ оперативной памяти. Я хотел бы использовать не менее 70-80% производительности моего компьютера, а не только это страдание. Это ограничение делает эту процедуру очень трудоемкой, и я не знаю, когда (количество дней) мне понадобится полный вывод. Мне нужна помощь для решения этой проблемы в кратчайшие сроки, будь то улучшение исходного кода или другие возможности.

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

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static int count = 0;

void print_permutations(char arr[], char prefix[], int n, int k) {
    int i, j, l = strlen(prefix);
    char newprefix[l + 2]; 

    if (k == 0) {
       printf("%d %s\n", ++count, prefix);
       return;
    }
    for (i = 0; i < n; i++) {
        //Concatenation of currentPrefix + arr[i] = newPrefix
        for (j = 0; j < l; j++)   
            newprefix[j] = prefix[j];
        newprefix[l] = arr[i];
        newprefix[l + 1] = '\0'; 

        print_permutations(arr, newprefix, n, k - 1); 
    }
}

int main() {            
    int n = 26, k = 10;
    char arr[27] = "abcdefghijklmnopqrstuvwxyz";

    print_permutations(arr, "", n, k);  

    system("pause");
    return 0;        
}

Ответы [ 2 ]

1 голос
/ 29 марта 2020

Существуют фундаментальные проблемы с вашим подходом:

  • Чего вы пытаетесь достичь?

    Если вы хотите перечислить перестановки размером 10 обычного алфавита , ваша программа имеет недостатки, поскольку она перечисляет все комбинации из 10 букв алфавита. Ваша программа будет производить 26 10 комбинаций, огромное количество 141167095653376 , 141,167 миллиарда! Игнорирование нумерации, которая будет превышать диапазон типа int, это более 1,5 петабайта, вряд ли уместится на вашем пространстве хранения. Запись этого с максимальной скоростью 100 МБ / с займет более 20 дней.

    Количество перестановок, то есть комбинаций различных букв из 26 букв алфавита, не так велико: 26! / 16! который все еще велик: 19275223968000 , в 7 раз меньше, чем предыдущий результат. Это по-прежнему более 212 терабайт памяти и 3 дня при скорости 100 МБ / с.

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

  • Тест на меньших наборах для проверки правильности

    Учитывая ожидаемый Размер проблемы, вы должны сначала проверить для меньших значений, таких как перечисление перестановок букв 1, 2 и 3, чтобы убедиться, что вы получите ожидаемое количество результатов.

  • Один раз у вас есть правильность, только тогда сосредоточьтесь на производительности

    Выбирая различные методы вывода, от printf("%d %s\n", ++count, prefix); до ++count; puts(prefix); до ++count;, вы увидите, что большую часть времени тратится на создание вывод. Как только вы прекратите производить вывод, вы можете увидеть, что strlen() потребляет значительную часть времени выполнения, что бесполезно, поскольку вы можете передавать длину префикса из вызывающей стороны. Дальнейшие улучшения могут быть связаны с использованием общего массива для текущего префикса, что исключает необходимость копирования на каждом рекурсивном шаге.

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

  • Использование памяти не является мерой производительности

    Использование как можно большего объема памяти не является цель сама по себе. Некоторые проблемы могут потребовать компромисса между памятью и временем, когда более быстрое время решения достигается с использованием большего количества памяти ядра, но это не так. 8 МБ на самом деле намного больше, чем фактические потребности вашей программы: это количество включает в себя все пространство стека, назначенное программе, из которых будет использоваться только крошечная доля.

    Фактически, использование меньшего количества памяти может улучшить Общая производительность, поскольку ЦП будет лучше использовать свои различные кэши.

Вот модифицированная программа:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

static unsigned long long count;

void print_permutations(char arr[], int n, char used[], char prefix[], int pos, int k) {
    if (pos == k) {
        prefix[k] = '\0';
        ++count;
        //printf("%llu %s\n", count, prefix);
        //puts(prefix);
        return;
    }
    for (int i = 0; i < n; i++) {
        if (!used[i]) {
            used[i] = 1;
            prefix[pos] = arr[i];
            print_permutations(arr, n, used, prefix, pos + 1, k);
            used[i] = 0;
        }
    }
}

int main(int argc, char *argv[]) {
    int n = 26, k = 10;
    char arr[27] = "abcdefghijklmnopqrstuvwxyz";
    char used[27] = { 0 };
    char perm[27];
    unsigned long long expected_count;
    clock_t start, elapsed;

    if (argc >= 2)
        k = strtol(argv[1], NULL, 0);
    if (argc >= 3)
        n = strtol(argv[2], NULL, 0);

    start = clock();
    print_permutations(arr, n, used, perm, 0, k);
    elapsed = clock() - start;
    expected_count = 1;
    for (int i = n; i > n - k; i--)
        expected_count *= i;
    printf("%llu permutations, expected %llu, %.0f permutations per second\n",
           count, expected_count, count / ((double)elapsed / CLOCKS_PER_SEC));
    return 0;
}

Без вывода эта программа перечисляет 140 миллионов комбинаций в секунду на моем медленном ноутбуке потребовалось бы 1,5 дня , чтобы перечислить 19275223968000 10-буквенные перестановки из 26-буквенного алфавита. Он использует почти 100% одного ядра, но процессор все еще на 63% простаивает, поскольку у меня двухъядерный гиперпоточный процессор Intel Core i5. Использование нескольких потоков должно повысить производительность, но программа должна быть изменена, чтобы больше не использовать глобальную переменную count.

1 голос
/ 29 марта 2020

Есть несколько причин вашего плохого опыта:

  1. Ваш показатель c: Ваш показатель c в корне ошибочен. Peak-CPU% - это неточное измерение того, «сколько работы выполняет мой CPU». Что обычно не совсем то, что вас больше всего интересует. Вы можете завышать это число, если я выполняю больше работы (например, запускаю другой поток, который вообще не способствует выводу). Ваш правильный показатель c будет количество элементов в секунду: сколько различных строк будет напечатано или записано в файл в секунду. Чтобы измерить это, запустите тестовый прогон с меньшим размером (например, k = 4) и измерьте, сколько времени это займет.

  2. Ваша проблема: Ваша проблема жесткий. Печать или запись всех 26^10 ~1.4e+14 различных слов с ровно 10 буквами займет некоторое время. Даже если вы изменили его на все перестановки - чего не делает ваша программа - это все равно ~1.9e13. Полученный файл будет иметь размер 1,4 петабайта, что, скорее всего, больше, чем допустит ваш жесткий диск. Кроме того, если вы использовали свой процессор на 100% и использовали тысячу циклов для одного слова, это заняло бы 1,5 года. 1000 циклов - это верхняя граница, вы, скорее всего, не будете быстрее, чем при печати результата, так как printf обычно занимает около 1000 циклов.

  3. Ваш вывод: Запись в стандартный вывод выполняется медленно для записи в файл, см. { ссылка }.

  4. Ваша программа: Есть проблемы с вашей программой, которая может быть проблемой для вашей производительности. Однако в них преобладают другие проблемы, указанные здесь. С моей настройкой эта программа использует 93,6% времени выполнения в printf. Поэтому оптимизация этого кода не даст удовлетворительных результатов.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...