- 主题:std::vector中初始化与否导致性能差异巨大
最近碰到一个奇怪的问题, 一个简单的R3对象, 存放在std::vector中.
费解的一幕出现了, 如果vv定义的时候, 为每个元素指定初值, 那么后面对vv进行写操作就飞快.
而如果vv定义的时候, 不指定初值, 那么后面对vv写操作的时候, 就花费大约前一种3倍的时间.
大家知道这是什么原因引起的?
ps1. 后来我不用vector, 直接用new : R3* vv = new R3[POPULATION], 发现也有类似的情况. 如果new 了之后, 直接使用这个数组, 第一次访问的时候肯定慢, 第二次访问就快了. 太神奇了.
ps2. 我曾经怀疑, 是否是第一次使用这片内存后, 这片内存以某种机制被缓存了, 于是就在第一次访问和第二次访问之间, 插入了许多读写其他内存的代码, 可是发现第二次访问, 仍然非常块, 还是很神奇.
下面是代码
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <vector>
namespace {
struct R3
{
R3() {}
R3(double x, double y, double z)
{
m_data[0] = x; m_data[1] = y; m_data[2] = z;
}
double m_data[3];
};
static int const POPULATION = 10000000;
inline void time_report (const char* title, clock_t& latest_value)
{
clock_t now = clock();
printf("time elapsed for %s : %.8f(s)\n",
title, (double(now) - double(latest_value)) /CLOCKS_PER_SEC );
latest_value = now;
};
static const bool FILL_WITH_COMP = true;
}
void test_R3_performance()
{
clock_t latest_value = clock();
//std::vector<R3> vv(POPULATION); // <--- uninitialized vector
std::vector<R3> vv(POPULATION , R3{ 0, 0, 0 });
time_report("allocation finished", latest_value);
for (int i = 0; i<POPULATION; ++i)
{
vv[i] = {(double)i*2, (double)i + 4, (double)i + 6};
}
time_report("assignment finished", latest_value);
}
int main()
{
test_R3_performance();
return 0;
}
--
FROM 120.244.224.*
gcc执行结果(带初值):
time elapsed for allocation finished : 0.04800000(s)
time elapsed for assignment finished : 0.01400000(s)
如果不带初值定义vv, 结果如下
time elapsed for allocation finished : 0.00000000(s)
time elapsed for assignment finished : 0.05200000(s)
--
FROM 120.244.224.*
我在linux下也测出了同样的现象。
我搞成两个独立程序试一试。
【 在 mvtec (mvtec) 的大作中提到: 】
: 你应该把测试写成两个独立的程序做测试
: 否则你之前的测试用的内存页
: 还在操作系统里的页缓存里
: ...................
--
FROM 120.244.224.*
还是这个效果, 现象太稳定了。
???@localhost:~/ttest> ./strange_init && ./strange_uninit
time elapsed for allocation finished : 0.01931500(s) <---初始化
time elapsed for assignment finished : 0.01334200(s)
time elapsed for allocation finished : 0.00000200(s) <---未初始化
time elapsed for assignment finished : 0.01982100(s)
【 在 mvtec 的大作中提到: 】
: 你应该把测试写成两个独立的程序做测试
: 否则你之前的测试用的内存页
: 还在操作系统里的页缓存里
--
修改:xieyf FROM 120.244.224.*
FROM 120.244.224.*
我现在怀疑new出来的内存是没有commit的。
访问一遍后, vv中所有内容都是commited, 所以访问起来就快了。
这只是一种猜测.
【 在 ziqin (子青|会挽雕弓如满月|西北望|射天狼) 的大作中提到: 】
: 你看下编译出来的机器码,我猜是初始化的时候因为assigment,所以m_data[3]全部进入缓存了,类似pre-fetch,后面访问起来就快。如果用default ctor初始化(并不是没有初始化,是用default ctor初始化),default ctor里并没有访问m_data[3],所以没有进入缓存
--
FROM 120.244.224.*
改动了一下测试函数, 搞成初始化一半的空间.
居然性能真的介于 初始化和不初始化之间. 我严重怀疑这是 uncommited memory搞得鬼。
void test_R3_performance()
{
clock_t latest_value = clock();
std::vector<R3> vv(POPULATION); // <--- uninitialized vector
//std::vector<R3> vv(POPULATION , R3{ 0, 0, 0 });
for (int i = 0; i<POPULATION/2; ++i) // <-------- key code!
vv[i] = {0, 0, 0};
time_report("allocation finished", latest_value);
for (int i = 0; i<POPULATION; ++i)
{
vv[i] = {(double)i*2, (double)i + 4, (double)i + 6};
}
time_report("assignment finished", latest_value);
}
【 在 ziqin 的大作中提到: 】
: 你看下编译出来的机器码,我猜是初始化的时候因为assigment,所以m_data[3]全部进入缓存了,类似pre-fetch,后面访问起来就快。如果用default ctor初始化(并不是没有初始化,是用default ctor初始化),default ctor里并没有访问m_data[3],所以没有进入缓存
--
FROM 120.244.224.*
我32GB的物理内存, 也会有page fault问题吗?
win10 确实有page file, 但是linux下我是禁用了swap分区的, 这个按说不会有page fault了吧.
【 在 javaboy 的大作中提到: 】
: 应该是触发了page fault
:
--
FROM 120.244.224.*
intel i7-9700k, 32GB内存.
win10 ltsc 2019
【 在 mvtec 的大作中提到: 】
: 你把你机器的配置贴一下
--
FROM 120.244.224.*
嗯, 我搜了一下, c++ 有个lazy allocation机制, 意思就是malloc的时候, 分配的虚拟内存, 只有第一次使用的时候, 才提交成物理内存.
应该是这个问题没跑了
直接换成内存池jemalloc, 性能涨了许多, 不过这个现象仍然在.
都不初始化vector,
========= debug 模式(千万单元)=========
test with malloc, vector un-initialized
time elapsed for allocation finished : 0.77000000(s)
time elapsed for assignment finished : 1.45100000(s)
test with je-malloc, dyna-array un-initialized
time elapsed for allocation finished : 0.02800000(s)
time elapsed for assignment finished : 0.20400000(s)
==============release 模式==============
test with malloc, vector un-initialized
time elapsed for allocation finished : 0.00000000(s)
time elapsed for assignment finished : 0.05400000(s)
test with je-malloc, dyna-array un-initialized
time elapsed for allocation finished : 0.00000000(s)
time elapsed for assignment finished : 0.03500000(s)
【 在 jjfz 的大作中提到: 】
: 感觉是内存分配器虚实转换时机不同造成的
: vector申请了一大块内存,内存分配器只是给出了逻辑地址
: 当访问具体数据时,OS才会映射到物理地址
: ...................
--
FROM 120.244.224.*