为什么新的展示位置比直接作业更快?

bri*_*ore 21 c++ performance gcc x86-64 c++11

我最近发现使用placement new比执行16个赋值更快:
考虑以下代码段(c ++ 11):

class Matrix
{
public:
    double data[16];

    Matrix() : data{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }
    {
    };

    void Identity1()
    {
        new (this) Matrix();
    };

    void Identity2()
    {
        data[0]  = 1.0; data[1]  = 0.0; data[2]  = 0.0; data[3]  = 0.0;
        data[4]  = 0.0; data[5]  = 1.0; data[6]  = 0.0; data[7]  = 0.0;
        data[8]  = 0.0; data[9]  = 0.0; data[10] = 1.0; data[11] = 0.0;
        data[12] = 0.0; data[13] = 0.0; data[14] = 0.0; data[15] = 1.0;
    };
};
Run Code Online (Sandbox Code Playgroud)

用法:

Matrix m;
//modify m.data

m.Identity1(); //~25 times faster
m.Identity2();
Run Code Online (Sandbox Code Playgroud)

在我的机器Identity1()上比第二个功能快约25倍.现在我很好奇为什么会有这么大的差异?

我还尝试了第三个:

void Identity3()
{
    memset(data, 0, sizeof(double) * 16);
    data[0] = 1.0;
    data[5] = 1.0;
    data[10] = 1.0;
    data[15] = 1.0;
};
Run Code Online (Sandbox Code Playgroud)

但这甚至比Identity2()我想象的要慢.


分析信息

我做了几个分析测试,看它是否是一个与分析相关的问题,所以有默认的'for loop'测试,但也有外部分析测试:

分析方法1 :(众所周知的循环测试)

struct timespec ts1;
struct timespec ts2;

clock_gettime(CLOCK_MONOTONIC, &ts1);

for (volatile int i = 0; i < 10000000; i++)
    m.Identity(); //use 1 or 2 here

clock_gettime(CLOCK_MONOTONIC, &ts2);

int64_t start = (int64_t)ts1.tv_sec * 1000000000 + (int64_t)ts1.tv_nsec;
int64_t elapsed = ((int64_t)ts2.tv_sec * 1000000000 + (int64_t)ts2.tv_nsec) - start;

if (elapsed < 0)
    elapsed += (int64_t)0x100000 * 1000000000;

printf("elapsed nanos: %ld\n", elapsed);
Run Code Online (Sandbox Code Playgroud)

方法2:

$ valgrind --tool=callgrind ./testcase

$ # for better overview:
$ python2 gprof2dot.py -f callgrind.out.22028 -e 0.0 -n 0.0 | dot -Tpng -o tree.png
Run Code Online (Sandbox Code Playgroud)

大会信息

正如用户TC在评论中所述,这可能会有所帮助:

http://goo.gl/LC0RdG


编译和机器信息

编译: g++ --std=c++11 -O3 -g -pg -Wall

-pg不是问题.在不使用该标志的情况下,在测量方法1中获得相同的时间差.

Machine info (lscpu):

Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                8
On-line CPU(s) list:   0-7
Thread(s) per core:    2
Core(s) per socket:    4
Socket(s):             1
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 58
Model name:            Intel(R) Core(TM) i7-3612QM CPU @ 2.10GHz
Stepping:              9
CPU MHz:               2889.878
CPU max MHz:           3100.0000
CPU min MHz:           1200.0000
BogoMIPS:              4192.97
Virtualization:        VT-x
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              6144K
NUMA node0 CPU(s):     0-7
Run Code Online (Sandbox Code Playgroud)

Tho*_* B. 1

我敢打赌,如果您手动 memcopy 一个 const-expr 数组,您会获得相同的性能:

static constexpr double identity_data[16] = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 };

void Identity3()
{
    std::copy(std::begin(identity_data), std::end(identity_data), data);
}
Run Code Online (Sandbox Code Playgroud)