У меня есть .NET и С++ реализации перфекционной тестовой функции, которая делает 854,750 поисков в словаре, используя строковые ключи из пула из 6838 ключей. Я написал эти функции, чтобы исследовать "узкое место" в реальном приложении.
.NET-реализация написана в F #, использует словарь и скомпилирована для .NET 4.0
Реализация С++ использует std:: unordered_map и встроена в VS2010 в режиме Release.
На моей машине .NET-код работает в среднем 240 мс, а код С++ работает через 630 мс. Не могли бы вы помочь мне понять, что может быть причиной этой огромной разницы в скорости?
Если я делаю ключевую длину в реализации С++ короче и использую префикс "key_" вместо "key_prefix_", он будет работать через 140 мс.
Другим трюком, который я пытался, является замена std::string на пользовательскую неизменяемую строковую реализацию с указателем const char * на источник и одноразовый вычисленный хеш. Использование этой строки позволило получить производительность реализации С++ до 190 мс.
Код С++:
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 # code
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