C ++ ~ 1M поисков в unordered_map со строковым ключом работает намного медленнее, чем код .NET

У меня есть .NET и C ++ реализации тестовой функции perf, которая выполняет 854 750 поисков в словаре с использованием строковых ключей из пула из 6838 ключей. Я написал эти функции, чтобы исследовать узкое место производительности в реальном приложении.

Реализация .NET написана на F #, использует словарь и скомпилирована для .NET 4.0

Реализация C ++ использует std :: unordered_map и построена с VS2010 в режиме выпуска.

На моей машине код .NET выполняется в среднем за 240 мсек, а код C ++ - за 630 мсек. Не могли бы вы помочь мне понять, в чем может быть причина такой огромной разницы в скорости?

Если я сделаю длину ключа в реализации C ++ короче и использую префикс «key_» вместо «key_prefix_», он будет работать через 140 мс.

Еще один трюк, который я пробовал, - заменить std :: string пользовательской реализацией неизменяемой строки, которая имеет указатель const char * на источник и одноразовый вычисленный хэш. Использование этой строки позволило снизить производительность реализации C ++ до 190 мс.

Код C ++:

struct SomeData
{
public:
    float Value;
};

typedef std::string KeyString;
typedef std::unordered_map<KeyString, SomeData> DictionaryT;

const int MaxNumberOfRuns = 125;
const int MaxNumberOfKeys = 6838;

DictionaryT dictionary;
dictionary.rehash(MaxNumberOfKeys);

auto timer = Stopwatch::StartNew();

int lookupCount = 0;

char keyBuffer[100] = "key_prefix_";
size_t keyPrefixLen = std::strlen(keyBuffer);

/// run MaxNumberOfRuns * MaxNumberOfKeys iterations
for(int runId = 0; runId < MaxNumberOfRuns; runId++)
{
    for(int keyId = 0; keyId < MaxNumberOfKeys; keyId++)
    {
        /// get a new key from the pool of MaxNumberOfKeys keys           
        int randomKeySuffix = (std::rand() % MaxNumberOfKeys);
        ::itoa(randomKeySuffix, keyBuffer + keyPrefixLen, 10);

        KeyString key = keyBuffer;

        /// lookup key in the dictionary         
        auto dataIter = dictionary.find(key);
        SomeData* data;

        if(dataIter != dictionary.end())
        {
            /// get existing value           
            data = &dataIter->second;
        }
        else
        {
            /// add a new value
            data = &dictionary.insert(dataIter, DictionaryT::value_type(key, SomeData()))->second;
        }

        /// update corresponding value in the dictionary
        data->Value += keyId * runId;
        lookupCount++;
    }
}

timer.Stop();
std::cout << "Time: " << timer.GetElapsedMilleseconds() << " ms" << std::endl;
std::cout << "Lookup count: " << lookupCount << std::endl;

Выводит:

Время: 636 мс
Счетчик запросов: 854750

Код F #

open System
open System.Diagnostics
open System.Collections.Generic

type SomeData =
    struct
        val mutable Value : float
    end

let dictionary = new Dictionary<string, SomeData>()
let randomGen = new Random()

let MaxNumberOfRuns = 125
let MaxNumberOfKeys = 6838

let timer = Stopwatch.StartNew()

let mutable lookupCount = 0

/// run MaxNumberOfRuns * MaxNumberOfKeys iterations
for runId in 1 .. MaxNumberOfRuns do
    for keyId in 1 .. MaxNumberOfKeys do

        /// get a new key from the pool of MaxNumberOfKeys keys
        let randomKeySuffix = randomGen.Next(0, MaxNumberOfKeys).ToString()        
        let key = "key_prefix_" + randomKeySuffix

        /// lookup key in the dictionary
        let mutable found, someData = dictionary.TryGetValue (key)
        if not(found) then
            /// add a new value
            someData <- new SomeData()
            dictionary.[key] <- someData

        /// update corresponding value in the dictionary
        someData.Value <- someData.Value + float(keyId) * float(runId)

        lookupCount <- lookupCount + 1

timer.Stop()

printfn "Time: %d ms" timer.ElapsedMilliseconds
printfn "Lookup count: %d" lookupCount

Выводит:

Время: 245 мс
Количество запросов: 854750

23
задан ildjarn 4 December 2011 в 19:05
поделиться