读书人

stl string的内存储器分配居然比HeapA

发布时间: 2013-07-04 11:45:33 作者: rapoo

stl string的内存分配居然比HeapAlloc快40倍!?
先上代码:



#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <string>

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
const size_t MB = 1048576UL;
DWORD dwTick = GetTickCount();

#if 0
//情形1:使用stl string,耗时 406ms
std::string s; //初始为空
for (size_t idx = 1; idx <= 256; ++idx)
{
s.append(MB, '$'); //循环256次,每次追加1M数据
}
#else
//情形2:使用HeapAlloc,耗时 16031ms
char *s = NULL;
HANDLE hHeap = GetProcessHeap();
for (size_t idx = 1; idx <= 256; ++idx) //循环256次
{
//(1)空间比上次扩充1M
if (!s) s = (char*)HeapAlloc(hHeap, 0, MB * idx);
else s = (char*)HeapReAlloc(hHeap, 0, s, MB * idx);

//(2)新扩充的1M填充为'$'
FillMemory(s + (idx - 1) * MB, MB, '$');
}
HeapFree(hHeap, 0, s);
#endif

dwTick = GetTickCount() - dwTick;
return 0;
}


问题是:
情形1:使用stl string,耗时 406ms
情形2:使用HeapAlloc,耗时 16031ms
“情形1”比“情形2”居然快了40倍!!差别在哪里?
std::string的源码犹如天书,没看明白,跟踪发现深层也是用了HeapAlloc(),两者差别在哪里? STL 内存分配
[解决办法]
string的内存分配策略应该有优化:预先分配。你代码中第二中分配策略:按需分配。

[解决办法]
string书上明显说了,分配内存,是按一定比例分配。
[解决办法]
不要浪费精力在这种不精确的测试上。
换个测试场景,你的答案就可能翻转的。
[解决办法]
当然保证连续。
用你的办法,假设每次n MB原地Realloc到n+1 MB都失败,那么就要Alloc n+1 MB,然后n MB数据拷贝过去。从0开始分配到n MB的话总共需要拷贝n^2 MB数据。
但是如果用倍增法,策略是2^k MB的时候再要增加那就Realloc 2^(k+1) MB的话,你可以计算出来总共需要拷贝的数据不会超过2n MB。n^2和2n这性能比较当然是显然的。

动态内存分配的策略那是挺古老的算法了。早就有成熟的办法了。
[解决办法]
这种数组型容器增加空间的时候并不是需要多少申请多少,而是用原来的尺寸乘一个系数。你的方法需要申请256次内存,而如果增长系数是2的话,其实只需要申请9次。当然实际的系数可能不是2,但无论如何这个也是log(N)级的。
[解决办法]
引用:
Quote: 引用:

不要浪费精力在这种不精确的测试上。
换个测试场景,你的答案就可能翻转的。


呵呵,是工作需要,不是为了钻牛角尖:
需求是编写一个缓冲区类,不断追加数据至尾部,但预先不知道有多少数据,又希望是连续的单块内存。
std::string可以做到,按道理我们也应该能的。


这种场景不需要不断分配内存空间的,仅需一次即可,一次分配合适大小的环形缓冲区,涉及多线程就加把锁得了。
[解决办法]
这种问题自己用VirtualAlloc申请一块超大的地址空间,然后要用的时候再给内存,这不就可以解决了么,string分配内存,如果超过的话,还要再HeapAlloc一份,然后把原先数据复制过去,这效率可见一斑,自己申请一块虚拟内存的话,只要不分配物理内存是不占空间而,而且如果字符串一大,可以映射更多的物理内存,完全可以在后面追加,而不需要将数据拷贝到另一个大内存再在尾部加数据。


[解决办法]

引用:
Quote: 引用:

不要浪费精力在这种不精确的测试上。
换个测试场景,你的答案就可能翻转的。


呵呵,是工作需要,不是为了钻牛角尖:
需求是编写一个缓冲区类,不断追加数据至尾部,但预先不知道有多少数据,又希望是连续的单块内存。
std::string可以做到,按道理我们也应该能的。


可以用std::vector<char>作为缓冲区。

读书人网 >C++

热点推荐