Параллельное создание последовательных ключей - PullRequest
1 голос
/ 01 апреля 2010

Я работаю над проектом, который генерирует очень большое количество последовательных текстовых строк в очень узком цикле. Мое приложение интенсивно использует расширения набора команд SIMD, такие как SSE и MMX, в других частях программы, но генератор ключей - простой C ++.

Способ работы моего генератора ключей заключается в том, что у меня есть класс keyGenerator, который содержит один массив символов, в котором хранится текущий ключ. Чтобы получить следующий ключ, есть функция с именем «incrementKey», которая обрабатывает строку как число, добавляя ее к строке и перенося, где это необходимо.

Теперь проблема в том, что кейген является узким местом. Это быстро, но было бы хорошо, если бы это было быстрее. Одна из самых больших проблем заключается в том, что когда я генерирую набор последовательных ключей для обработки с использованием моего кода SSE2, мне нужно хранить весь набор в массиве, что означает, что я должен последовательно генерировать и копировать 12 строк в массив один за другим, вот так:

char* keys[12];
for(int i = 0; i < 12; i++)
{
    keys[i] = new char[16];
    strcpy(keys[i], keygen++);
}

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

Вот код, соответствующий генератору ключей:

KeyGenerator.h

class keyGenerator
{

public:

    keyGenerator(unsigned long long location, characterSet* charset)
            : location(location), charset(charset)
    {           
        for(int i = 0; i < 16; i++)
            key[i] = 0;

        charsetStr = charset->getCharsetStr();
        integerToKey();
    }

    ~keyGenerator()
    {
    }

    inline void incrementKey()
    {
        register size_t keyLength = strlen(key);

        for(register char* place = key; place; place++)
        {
            if(*place == charset->maxChar)
            {
                // Overflow, reset char at place
                *place = charset->minChar;

                if(!*(place+1))
                {
                    // Carry, no space, insert char
                    *(place+1) = charset->minChar;
                    ++keyLength;

                    break;
                }
                else
                {
                    continue;
                }
            }
            else
            {
                // Space available, increment char at place
                if(*place == charset->charSecEnd[0]) *place = charset->charSecBegin[0];
                else if(*place == charset->charSecEnd[1]) *place = charset->charSecBegin[1];

                (*place)++;

                break;
            }
        }
    }

    inline char* operator++() // Pre-increment
    {
            incrementKey();
            return key;
    }

    inline char* operator++(int) // Post-increment
    {
            memcpy(postIncrementRetval, key, 16);
            incrementKey();

            return postIncrementRetval;
    }

    void integerToKey()
    {
        register unsigned long long num = location;

        if(!num)
        {
            key[0] = charsetStr[0];
        }
        else
        {
            num++;

            while(num)
            {
                num--;
                unsigned int remainder = num % charset->length;
                num /= charset->length;

                key[strlen(key)] = charsetStr[remainder];
            }
        }
    }

    inline unsigned long long keyToInteger()
    {
        // TODO
        return 0;
    }

    inline char* getKey()
    {
        return key;
    }

private:

    unsigned long long location;

    characterSet* charset;
    std::string charsetStr;

    char key[16];

    // We need a place to store the key for the post increment operation.
    char postIncrementRetval[16];
};

CharacterSet.h

struct characterSet
{
    characterSet()
    {
    }

    characterSet(unsigned int len, int min, int max, int charsec0, int charsec1, int charsec2, int charsec3)
    {
        init(length, min, max, charsec0, charsec1, charsec2, charsec3);
    }

    void init(unsigned int len, int min, int max, int charsec0, int charsec1, int charsec2, int charsec3)
    {
        length = len;
        minChar = min;
        maxChar = max;

        charSecEnd[0] = charsec0;
        charSecBegin[0] = charsec1;
        charSecEnd[1] = charsec2;
        charSecBegin[1] = charsec3;
    }

    std::string getCharsetStr()
    {
        std::string retval;

        for(int chr = minChar; chr != maxChar; chr++)
        {
            for(int i = 0; i < 2; i++) if(chr == charSecEnd[i]) chr = charSecBegin[i];
            retval += chr;
        }

        return retval;
    }

    int minChar, maxChar;

    // charSec = character set section
    int charSecEnd[2], charSecBegin[2];

    unsigned int length;
};

1 Ответ

1 голос
/ 01 апреля 2010

Хорошо. Что касается производительности, все новые / strcpy / strmp, вероятно, причиняют вам больший вред, чем ваш keygen.

Размещайте свою память в большем пуле за раз, а затем используйте указатели внутри нее.

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

На определенных интервалах вы можете использовать SSE / MMX для создания ключей, по крайней мере, когда строка выровнена и делится на длину вашего слова SSE / MMX. Вы также можете попробовать заполнить его нулями, а затем сдвинуть их, если строка не является. Это, вероятно, не стоит усилий, если вы генерируете только 16 за раз.

...