我的目标是检查自定义数据结构中的重复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
方法。
那么如何提高速度呢?
答案 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
正如您所看到的,随着地图数量的增加,性能会变得更好,但代价是分配给地图对象的内存更多。