Эффективность времени на std :: tr1 :: unordered_map :: operator [] - PullRequest
2 голосов
/ 13 мая 2011

Я оптимизирую часть кода в Visual Studio 2008 SP1. Зная, что unorder_map потрясающе с постоянной вставкой / удалением / поиском, я оптимизировал код, используя unordered_map в качестве основной структуры данных. Пожалуйста, взгляните на следующий код.

....
    typedef std::tr1::unordered_map <__int64, int> umap_id;
    const int text1_length = text1.GetLength();
    const int text2_length = text2.GetLength();
    const int max_d = text1_length + text2_length - 1;
    const bool doubleEnd = (64 < max_d);
    vector<set<pair<int, int> > > v_map1;
    vector<set<pair<int, int> > > v_map2;
    vector<int> v1(2 *max_d, 0);
    vector<int> v2(2 *max_d, 0);

    int x, y;
    __int64 footstep;
    umap_id footsteps(max_d * 2);
    bool done = false;
    const bool forward = ((text1_length + text2_length) % 2 == 1);

    for (int d = 0; d < max_d; ++d)
    {
        // Walk forward path one step
        v_map1.push_back(set<pair<int, int> >());
        for (int k = -d; k <= d; k += 2)
        {
            if (k == -d || (k != d && v1[k - 1 + max_d] < v1[k + 1 + max_d]))
                x = v1[k + 1 + max_d];
            else
                x = v1[k - 1 + max_d] + 1;
            y = x - k;

            if (doubleEnd)
            {
                footstep = (__int64) ((__int64)x << 32 | y);
                if (!forward)
                    footsteps[footstep] = d;
                else if (footsteps.find(footstep) != footsteps.end())
                    done = true;
            }
            ....
        }
    }
....

Но оказывается, что это все еще довольно медленно. Учитывая мой относительно небольшой ввод (max_d = 946), он работает более 20 секунд.

Я выполнил анализ профилировщика для сборки release , и профилировщик обнаружил эту строку: footsteps[footstep] = d; - главный виновник, который был выполнен 447931 раз и занял около 20 секунд.

Обратите внимание, что в том же теле цикла есть еще одна строка кода: else if (footsteps.find(footstep) != footsteps.end()), которая выполнялась столько же раз (то есть 447931 раз), но стоила намного меньше секунд.

operator::[] из unordered_map кажется черным ящиком для меня. Я не мог понять, почему это так долго. Это 32-битное приложение. Любая помощь приветствуется.

Ответы [ 5 ]

2 голосов
/ 13 мая 2011

В VS 2008 без SP1 (но с пакетом возможностей, который предоставляет библиотеку TR1) хеш-функция по умолчанию для tr1::unordered_map<> учитывает только младшие 32 бита значения ключа.По крайней мере, это из-за того, что я прочитал реализацию template<class _Kty> class hash::operator() в заголовке <functional>.

Переменная footstep, являющаяся ключом, использует все, что рассчитано для y как его младшие 32 бита - достаточно ли этого?вариация в y, что он будет делать хорошее хеш-значение само по себе (я не могу сказать, что делает код, вычисляющий y)?Если нет, то вы можете поместить в конкретное хеш-хранилище гораздо больше элементов, чем вам хотелось бы, и создать слишком много коллизий.

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

Кстати, похоже, что VS 2010 имеет специализации для hash::operator() при использовании с 64-разрядными целыми числами, поэтому он будет хешировать все 64-разрядные - если вы используете VS 2010, предположение в моем ответе должноне применимо.


Обновление:

После некоторого тестирования я убежден, что это проблема (проблема также существует в VS 2008 SP1).Вы можете исправить это, обновив компилятор до VS 2010, который имеет лучшие хэш-функции для 64-битных типов, или используйте свою собственную хеш-функцию, чтобы справиться с этим самостоятельно.Ниже приведен пример, который я быстро протестировал в VS2008, и, похоже, он работает:

class hash_int64
    : public unary_function<__int64, size_t>
{
public:
    typedef __int64 key_t;
    typedef unsigned int half_t;

    size_t operator()(const key_t& key) const
    {   
        // split the 64-bit key into 32-bit halfs, hash each
        // then combine them
        half_t x = (half_t) (key & 0xffffffffUL);
        half_t y = (half_t) (((unsigned __int64) key) >> 32);

        return (hash<half_t>()(x) ^ hash<half_t>()(y));
    }
};

Затем измените typedef на:

typedef std::tr1::unordered_map <__int64, int, hash_int64> umap_id;
2 голосов
/ 13 мая 2011

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

0 голосов
/ 13 мая 2011

В Visual Studio 2005 и 2008 вы должны вручную установить _SECURE_SCL=0. По умолчанию он включен, даже в сборках выпуска, что добавляет множество проверок во время выполнения, которые в некоторых случаях могут быть очень дорогостоящими.

Visual Studio 2010 исправляет это, по умолчанию фактически выпуская сборки fast .

Помимо этого, возможно, стоит поэкспериментировать с заменой структуры данных на старую std::map. И, конечно, если вы используете 64-битные целочисленные ключи в 32-битной сборке, это тоже неоптимально.

Ориентация на x64 может заметно улучшить ситуацию, но если вы застряли на 32-битном, вы могли бы подумать, можно ли заменить целочисленные ключи на двойные, так как процессор может обрабатывать их изначально (я не знаю, что хотя функция хэширования по умолчанию для парных чисел выглядит примерно так, и, возможно, в целом она будет работать медленнее, но, по крайней мере, ее стоит протестировать)

0 голосов
/ 13 мая 2011

Хеш-карты, как известно, имеют довольно высокие постоянные издержки. Только 946 элементов с практически свободным оператором сравнения, вероятно, должны использовать поиск O (log (n)) std::map. Однако нет причины, по которой operator[] должен стоить больше времени, чем find(), если только нет ошибки реализации.

0 голосов
/ 13 мая 2011

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...