如何解决缓存命中和缓存未命中测量ARMV8
我一直在尝试测量缓存命中和缓存未命中。
我一直在研究1.5GHz的四核Cortex-A72(ARM v8)64位SoC。
我用来衡量缓存命中率的C代码是:
#define _GNU_SOURCE
#include <assert.h>
#include <sched.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/random.h>
#include <stdbool.h>
#include <fcntl.h>
#include <fcntl.h>
#include <sys/time.h>
char *chunk;
const size_t chunk_size = 1<<30;
/* FUNCTIONS */
struct timeval start_time,start_time1;
double get_diff(){
struct timeval end_time;
int rc = gettimeofday(&end_time,NULL);
assert(rc == 0);
return (end_time.tv_sec - start_time.tv_sec + (double) (end_time.tv_usec - start_time.tv_usec) / 1e6);
}
void print_affinity(){
cpu_set_t mask;
long nproc,i;
if (sched_getaffinity(0,sizeof(cpu_set_t),&mask) == -1){
perror("sched_getaffinity");
assert(false);
}
nproc = sysconf(_SC_NPROCESSORS_ONLN);
printf("sched_getaffinity = ");
for (i = 0; i < nproc; i++)
printf("%d ",cpu_ISSET(i,&mask));
}
void bind_to_cpu (){
cpu_set_t mask;
print_affinity();
printf("\n");
printf("sched_getcpu = %d\n",sched_getcpu());
cpu_ZERO(&mask);
cpu_SET(0,&mask);
if (sched_setaffinity(0,&mask) == -1) {
perror("sched_setaffinity");
assert(false);
}
print_affinity();
printf("\nsched_getcpu = %d\n",sched_getcpu());
}
void reset_mem(){
memset(chunk,-1,chunk_size);
}
void initialize(size_t chunk_size){
chunk = (char *) mmap(NULL,chunk_size,PROT_READ | MAP_POPULATE |PROT_WRITE,MAP_ANONYMOUS | MAP_PRIVATE,0);
assert(chunk!=MAP_Failed);
//initialize all bits to INIT_BIT value
printf("Initializing memory...\n\n");
reset_mem();
}
int main(int argc,char** argv){
bind_to_cpu(); // pinning/binding cpu
initialize(chunk_size);
uint64_t temp=0 ;
size_t offset1 = (rand() << 12) % chunk_size;
size_t offset2 = (rand() << 12) % chunk_size;
uint64_t *addr1 = (uint64_t*) (chunk+offset1);
uint64_t *addr2 = (uint64_t*) (chunk+offset2);
double time_result;
sched_yield();
__asm__ __volatile__("dc civac,%0\n\t" : : "r" (addr1) :"memory");
__asm__ __volatile__("dc civac,%0\n\t" : : "r" (addr2) :"memory");
for(int i =0; i<5000;i++){
gettimeofday(&start_time,NULL);
volatile uint64_t value;
asm volatile ("LDR %0,[%1]\n\t"
: "=r" (value)
: "r" (addr1)
);
asm volatile ("LDR %0,[%1]\n\t"
: "=r" (value)
: "r" (addr2)
);
time_result += get_diff();
//__asm__ __volatile__("dc civac,%0\n\t" : : "r" (addr1) :"memory");
//__asm__ __volatile__("dc civac,%0\n\t" : : "r" (addr2) :"memory");
}
sched_yield();
printf("Total Time: %f\n\n",time_result);
return 0;
}
用于测量缓存未命中的代码是相同的,但是使用两个带有注释的刷新指令:
__asm__ __volatile__("dc civac,%0\n\t" : : "r" (addr1) :"memory");
__asm__ __volatile__("dc civac,%0\n\t" : : "r" (addr2) :"memory");
因此,当我使用LDR指令时,一切似乎都正常,并且得到了以下输出:
Cache hit: Cache miss:
0.000522 0.001503
0.000558 0.001696
0.000584 0.001977
0.000712 0.002032
0.000683 0.001137
当我使用STR指令时:
for(int i =0; i<5000;i++){
gettimeofday(&start_time,NULL);
asm volatile("str %x1,%x0" : "=m"(*addr1) : "r"(temp));
asm volatile("str %x1,%x0" : "=m"(*addr2) : "r"(temp));
time_result += get_diff();
__asm__ __volatile__("dc civac,%0\n\t" : : "r" (addr2) :"memory");
}
我得到了这些输出:
Cache hit: Cache miss:
0.000603 0.000299
0.000287 0.000311
0.000376 0.000290
0.000311 0.000305
0.000518 0.000297
缓存命中和缓存未命中之间的区别非常细微。
为什么呢我不是以正确的方式刷新缓存吗?
解决方法
高性能CPU的存储缓冲区使存储指令的执行从提交到高速缓存脱钩,从而使实际存储指令本身能够快速(推测地)执行,而与高速缓存的命中或未命中无关。 (请参见this,this,this,也Does processor stall during cache coherence operation)
在第二个版本中,您只是在定时 个存储,而不是dc civac
instructions,这些存储必须做确保将脏数据写回到RAM的实际工作。 str
指令本身仅需要将存储数据和地址写入存储缓冲区,而不必等待其提交到L1d缓存。
但是在您的LDR版本中,您正在计时实际的缓存丢失加载,直到它们实际从缓存中读取数据后,该加载才能完成。
如果您计时整个循环(包括dc civac
指令),您可能会看到一些有意义的东西。与其注释掉dc civac
指令,不如给它们地址不同的缓存行,而不是您要加载或存储到的缓存行。
在允许乱序执行的内核上尤其如此。当几个指令可以用读取时钟的指令重新排序时,以及当正常情况是许多指令处于“运行中”状态时,定时几个指令的意义并不大。排空障碍的执行障碍,以便您可以计时某些东西,这会创造出相当人为的条件,而计时开销意味着您永远无法以这种方式计时某物的实际成本。
gettimeofday
与高速缓存未命中相比总开销很高;最好构建一个可以运行多个周期且时间不限的测试。
也不要使用全局变量;没有理由不只是将arg传递给get_diff
。
此外,您的负载测试出于明显的原因使用了volatile uint64_t value;
作为asm输出,从而迫使编译器针对每次负载向堆栈发出2个存储。 asm volatile
语句确保装入。除非CPU积极优化以放弃未使用的加载结果(如果以后的指令覆盖寄存器,则不要等待它们),您就可以让这些加载不使用。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。