使用std :: map查找数据中的重复项及其性能问题。我可以预先分配吗?

时间:2017-06-07 19:38:33

标签: c++ visual-studio visual-studio-2008 stdmap

我的目标是检查自定义数据结构中的重复ID。我用using SharpDX; using SharpDX.Windows; using SharpDX.D3DCompiler; using SharpDX.Direct2D1; using SharpDX.Direct3D11; using SharpDX.DXGI; using SharpDX.Mathematics; using System.Drawing; using System.Windows.Forms; using Device = SharpDX.Direct3D11.Device; using Color = SharpDX.Color; namespace SharpDXWic { public class SharpDXDisplay : IDisposable { private const int WIDTH = 1500; private const int HEIGHT = 800; private Device device; private SwapChain swapChain; private RenderForm renderForm; private RenderTargetView targetView; public SharpDXDisplay(string display_title) { renderForm = new RenderForm(display_title); renderForm.Width = WIDTH; renderForm.Height = HEIGHT; Texture2D target; SwapChainDescription scd = new SwapChainDescription() { BufferCount = 1, Flags = SwapChainFlags.None, IsWindowed = true, ModeDescription = new ModeDescription(WIDTH,HEIGHT, new Rational(60, 1),Format.R8G8B8A8_UNorm), OutputHandle = renderForm.Handle, SampleDescription = new SampleDescription(1, 0), SwapEffect = SwapEffect.Discard, Usage = Usage.RenderTargetOutput }; Device.CreateWithSwapChain( SharpDX.Direct3D.DriverType.Hardware,DeviceCreationFlags.Debug, scd, out device, out swapChain); target = Texture2D.FromSwapChain<Texture2D>(swapChain, 0); targetView = new RenderTargetView(device, target); device.ImmediateContext.OutputMerger.SetRenderTargets(targetView); renderForm.Show(); device.ImmediateContext.ClearRenderTargetView(targetView, Color.CornflowerBlue); swapChain.Present(0, PresentFlags.None); } private void OnClosing() { Dispose(); } public void Dispose() { device.Dispose(); swapChain.Dispose(); renderForm.Dispose(); targetView.Dispose(); } } } 编码。所以我编写了以下仅调试器函数,在处理代码时对数据进行“健全性检查”。

我正在使用以下方法:

  • 我列举了数据结构中的所有元素(这本身就相对较快。)

  • 然后我使用Visual Studio 2008 Sp1收集所有元素的ID作为键,同时我将std::map部分指向数据结构元素本身(这将帮助我快速识别重复。 )

  • 对于我的数据结构中的每个元素,我看到它的ID是否在我的value中,如果是,则该函数断言。否则,它会将其添加到地图中。

这是一个伪代码:

std::map

这是有效的,除了上面的函数随着我的数据结构的大小增长而指数地变慢。因为我在整个“调试器构建”代码中随处使用它,所以加速它会很好。

所以我想要预先分配std::map<ULONGLONG, BIN_XML_TAG*> dicTagIDs; for(std::vector<MY_ELEMENT>::const_iterator iTag = arrNode.begin(); iTag != arrNode.end(); ++iTag) { std::map<ULONGLONG, MY_ELEMENT*>::const_iterator itrT = dicTagIDs.find(iTag->uiTagID); if(itrT != dicTagIDs.end()) { //Duplicate tag ID! MY_ELEMENT* pDuplicateToTag = itrT->second; ASSERT(NULL); } //If not, then add it dicTagIDs[iTag->uiTagID] = iTag._Myptr; //... may later call itself recursively } 。我不知道我的数据结构中的元素的确切数量(它的结构有点像XML文件)但我可以做一个近似猜测,我可以用它来预分配。

std::map似乎没有提供std::map方法。

那么如何提高速度呢?

3 个答案:

答案 0 :(得分:3)

在大多数实现中,

std::unordered_map对于键查找更快(在我的经验中快得多)(在算法上,它们是O(1)而不是O(log(n))的{​​{1}}) 。

他们有一个reserve()方法

答案 1 :(得分:3)

  

对于我的数据结构中的每个元素,我看看它的ID是否在我的   std :: map,如果是这样,函数断言。否则它会将其添加到   地图。

您可能不想这样做,只需使用map的insert()成员函数,如果该键已经在地图中,则该函数将失败。

并且你不能在像std :: map这样的基于树的容器中保留空间。

答案 2 :(得分:1)

Map使用树,因此可以按排序顺序遍历键,因此插入和查找都会在lg(n)时间内发生。此外,插入可以触发重新平衡树。无序映射使用哈希表,该表允许以遍历密钥为代价进行恒定时间插入和查找。 Here是一项与您的用例类似的有趣的性能研究。

正如一些人所指出的,您可能无法访问VS2008中的无序地图。您可以通过使用自己的哈希表获得一些好处:只使用H映射数组,并使用模数H查找/插入数组。地图下面的树的平均深度将减少lg(H ),再平衡也应该更便宜。例如:

std::map<int, int> dicts[HASHKEY]; // array of H maps
dicts[id % HASHKEY][id] = whatever; // insertion
auto it = dicts[id % HASHKEY].find(id); // lookup

以下是一些演示加速的示例代码:

#include <cstdlib>
#include <random>
#include <vector>
#include <map>
#include <iostream>
#include <ctime>
#include <cassert>

#define MAX_HASH 16384

int main() {
    unsigned int nIDs = 1 << 21;
    std::vector<int> IDs(nIDs);

    // random fill
    std::random_device rnd;
    std::mt19937 mersenne_engine(rnd());
    std::uniform_int_distribution<int> dist(0, nIDs); // get some collisions
    auto gen = std::bind(dist, mersenne_engine);
    generate(begin(IDs), end(IDs), gen);

    std::map<int, int> dicts[MAX_HASH];

    // time insertion into 1..MAX_HASH maps
    for (unsigned int hash = 1; hash <= MAX_HASH; hash <<= 1) {
        for (unsigned int i = 0; i < hash; ++i)
            dicts[i].clear();

        // time simple insertion/overwrite
        clock_t start = clock();

        for (unsigned int i = 0; i < nIDs; ++i) {
            int id = IDs[i];
            dicts[id % hash][id] = 1;
        }

        double insert_elapsed = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;

        // time lookup
        // shuffle the vector to avoid optimal lookups
        std::shuffle(begin(IDs), end(IDs), mersenne_engine);        
        start = clock();

        for (unsigned int i = 0; i < nIDs; ++i) {
            int id = IDs[i];
            auto it = dicts[id % hash].find(id);
            assert(it->second == 1);
        }

        double lookup_elapsed = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;

        // time lookup+insertion
        for (unsigned int i = 0; i < hash; ++i)
            dicts[i].clear();

        start = clock();

        for (unsigned int i = 0; i < nIDs; ++i) {
            int id = IDs[i];
            unsigned int dnum = id % hash;
            auto it = dicts[dnum].find(id);

            if (it == dicts[dnum].end())
                dicts[dnum][id] = 1;
        }

        double cond_elapsed = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;

        printf("%5d maps, avg. %7ld keys - lookup: %1.3f, lookup: %1.3f, conditional insert: %1.3f\n",
            hash, IDs.size()/hash, insert_elapsed, lookup_elapsed, cond_elapsed);
    }
}

输出:

    1 maps, avg. 2097152 keys - insert: 1.958, lookup: 2.010, conditional insert: 2.473
    2 maps, avg. 1048576 keys - insert: 1.956, lookup: 1.988, conditional insert: 2.390
    4 maps, avg.  524288 keys - insert: 1.911, lookup: 1.982, conditional insert: 2.416
    8 maps, avg.  262144 keys - insert: 1.848, lookup: 1.956, conditional insert: 2.407
   16 maps, avg.  131072 keys - insert: 1.846, lookup: 1.973, conditional insert: 2.312
   32 maps, avg.   65536 keys - insert: 1.814, lookup: 1.882, conditional insert: 2.303
   64 maps, avg.   32768 keys - insert: 1.796, lookup: 1.973, conditional insert: 2.258
  128 maps, avg.   16384 keys - insert: 1.807, lookup: 1.879, conditional insert: 2.250
  256 maps, avg.    8192 keys - insert: 1.785, lookup: 1.849, conditional insert: 2.158
  512 maps, avg.    4096 keys - insert: 1.731, lookup: 1.820, conditional insert: 2.229
 1024 maps, avg.    2048 keys - lookup: 1.743, lookup: 1.787, conditional insert: 2.050
 2048 maps, avg.    1024 keys - lookup: 1.614, lookup: 1.709, conditional insert: 1.993
 4096 maps, avg.     512 keys - lookup: 1.628, lookup: 1.638, conditional insert: 1.963
 8192 maps, avg.     256 keys - lookup: 1.549, lookup: 1.568, conditional insert: 1.803
16384 maps, avg.     128 keys - lookup: 1.468, lookup: 1.474, conditional insert: 1.688

正如您所看到的,随着地图数量的增加,性能会变得更好,但代价是分配给地图对象的内存更多。