簡體   English   中英

地圖何時變得比兩個向量更好?

[英]When does a map become better than two vectors?

地圖對其所有元素進行二元搜索,這些元素具有對數復雜度 - 這意味着對於足夠小的對象集合,地圖將比具有線性搜索的兩個向量執行得更差。

對象(關鍵字)池應該有多大才能使地圖開始比兩個向量表現更好?

編輯:問題的更通用版本:對象池應該有多大,以便二進制搜索比線性搜索更好?

我使用字符串作為鍵,值是指針,但我的特定用例可能無關緊要。 我更好奇地了解如何正確使用這兩個工具。

如果你原諒我這么說的話,大多數答案聽起來像我說的各種方式:“我不知道”,沒有實際承認他們不知道。 雖然我普遍同意他們給出的建議,但似乎沒有人試圖直接解決你提出的問題:什么是收支平衡點。

公平地說,當我讀到這個問題時,我也不知道。 這是我們所知道的基本知識之一:對於足夠小的集合,線性搜索可能會更快,而對於足夠大的集合,二進制搜索無疑會更快。 然而,我從來沒有太多理由去調查關於盈虧平衡點的真實情況。 然而,你的問題讓我很好奇,所以我決定編寫一些代碼來至少得到一些想法。

這段代碼肯定是一個非常快速的黑客攻擊(很多重復,目前只支持一種類型的密鑰等),但至少它可能會讓人知道會發生什么:

#include <set>
#include <vector>
#include <string>
#include <time.h>
#include <iostream>
#include <algorithm>

int main() {   

    static const int reps = 100000;

    std::vector<int> data_vector;
    std::set<int> data_set;
    std::vector<int> search_keys;

    for (int size=10; size<100; size += 10) {
        data_vector.clear();
        data_set.clear();
        search_keys.clear();

        int num_keys = size / 10;   
        for (int i=0; i<size; i++) {
            int key = rand();

            if (i % num_keys == 0)
                search_keys.push_back(key);

            data_set.insert(key);
            data_vector.push_back(key);
        }

        // Search for a few keys that probably aren't present.
        for (int i=0; i<10; i++)
            search_keys.push_back(rand());

        long total_linear =0, total_binary = 0;

        clock_t start_linear = clock();
        for (int k=0; k<reps; k++) {
            for (int j=0; j<search_keys.size(); j++) {
                std::vector<int>::iterator pos = std::find(data_vector.begin(), data_vector.end(), search_keys[j]);
                if (pos != data_vector.end())
                    total_linear += *pos;
            }
        }
        clock_t stop_linear = clock();                      

        clock_t start_binary = clock();
        for (int k=0; k<reps; k++) {
            for (int j=0; j<search_keys.size(); j++) {
                std::set<int>::iterator pos = data_set.find(search_keys[j]);
                if (pos != data_set.end())
                    total_binary += *pos;
            }
        }
        clock_t stop_binary = clock();

        std::cout << "\nignore: " << total_linear << " ignore also: " << total_binary << "\n";

        std::cout << "For size = " << size << "\n";
        std::cout << "\tLinear search time = " << stop_linear - start_linear << "\n";
        std::cout << "\tBinary search time = " << stop_binary - start_binary << "\n";
    }
    return 0;
}

以下是我在我的機器上運行的結果:

ignore: 669830816 ignore also: 669830816
For size = 10
        Linear search time = 37
        Binary search time = 45

ignore: 966398112 ignore also: 966398112
For size = 20
        Linear search time = 60
        Binary search time = 47

ignore: 389263520 ignore also: 389263520
For size = 30
        Linear search time = 83
        Binary search time = 63

ignore: -1561901888 ignore also: -1561901888
For size = 40
        Linear search time = 106
        Binary search time = 65

ignore: -1741869184 ignore also: -1741869184
For size = 50
        Linear search time = 127
        Binary search time = 69

ignore: 1130798112 ignore also: 1130798112
For size = 60
        Linear search time = 155
        Binary search time = 75

ignore: -1949669184 ignore also: -1949669184
For size = 70
        Linear search time = 173
        Binary search time = 83

ignore: -1991069184 ignore also: -1991069184
For size = 80
        Linear search time = 195
        Binary search time = 90

ignore: 1750998112 ignore also: 1750998112
For size = 90
        Linear search time = 217
        Binary search time = 79

顯然,這不是唯一可能的測試(甚至接近最好的測試),但在我看來,即使是一點點硬數據也比沒有更好。

編輯:我會注意到記錄,我認為沒有理由使用兩個向量(或對向量)的代碼不能像使用集合或映射的代碼一樣干凈。 顯然,你會想要把它的代碼到一個小它自己的階級,但我看不出有任何理由可言,它可能不存在完全相同的界面向外界表明map一樣。 事實上,我可能只是稱它為“tiny_map”(或該命令中的某些東西)。

面向對象編程的基本要點之一(在通用編程中仍然如此,至少在某種程度上)是將接口與實現分開。 在這種情況下,您所說的純粹是一個不需要影響接口的實現細節。 事實上,如果我正在編寫一個標准庫,我很想把它作為一個“小地圖優化”,類似於常見的小字符串優化。 基本上,只需在map對象本身中直接分配一個包含value_type的10個(或左右)對象的數組,並在/如果地圖很小時使用它們,然后將數據移動到樹中,如果它變大到足以證明它的合理性。 唯一真正的問題是人們是否經常使用微小的地圖來證明這項工作的合理性。

map的代碼比兩個vector的代碼要清晰得多; 那應該是你最關心的問題。 只有當您確定map的性能在您自己的應用程序中存在問題時,您才會擔心差異,此時您應該自己對其進行基准測試。

地圖永遠不會等於有序矢量搜索速度,因為矢量內存更好地打包,內存訪問更直接。

然而,這只是故事的一半。 一旦開始修改容器,節點基容器(不需要在其元素周圍移動以容納中間的插入),就可以獲得理論上的優勢。 與搜索一樣,更好的緩存局部性仍然為少量元素提供了矢量邊緣。

精確的測量很難提供,並且取決於具體的優化,上下文使用和機器,因此我建議簡單地按功能進行。 地圖有一個按鍵搜索的自然界面,所以使用它會讓你的生活更輕松。

如果你真的發現自己正在測量並且需要真正地從性能中擠出性能,那么你可能需要更專業的數據結構。 查找B樹。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM