Performance 在x86-64上测量memcpy的性能

Performance 在x86-64上测量memcpy的性能,performance,memcpy,tsc,Performance,Memcpy,Tsc,我有3个内存块 char block_a[1600]; // Initialized with random chars unsigned short block_b[1600]; // Initialized with random shorts 0 - 1599 with no duplication char block_c[1600]; // Initialized with 0 我正在对此文件执行以下复制操作 for ( int i = 0; i < 1600; i++ ) {

我有3个内存块

char block_a[1600]; // Initialized with random chars
unsigned short block_b[1600]; // Initialized with random shorts 0 - 1599 with no duplication
char block_c[1600]; // Initialized with 0
我正在对此文件执行以下复制操作

for ( int i = 0; i < 1600; i++ ) {
    memcpy(block_c[i], block_a[block_b[i]], sizeof(block_a[0]); // Point # 1
}
如果我再次加载模块,请给出结果

Cycles Time(NS)
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0006 0005
0006 0005
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0011 0009
0011 0009
0011 0009
0011 0009
0011 0009
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0000 0000
0017 0014
0011 0009
0011 0009
0000 0000
0000 0000
0000 0000
0011 0009
0000 0000
0000 0000
0011 0009
0011 0009
0011 0009
0000 0000
0022 0018
0006 0005
0011 0009
0006 0005
0006 0005
0104 0086
0104 0086
0011 0009
0011 0009
0011 0009
0006 0005
0006 0005
0017 0014
0017 0014
0022 0018
0022 0018
0022 0018
0017 0014
0011 0009
0022 0018
0011 0009
0006 0005
0011 0009
0006 0005
0006 0005
0006 0005
0011 0009
0011 0009
0011 0009
0011 0009
0011 0009
0006 0005
0006 0005
0011 0009
0006 0005
0022 0018
0011 0009
0028 0023
0006 0005
0006 0005
0022 0018
0006 0005
0022 0018
0006 0005
0011 0009
0006 0005
0011 0009
0006 0005
0000 0000
0006 0005
0017 0014
0011 0009
0022 0018
0000 0000
0011 0009
0006 0005
0011 0009
0022 0018
0006 0005
0022 0018
0011 0009
0022 0018
0022 0018
0011 0009
0006 0005
0011 0009
0011 0009
0006 0005
0011 0009
0126 0105
0006 0005
0022 0018
0000 0000
0022 0018
0006 0005
0017 0014
0011 0009
0022 0018
0011 0009
0006 0005
0006 0005
0011 0009
在上面的列表中,您会注意到有许多复制操作,我有0个CPU周期。很多时候我看到<3个周期

您认为memcpy操作的CPU周期为0或很少的原因是什么?知道memcpy通常占用多少CPU周期吗

更新 我尝试了以下更改并获得了给定的结果
1) 如果在重新启动后使用memcpy复制单个字节,则循环时间为0-8
2) 如果在重新启动后使用memcpy复制整个块,则循环时间为0
3) BIOS更改为单核(虽然此代码已仅在单核上运行,但只是为了确保),对结果没有影响

4) BIOS对禁用“英特尔SpeedStep”的更改无效,但一旦解决此问题,为获得最大可能的CPU周期,应禁用“英特尔SpeedStep”,使CPU以最大频率工作

看起来缓存是不正确的CPU周期的原因(实际上这不是不正确的CPU周期,但在这种情况下还应考虑缓存性能度量,以获得准确的结果)。在确保给定数据的缓存已清除后,我的结果看起来很好。我添加了以下函数来清除缓存。clflush函数在内核API中可用,它利用x86 clflush指令

static void flush_cache(char random_byte_array[], char shuffled_byte_array[])
{
    unsigned int idx = 0;
    for ( idx = 0; idx < (MEM_BLOCK_SIZE/64); idx++ ) {
        clflush(random_byte_array+(idx*64));
    }
    for ( idx = 0; idx < (MEM_BLOCK_SIZE/64); idx++ ) {
        clflush(shuffled_byte_array+(idx*64));
    }
}
对于第一个元素(第0个索引)的memcpy,它需要大约140-160个周期,对于一些元素,它需要0-10个周期(这是因为我猜数据加载到缓存中),在一些元素之后,它需要大约140-160个元素(可能缓存未命中)


只要数据不在缓存中,我就可以获得良好的CPU周期,但只要数据在缓存中,周期就不足以测量,可能还应该考虑缓存性能测量

看起来缓存是不正确的CPU周期的原因(实际上这不是不正确的CPU周期,但在这种情况下还应考虑缓存性能度量,以获得准确的结果)。在确保给定数据的缓存已清除后,我的结果看起来很好。我添加了以下函数来清除缓存。clflush函数在内核API中可用,它利用x86 clflush指令

static void flush_cache(char random_byte_array[], char shuffled_byte_array[])
{
    unsigned int idx = 0;
    for ( idx = 0; idx < (MEM_BLOCK_SIZE/64); idx++ ) {
        clflush(random_byte_array+(idx*64));
    }
    for ( idx = 0; idx < (MEM_BLOCK_SIZE/64); idx++ ) {
        clflush(shuffled_byte_array+(idx*64));
    }
}
对于第一个元素(第0个索引)的memcpy,它需要大约140-160个周期,对于一些元素,它需要0-10个周期(这是因为我猜数据加载到缓存中),在一些元素之后,它需要大约140-160个元素(可能缓存未命中)


只要数据不在缓存中,我就可以获得良好的CPU周期,但只要数据在缓存中,周期就不足以测量,可能还应该考虑缓存性能测量

您是否尝试过从编译器生成程序集输出,并查看
memcpy
是否已实际优化?如果您实际上没有将复制的内存用于某些事情,那么积极的优化可能会完全删除这些调用。第二次运行也要考虑到你的内存可能已经进入缓存。谢谢帕迪。我还没有检查memcpy的汇编输出,但我会检查一下。到目前为止,您提到的第二点似乎是一个可能的原因。如果您想测试/验证第二点,您可以尝试刷新缓存。所有架构都没有具体的内容,但您肯定可以为您的系统做些什么。感谢paddy,我使用CLFLUSH指令清除缓存,结果看起来很有希望,但是度量方法也应该考虑缓存性能度量。您是否尝试过从编译器生成程序集输出,并查看
memcpy
是否已实际优化?如果您实际上没有将复制的内存用于某些事情,那么积极的优化可能会完全删除这些调用。第二次运行也要考虑到你的内存可能已经进入缓存。谢谢帕迪。我还没有检查memcpy的汇编输出,但我会检查一下。到目前为止,您提到的第二点似乎是一个可能的原因。如果您想测试/验证第二点,您可以尝试刷新缓存。所有体系结构都没有具体的内容,但您肯定可以为您的系统设计一些东西。感谢paddy,我使用CLFLUSH指令来清除缓存,结果看起来很有希望,但测量方法也应该考虑缓存性能测量。
static void flush_cache(char random_byte_array[], char shuffled_byte_array[])
{
    unsigned int idx = 0;
    for ( idx = 0; idx < (MEM_BLOCK_SIZE/64); idx++ ) {
        clflush(random_byte_array+(idx*64));
    }
    for ( idx = 0; idx < (MEM_BLOCK_SIZE/64); idx++ ) {
        clflush(shuffled_byte_array+(idx*64));
    }
}
Cycles Time (ns)
0159 0132
0000 0000
0000 0000
....
....
0049 0040
0049 0040
0049 0040
0000 0000
0000 0000
....
....