- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我有一个使用工具集 V10 的托管 VS2010 C++ 项目。我想不通的是,如果我使用调试配置编译我的项目,hash_map 析构函数会非常慢。 hash_map.clear() 稍快一些,但同样痛苦。
注意:VS2015 + Win10无法复现。很可能是 VS2010 问题。
我环顾网络,但没有任何东西能解释我得到的结果。
1) 我检查了 _NO_DEBUG_HEAP=1 环境设置。这对我不起作用,因为我没有通过 VS 进行调试。我只是在调试配置中编译代码并在没有调试器的情况下运行它。
2) 这与插入大量数据无关。插入没问题。这只是简单地从 hash_map 中清除数据。
3) 我以为如果我在 C++ 代码生成设置下关闭 C++ 异常,我可以解决问题,但事实并非如此。
如果我在发布配置中编译代码,销毁是即时的。如果我在调试配置中编译代码,破坏大约需要 5 分钟或更长时间,这取决于数据有多大。
我确定这只是我需要更正的某种 C++ 项目设置。有人知道如何解决这个问题吗?
回顾一下,我的项目是 VS2010 Managed C++(C++ 和 Managed C# 对象的混合),工具集是 v10。当我使用调试配置编译代码时,销毁 hash_map 需要 5 分钟(比插入数据本身慢)。当我使用 Release Configuration 编译代码时,它是即时的。
我该如何解决这个问题?谢谢。
这是我使用 VS2010 制作的新项目的完整代码。我用了不到 5 秒的时间来插入元素。myMap.clear() 需要 202 秒。myMap 析构函数需要 280 秒。
这就是我所做的。
1) 使用 VS2010 创建新的 C++ 控制台应用程序...
2) 设置配置属性...
2.1) 一般 > 通用语言运行时支持 > 不支持....
2.2) 通用 > 字符集 > 使用多字节...
2.3) C/C++ > 一般 > 公共(public)语言运行时支持 > 是/clr...
2.4) C/C++ > 通用 > 调试信息格式 > 程序数据库/Zi...
2.5) C/C++ > 代码生成 > 启用最小重建 > 否/Gm-...
2.6) C/C++ > 代码生成 > 启用 C++ 异常 > Yes with SEH/EHa...
Use Koby code below.
更多更新:
这似乎也依赖于硬件? 30K 和 40K 之间的插入性能差异很大。插入停留在 32K 一段时间,然后快速插入其余项目。发生这种情况时,map.clear() 或析构函数会变得非常慢。
初始容量可能在不同的机器上不同?我在 64 位 Windows7 上使用具有 4GB RAM 的虚拟机。该程序在 Debug 配置下为 32 位。
Koby code result. Running compiled exe without VS2010.
Testing with 30K items:
TestClear()
Insertion: 0.163s
Clear: 0.075s
TestDestructor()
Insertion: 0.162s
Destruction: 4.262s
Testing with 40K items:
TestClear()
Insertion: 4.552s
Clear: 197.363s
TestDestructor()
Insertion: 4.49s
I gave up since destructor is much slower in 30K result..
在 VS2015 和 64 位 Win10 上测试
Testing with 4M items:
TestClear()
Insertion: 8.988s
Clear: 0.878s
TestDestructor()
Insertion: 9.669s
Destruction: 0.869s
结论:VS2010 很可能有问题。我将其标记为已解决,因为 VS2010 太旧了。我将尝试将整个解决方案升级到更高的 VS。谢谢科比。
最佳答案
长话短说: std::hash_map
不是完成这项工作的正确工具。这是一个non-standard extension多年前在 MSVC 中已弃用。继续阅读 std::unordered_map
, 它应该满足您的一般性能要求。另请参阅 this .
在撰写本文时,我使用的是 VS 2017 15.3.5,因此我的 std 库 header 拷贝是最新的。我不再拥有 VS 2010 头文件的拷贝,因此我的回答可能不完全适用于您的版本。如果可能的话,你真的应该升级。
首先我们来分析一下hash_map.clear()
:
void clear() _NOEXCEPT
{ // erase all
_List.clear();
_Init();
}
_List
是一个 std::list
。清除它需要在内存破坏对象周围弹跳,直到所有节点都被清理干净。虽然对于链表来说是必需的,但它对性能来说很糟糕。链接列表通常会导致许多 cache misses .这是大部分问题。
现在让我们看一下_Init()
:
void _Init(size_type _Buckets = _Min_buckets)
{ // initialize hash table with _Buckets buckets, leave list alone
_Vec.reserve(2 * _Buckets); // avoid curdling _Vec if exception occurs
_Vec.assign(2 * _Buckets, _Unchecked_end());
_Mask = _Buckets - 1;
_Maxidx = _Buckets;
}
_Vec
是迭代器的 std::vector
。_Min_buckets
在我的拷贝中是 8,reserve()
只增长,从不收缩,所以我们可以假设大多数时间都没有发生重新分配(至少从清除开始)。但是,通过调用它会执行多个分支、潜在的大量析构函数调用和 8 次迭代器分配。这可能看起来不多,但可以很快加起来。
如果不编写您自己的 HashMap 或使用替代实现,您将无能为力。幸运的是,标准为我们提供了 std::unordered_map
.
更新:
我在 VS 2015 和 VS 2017 中运行了您的示例,更改了您提到的所有设置以尝试重现缓慢的场景。在所有情况下,它都很快完成,不会超过一秒钟。
您的示例不会独立测量 clear()
或销毁时间。这是一个针对 C++/CLI(托管 C++ 的后继者)的修改版本。如果它不针对 VS 2010 进行编译,请修改 main
的签名。
#include "stdafx.h"
#define _SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
#include <hash_map>
#include <iostream>
#include <sstream>
#include <time.h>
using namespace System;
namespace Test
{
typedef stdext::hash_map<unsigned int, float> Collection;
typedef std::pair<unsigned int, float> Pair;
const int Iterations = 40000;
const int Multiple = 1000;
const float Increment = 0.004;
clock_t startTime = 0;
std::string ToDisplayString(double count)
{
const double Million = 1000000;
const double Thousand = 1000;
std::stringstream stream;
if (count > Million) {
stream << (count / Million) << "M";
} else if (count > Thousand) {
stream << (count / Thousand) << "K";
} else {
stream << count;
}
return stream.str();
}
void ResetClock()
{
startTime = clock();
}
double GetElapsedSeconds()
{
return (clock() - startTime) / (double)CLOCKS_PER_SEC;
}
void ClearSample()
{
std::cout << "TestClear()\n";
Collection map;
double duration;
ResetClock();
for (int i = 0; i < Iterations; i++) {
if (i % Multiple == 0) {
if (i != 0) {
std::cout << ' ';
}
std::cout << i;
}
map.insert(Pair(i, i + Increment));
}
duration = GetElapsedSeconds();
std::cout << "\nInsertion: " << duration << "s";
ResetClock();
map.clear();
duration = GetElapsedSeconds();
std::cout << "\nClear: " << duration << "s";
}
void DestructorSample()
{
std::cout << "TestDestructor()\n";
double duration;
{
// Moved to a block so we can time destruction.
Collection map;
ResetClock();
for (int i = 0; i < Iterations; i++) {
if (i % Multiple == 0) {
if (i != 0) {
std::cout << ' ';
}
std::cout << i;
}
map.insert(Pair(i, i + Increment));
}
duration = GetElapsedSeconds();
std::cout << "\nInsertion: " << duration << "s";
ResetClock();
}
duration = GetElapsedSeconds();
std::cout << "\nDestruction: " << duration << "s";
}
}
int main(array<String^>^ args)
{
std::cout << "Testing with " << Test::ToDisplayString(Test::Iterations) << " items:\n\n";
Test::ClearSample();
std::cout << "\n\n";
Test::DestructorSample();
std::cin.get();
return 0;
}
这些是我在多次运行中测得的平均时间:
清除样本()
插入:0.45s
清除:0.021s
析构函数示例()
插入:0.4s
破坏:0.013s
关于如果在调试配置中编译 C++ hash_map.clear() 很慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46801316/
在visual C++下,我们有“hash_map”和“hash_set”。在 g++ 中,我们有“stdext::hash_map”和“stdext::hash_set”。它们在各自的性能或其他因素
我在 Ubuntu 中使用 GCC 定义了一个 hash_map stringHashMap。我保证 stringHashFunction 是正确的,因为我可以在 hash_map 中正确使用 str
我知道这应该很容易,但我就是没能消除编译错误。这是我的代码: template struct basic_field_type_map : public hash_map, basic_string>
我正在尝试读取单词列表并将它们连同它们在按字母顺序排序的文件中的位置一起保存在 C++ STL hash_map 中。这个想法是稍后我需要能够判断一个字符串是否是一个词以及它是在另一个词之前还是之后。
#include using namespace std; class hash_t : public __gnu_cxx::hash_map > { }; hash_t hash; ... 我在使
我想要一个 std::hash_map 将(例如)常规 std:string 映射到 多个不同的特化另一个模板类。 这个例子是我想要实现的(虽然它是错误的并且无法编译): template class
hash_map 和 hash_set header 尚未包含在 C++ 标准中,但它们可作为我最近使用的所有编译器的扩展使用。 我想知道在不牺牲可移植性的情况下,在实际代码中我可以在多大程度上依赖这
我想看一个如何正确重写 stdext::hash_compare 的简单示例,以便为我自己的用户定义类型定义新的哈希函数和比较运算符。我正在使用 Visual C++ (2008)。 最佳答案 这就是
我使用以下结构: hash_map> 当我最初用从文本文件中读取的信息填充散列映射时,我可以毫无问题地将元素插入到那些 time_t 列表中。 hash_t::iterator it = hash.f
是否保证当 hash_map/unordered_map 加载相同的项目时,它们在迭代时具有相同的顺序?基本上我有一个 HashMap ,我从一个文件加载它,我定期将有限数量的项目提供给一个例程,之后
我有一个 HashMap 定义为 class KeyType { int key; mutable bool flag; KeyType(int key) : key(key)
我是 c++ 中的 hash_map 的新手。我必须将表转换为散列图。 这就是我在程序中声明和使用 hash_map 的方式 我正在使用 Microsoft Visual Studio。 #inclu
如何将迭代器作为 hash_map 的键? 你会如何在 gcc、Microsoft c++ 下定义它? 例如 vector::iterator i; hash_map::iterato
我有一个多线程应用程序,它使用 C++ STL hash_map 来存储键值对。 hash_map 定义如下: struct eqstr { bool operator()(const strin
我正在比较我编写的一个简单的哈希函数,该函数只是将它乘以一个质数模另一个质数(表大小),结果证明 STL 慢了 100 倍。这是我写的测试方法: stdext:: hash_map hashDict;
我创建了一个小类并尝试将其用作 STL hash_map 中的数据。大量搜索让我知道我需要添加一些东西作为分配器,但到目前为止我还没有看到如何在我的类里面使用 STL 中的默认分配器的示例。有人可以帮
key 的哈希值被计算出来并除以一个质数。一般来说,是否有任何标准素数(比如 32/64 位)? 我的理解是哈希表不可调整大小/不可调整,其内部数组取决于此。如果我有一个只有 5 个元素的哈希表,键空
我想访问 C++ hash_map 的散列值。我试过: __gnu_cxx::hash_map my_table; const hash hh = my_table.hash_funct(); str
我正在开发一个高性能应用程序,其中所有调用都必须合理。我有一张 map ,在每次交易开始时使用一次来进行我想改进的查找。 map 在启动时加载,之后不会改变。 下面映射中的键是一个 std::stri
使用 STL C++ hash_map... class MyKeyObject { std::string str1; std::string str2; bool oper
我是一名优秀的程序员,十分优秀!