我一直试图通过计算一个使用以下代码对数组元素进行扩展和求和的例程来了解在L1缓存与内存中使用数组的影响(我知道我应该将结果缩放为' a'在最后;关键是在循环中同时进行乘法和加法 - 到目前为止,编译器还没有想出要将'a'分解出来):
double sum(double a,double* X,int size)
{
double total = 0.0;
for(int i = 0; i < size; ++i)
{
total += a*X[i];
}
return total;
}
#define KB 1024
int main()
{
//Approximately half the L1 cache size of my machine
int operand_size = (32*KB)/(sizeof(double)*2);
printf("Operand size: %d\n", operand_size);
double* X = new double[operand_size];
fill(X,operand_size);
double seconds = timer();
double result;
int n_iterations = 100000;
for(int i = 0; i < n_iterations; ++i)
{
result = …Run Code Online (Sandbox Code Playgroud) 所以我试图用C测量L1,L2,L3缓存的延迟.我知道它们的大小,我觉得我从概念上理解如何做到这一点,但我遇到了我的实现问题.我想知道是否有一些其他硬件错综复杂如预取问题.
#include <time.h>
#include <stdio.h>
#include <string.h>
int main(){
srand(time(NULL)); // Seed ONCE
const int L1_CACHE_SIZE = 32768/sizeof(int);
const int L2_CACHE_SIZE = 262144/sizeof(int);
const int L3_CACHE_SIZE = 6587392/sizeof(int);
const int NUM_ACCESSES = 1000000;
const int SECONDS_PER_NS = 1000000000;
int arrayAccess[L1_CACHE_SIZE];
int arrayInvalidateL1[L1_CACHE_SIZE];
int arrayInvalidateL2[L2_CACHE_SIZE];
int arrayInvalidateL3[L3_CACHE_SIZE];
int count=0;
int index=0;
int i=0;
struct timespec startAccess, endAccess;
double mainMemAccess, L1Access, L2Access, L3Access;
int readValue=0;
memset(arrayAccess, 0, L1_CACHE_SIZE*sizeof(int));
memset(arrayInvalidateL1, 0, L1_CACHE_SIZE*sizeof(int));
memset(arrayInvalidateL2, 0, L2_CACHE_SIZE*sizeof(int));
memset(arrayInvalidateL3, 0, L3_CACHE_SIZE*sizeof(int));
index = 0;
clock_gettime(CLOCK_REALTIME, &startAccess); …Run Code Online (Sandbox Code Playgroud) 我在x86-64中查看下面的虚方法调用:
mov rcx, qword ptr [x]
mov rax, qword ptr [rcx]
call qword ptr [rax+8]
Run Code Online (Sandbox Code Playgroud)
以及Agner Fog的延迟表:
http://www.agner.org/optimize/instruction_tables.pdf
当我使用Ivy Bridge CPU时,我正在查看第175页.
我是否正确,前两个MOV指令都只占用2个(它们都是移动存储器来注册)CPU周期?我以为对虚拟方法的调用比这慢?
在178页的指令延迟表中,它表示此调用的延迟是2个CPU周期(我认为?).CALL与CALL'r'(寄存器)和CALL'm'(存储器)相比,'near'意味着什么?
所以根据Fog小册子,上面的ASM确实需要6个CPU周期,我没有误解过任何东西?
编辑:我将虚函数调用更改为vtable中的第二个.
我读了一篇文章(1.5岁http://www.drdobbs.com/parallel/cache-friendly-code-solving-manycores-ne/240012736),其中讨论了缓存性能和数据大小.他们展示了以下代码,他们说这些代码是在i7(沙桥)上运行的
static volatile int array[Size];
static void test_function(void)
{
for (int i = 0; i < Iterations; i++)
for (int x = 0; x < Size; x++)
array[x]++;
}
Run Code Online (Sandbox Code Playgroud)
他们声称如果他们保持Size*Iterations不变,增加Size,当数组内存中的大小增加超过L2缓存大小时,他们会观察到执行时间(10x)的巨大峰值.
作为我自己的练习,我想尝试一下,看看我是否可以为我的机器重现他们的结果.(i7 3770k,win7,visual c ++ 2012编译器,Win32调试模式,未启用优化).令我惊讶的是,我无法看到执行所花费的时间增加(甚至超过L3缓存大小),这让我觉得编译器在某种程度上优化了这段代码.但我也没有看到任何优化.我看到的唯一的速度变化是,在我的机器的字大小以下,它需要稍长.以下是我的时间,代码清单和相关的反汇编.
有谁知道原因:
1)为什么不管阵列的大小如何,所用的时间都不会增加?或者我怎么能找到?
2)为什么所花费的时间从高处开始然后减小直到达到缓存行大小,如果数据小于行大小,是否应该在没有从缓存读取的情况下处理更多迭代?
时序:
Size=1,Iterations=1073741824, Time=3829
Size=2,Iterations=536870912, Time=2625
Size=4,Iterations=268435456, Time=2563
Size=16,Iterations=67108864, Time=2906
Size=32,Iterations=33554432, Time=3469
Size=64,Iterations=16777216, Time=3250
Size=256,Iterations=4194304, Time=3140
Size=1024,Iterations=1048576, Time=3110
Size=2048,Iterations=524288, Time=3187
Size=4096,Iterations=262144, Time=3078
Size=8192,Iterations=131072, Time=3125
Size=16384,Iterations=65536, Time=3109
Size=32768,Iterations=32768, Time=3078
Size=65536,Iterations=16384, Time=3078
Size=262144,Iterations=4096, Time=3172
Size=524288,Iterations=2048, Time=3109
Size=1048576,Iterations=1024, Time=3094
Size=2097152,Iterations=512, Time=3313
Size=4194304,Iterations=256, Time=3391
Size=8388608,Iterations=128, …Run Code Online (Sandbox Code Playgroud) performance ×3
assembly ×2
c ×2
c++ ×2
caching ×2
arrays ×1
cpu ×1
memory ×1
optimization ×1
x86 ×1