链表的缺点以及程序关于cache的优化

小的程序使用链表就是智障行为

受到辣鸡计算机导论课程影响,链表受到了计算机学生的广泛欢迎。然而链表看似在某些地方很方便,能随时插入和删除,还一堆指针指过来指过去,写起来还装逼,多捞哦!
然而由于链表的节点之间关系不够紧密,节点的内存位置不确定定,cache miss就基本是必然了。然后cpu就在等着内存慢慢的找,吔屎啦

如果是c++就用vector谢谢。
对于c就老老实实的实现一个变长数组多好。

你说链表插入数据多方便,变长数组还要移位。
然而事实是你链表想要找到你要的元素要一个个摸过去。而且这期间cache基本帮不上忙,内存在后面死死的拖着后腿。实际效率还不如变长数组。

既然说到cache了多提几点。

cache就是根据你的指令猜测接下来要用的东西,然后把内存里的东西prefetch到cpu cache里,下次用的时候直接从缓存里拿,多快!

cpu三级缓存,就我的笔记本标压u来说
第一级 tb级IO速度,100kb左右
第二级 速度忘了,反正在第一级和第三级之间(逃 ,1mb左右
第三级 100gb左右的IO速度,8mb左右

然后这是内存与三级缓存之间时钟时钟周期的对比:
内存与三级缓存速度对比
看着红色的数据条,再看看以上这些证据,用脚趾头都能想到,如果cache miss,损失在从内存取东西到cpu上的时间有多少(这里不考虑cpu流水线),然后分分钟把你在那些指令的优化的小trick损失的时间全部丢掉。

c写程序的时候经常会有类似于这种设计:

typedef struct string{
    int num;
    char*s;
}string

然后初始化的时候分配内存:

int length=5;
string *mystring;
mystring->s=(char*)malloc(5*sizeof(char));

然后经常num和s一块用,这也经常导致cache miss。。。。
从大佬那里弄到了一招,这么写:

typedef string{
    int num;
    char s[1];
}string;

然后初始化的时候这样:

int length=5;
string *mystring=(string*)malloc(sizeof(int)+sizeof(char)*length);

于是乎num的地址和s的地址相连,大幅提高效率,成功防止cache miss

最后重提一下,那intel knight landing做例子(来源:Intel64 and IA-32 Architectures
Optimization Reference Manual),给一个触目惊心的数据:
Integer Pipeline Characteristics of the Knights Landing Microarchitecture

Integer Instruction/operations Latency (cycle) Throughput ( cycles per instruction)
Simple Integer 1 0.5
Integer Multiply 3 or 5 1
IntegerDivide Varies >20
Integer Loads 4 1

所以就算是神仙,能像卡马克一样把除法开根号优化成加减移位那么牛,节省的时间经常还不够在等内存上浪费的时间的零头。

所以我们为什么要暴殄天物?

PS:纯原创,禁止没有本人同意就转载,谢谢

猜你喜欢

转载自blog.csdn.net/qq_21201963/article/details/80296446