2009-06-29 12 views
7

私はHashSetとDictionaryをC#で多く使っていて、非常に高速であることがわかりました...C#HashSet <T>とDictionary <K,V>のような高速C++コンテナ?

私はstd :: mapとstd :: hash_mapを使ってみました。これは予想される動作のようですか?私はstd :: hash_mapの使用で間違っているかもしれない何かがありますか?

または、そこにはC++ハッシュコンテナがありますか?

私はint32sをハッシュしていますが、通常は約100,000個です。

アップデート:C#とC++でreproを作成しました。 2回の試行を行い、C#で19msと13ms、C++で約11,000msかかる。

Found 511 values in the intersection, in 19 ms 
Found 508 values in the intersection, in 13 ms 

C++出力:

Found 308 values in the intersection, in 11764.7ms 
Found 316 values in the intersection, in 11742.8ms 
私のC++コードで、本当に間違って何か:)

(リリースビルドの両方が実行された、両方のコンソールアプリケーションです)

C#出力がなければなりません

出力(std :: mapの代わりにstdext :: hash_mapを使用)

Found 300 values in the intersection, in 383.552ms 
Found 306 values in the intersection, in 2277.02ms 

C++出力

Found 292 values in the intersection, in 1037.67ms 
Found 302 values in the intersection, in 3663.71ms 

ノート(stdext :: hash_map、リリースのx64ビルド使用して):私はC++に望んでいたよう

  • SET2がかなり取り込ま取得されていないが、私が持っていることを期待していましたセット1と50%の交差点(それはC#で行うように)、しかし、私も彼らが部分的に

C#の交差しないことを得るためにいくつかの理由で10で私の乱数を乗算しなければならなかった:

0123を
static void Main(string[] args) 
    { 
     int start = DateTime.Now.Millisecond; 
     int intersectionSize = runIntersectionTest(); 
     int duration = DateTime.Now.Millisecond - start; 

     Console.WriteLine(String.Format("Found {0} values in the intersection, in {1} ms", intersectionSize, duration)); 

     start = DateTime.Now.Millisecond; 
     intersectionSize = runIntersectionTest(); 
     duration = DateTime.Now.Millisecond - start; 

     Console.WriteLine(String.Format("Found {0} values in the intersection, in {1} ms", intersectionSize, duration)); 

     Console.ReadKey(); 
    } 

    static int runIntersectionTest() 
    { 
     Random random = new Random(DateTime.Now.Millisecond); 

     Dictionary<int,int> theMap = new Dictionary<int,int>(); 

     List<int> set1 = new List<int>(); 
     List<int> set2 = new List<int>(); 

     // Create 100,000 values for set1 
     for (int i = 0; i < 100000; i++) 
     { 
      int value = 1000000000 + i; 
      set1.Add(value); 
     } 

     // Create 1,000 values for set2 
     for (int i = 0; i < 1000; i++) 
     { 
      int value = 1000000000 + (random.Next() % 200000 + 1); 
      set2.Add(value); 
     } 

     // Now intersect the two sets by populating the map 
     foreach(int value in set1) 
     { 
      theMap[value] = 1; 
     } 

     int intersectionSize = 0; 

     foreach (int value in set2) 
     { 
      int count; 
      if (theMap.TryGetValue(value, out count)) 
      { 
       intersectionSize++; 
       theMap[value] = 2; 
      } 
     } 

     return intersectionSize; 
    } 

C++:

int runIntersectionTest() 
{ 
    std::map<int,int> theMap; 

    vector<int> set1; 
    vector<int> set2; 

    // Create 100,000 values for set1 
    for (int i = 0; i < 100000; i++) 
    { 
     int value = 1000000000 + i; 
     set1.push_back(value); 
    } 

    // Create 1,000 values for set2 
    for (int i = 0; i < 1000; i++) 
    { 
     int random = rand() % 200000 + 1; 
     random *= 10; 

     int value = 1000000000 + random; 
     set2.push_back(value); 
    } 

    // Now intersect the two sets by populating the map 
    for (vector<int>::iterator iterator = set1.begin(); iterator != set1.end(); iterator++) 
    { 
     int value = *iterator; 

     theMap[value] = 1; 
    } 

    int intersectionSize = 0; 

    for (vector<int>::iterator iterator = set2.begin(); iterator != set2.end(); iterator++) 
    { 
     int value = *iterator; 

     map<int,int>::iterator foundValue = theMap.find(value); 

     if (foundValue != theMap.end()) 
     { 
      theMap[value] = 2; 

      intersectionSize++; 
     } 
    } 

    return intersectionSize; 

} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    srand (time(NULL)); 

    Timer timer; 
    int intersectionSize = runIntersectionTest(); 
    timer.Stop(); 

    cout << "Found " << intersectionSize << " values in the intersection, in " << timer.GetMilliseconds() << "ms" << endl; 

    timer.Reset(); 
    intersectionSize = runIntersectionTest(); 
    timer.Stop(); 

    cout << "Found " << intersectionSize << " values in the intersection, in " << timer.GetMilliseconds() << "ms" << endl; 

    getchar(); 

    return 0; 
} 
+1

ベンチマークを教えてください。 –

+0

C#でおそらく10msかかるのは、C++で1,000msかかると思われます。私は明日より制御の比較をしようとします、たぶん各C#とC + +のコードを投稿してください。 –

+0

ベンチマークを投稿しました。 –

答えて

5

Hash_mapとhash_setは標準ではありません。unordered_mapunordered_setは、間もなく標準バージョンになる予定です。再生器を持たなければ、これは遠くになるとは思わない。内部的には同じデータ構造なので、同様のパフォーマンスが必要です。

私は、Visual Cとして、MSのVisual Studio 2008のv9.0.30729.1の下で提供されているサンプルをコンパイル++

- > Win32の - >コンソールアプリケーション(私はあなたが何であったかわからなかったので、私は私自身のTimerクラスを巻いてもを使用して)。デバッグ中、私は1000ミリ秒の時間を得ましたが、リリース時のコンパイルは50ミリ秒でした。

#include <vector> 
#include <iostream> 
#include <map> 
#include <stdio.h> 
#include <stdlib.h> 
#include <time.h> 

#include <windows.h> 

typedef struct { 
    LARGE_INTEGER start; 
    LARGE_INTEGER stop; 
} stopWatch; 

class CStopWatch { 

private: 
    stopWatch timer; 
    LARGE_INTEGER frequency; 
    double LIToSecs(LARGE_INTEGER & L); 
public: 
    CStopWatch(); 
    void startTimer(); 
    void stopTimer(); 
    double getElapsedTime(); 
}; 

double CStopWatch::LIToSecs(LARGE_INTEGER & L) { 
    return ((double)L.QuadPart /(double)frequency.QuadPart) ; 
} 

CStopWatch::CStopWatch(){ 
    timer.start.QuadPart=0; 
    timer.stop.QuadPart=0; 
    QueryPerformanceFrequency(&frequency) ; 
} 

void CStopWatch::startTimer() { 
    QueryPerformanceCounter(&timer.start) ; 
} 

void CStopWatch::stopTimer() { 
    QueryPerformanceCounter(&timer.stop) ; 
} 

double CStopWatch::getElapsedTime() { 
    LARGE_INTEGER time; 
    time.QuadPart = timer.stop.QuadPart - timer.start.QuadPart; 
    return LIToSecs(time) ; 
} 

using namespace std; 
int runIntersectionTest() 
{ 
    std::map<int,int> theMap; 

    vector<int> set1; 
    vector<int> set2; 

    // Create 100,000 values for set1 
    for (int i = 0; i < 100000; i++) 
    { 
     int value = 1000000000 + i; 
     set1.push_back(value); 
    } 

    // Create 1,000 values for set2 
    for (int i = 0; i < 1000; i++) 
    { 
     int random = rand() % 200000 + 1; 
     random *= 10; 

     int value = 1000000000 + random; 
     set2.push_back(value); 
    } 

    // Now intersect the two sets by populating the map 
    for (vector<int>::iterator iterator = set1.begin(); iterator != set1.end(); iterator++) 
    { 
     int value = *iterator; 

     theMap[value] = 1; 
    } 

    int intersectionSize = 0; 

    for (vector<int>::iterator iterator = set2.begin(); iterator != set2.end(); iterator++) 
    { 
     int value = *iterator; 

     map<int,int>::iterator foundValue = theMap.find(value); 

     if (foundValue != theMap.end()) 
     { 
       theMap[value] = 2; 

       intersectionSize++; 
     } 
    } 

    return intersectionSize; 

} 

int main(int argc, char* argv[]) 
{ 
    srand (time(NULL)); 
    int tests = 2; 
    while(tests--){ 
     CStopWatch timer; 
     timer.startTimer(); 
     int intersectionSize = runIntersectionTest(); 
     timer.stopTimer(); 

     cout << "Found " << intersectionSize << " values in the intersection, in " << timer.getElapsedTime() << "s\r\n"; 
    } 

    getchar(); 

    return 0; 
} 

(私はunordered_mapで試していますが、私のバージョンにはありません)。私はあなたのC + +の設定にいくつかの問題があると思う。

+0

注:boostは両方の実装を提供します。 – GManNickG

+1

私は何かを考え出しました。デバッガをRELEASEまたはDEBUGビルド(たとえば、IDEでF5キーを押す)に接続すると、恐ろしい時間があります。 –

0

それは期待鳴りませんが、私たちは本当に助けることができる前に、あなたはいくつかのより多くの詳細情報を収集する必要があります。誰のhash_map実装を使用していますか?プロファイラを指差しましたか?もしそうなら、あなたに何を伝えましたか?

一般に、ハッシュテーブルの実装が明らかでない理由で不十分なパフォーマンスを示している場合は、通常、テーブルが使用しているハッシュ関数が特定の入力に対して悪い結果をもたらすためです。それはあなたの問題かもしれません.C++のhash_mapはキーを小さなバケットにマップするハッシュ関数を使用しますが、C#HashSetはそうではありません。まったく異なるものかもしれません。

std :: mapは一般にツリーとして実装されているため、異なるパフォーマンス特性を持ちます。ここでも、実装と入力データの詳細が重要です。

+0

hash_mapを使用したとき、私はMicrosoftのものを使用していたと信じています...私はVS 2008を起動し、#includeを入力しました。 Int32番号のhash_mapで良いハッシュ関数に関するヒントはありますか?私はグーグルでやります。 –

+0

VC++チームはIMEのようなものについてはかなり鋭敏です。それはハッシュ関数の問題になる可能性は低いと思います。私は明日サンプルコードを投稿した後で、この問題をより深く見ていきます。 –

+0

サンプルコードが掲載されています。 –

0

私はそれを使ったことがないけどGoogle Sparcehashは、あなたがOの挿入と検索時間を持っているあなたのC++コード、()N(ログ)内のstd ::マップを使用している良いフィット

0

かもしれません。より良い比較を得るためにhash_mapでテストしてみてください。

+0

私はstdxt :: mapをstdext :: hash_mapに切り替えて、WAYの方が良い結果を得ましたが、C#と比べてもひどいです。 交差点の300の値が383.552msで見つかりました 交差点の306の値が2277.02msで見つかりました –

0

何が本当に比較していることは、ほぼ一定と入力サイズの独立した意味、O(1)である

C#ハッシュセットであるC++ベクトル対

....(入力の大きさ)を意味時定数...

これはほとんど実用的ではありません。

あなたは のstd :: tr1を:: unordered_set < ...>(私は考えて2007年にTR1後)でC++でのHashSetと同等のものを使用してみてください(とstd :: tr1をすべきである:: unordered_set < ... >)

wikipedia link on TR1

はまたthis pageのVisual Studioに応じて、独自の次善のSTLのTR1の実装を持っていることに注意してください。 (個人的な経験はなく、見つけたhere

関連する問題