我的这些课程是通过B站视频学习《深入立即计算机系统》书籍学习所做的一些笔记。
分享出来希望和大家共同学习。
学习的资料:
【精校中英字幕】2015 CMU 15-213 CSAPP 深入理解计算机系统 课程视频
CS:APP主页
csapp 代码地址
放弃复制黏贴,放弃搜索。
Computer Systems: A Programmer’s Perspective(CS:APP3e)
地址:http://csapp.cs.cmu.edu
The C Programming Language
作者:Brian Kernighan and Dennis Ritchie
C语言学习书籍。
Lecture
Recitations 背诵
所以不要忽略记忆。死记硬背也是基础。概念都不知道何来后面的。
Labs 实验
课程的灵魂,编程只有不断的实践,遇到问题,解决问题,才会有所成长。
背诵只是让你知道,实践才可以让你能够真正处理问题。
Exams 考试
网址:http://www.cs.cmu.edu/~123
使用 Autolab 来做作业。
使用机器:
ssh shark.ics.cs.cmu.edu
考试和实验室分数各分50%,期中考试20%,期末考试30%
作业:
作业:
作业:
L5(tshlab): Writing your own Unix shell
写一个自己的shell工具。
开始了解并发。
作业:
L6(malloclab): Writing your own malloc package
写一个自己的内存(malloc)包
获得对系统级编程的真实感受。
作业:
L7(proxylab): Writing your own Web proxy
写一个自己的Web代理
学习网络编程和更多关于并发和同步。
Ints are not Integers, Floats are not Reals
40000 * 40000 ➙ 1600000000
50000 * 50000 ➙ ??
Unsigned & Signed Int’s: Yes!
Float’s:
(1e20 + -1e20) + 3.14 --> 3.14
1e20 + (-1e20 + 3.14) --> ??
简单的理解:就是浮点运算时会进行舍入, (-1e20 + 3.14)
导致3.14丢失。
#include
void reality(int a) {
printf("%d*%d=%d\n",a, a, a*a);
}
int main() {
reality(40000);
reality(50000);
}
// output:
// 40000*40000= 1600000000
// 50000*50000=-1794967296
2^31 = 2147483648
50000*50000 = 2500000000
超出int32的边界了。
#include
void floatAdd(float a) {
printf("(%f-%f)+3.14=%f\n",a,a, (a-a)+3.14);
printf("%f+(-%f+3.14)=%f\n",a,a, a + (-a+3.14));
}
int main() {
floatAdd(1e20);
}
// output:
// (100000002004087730000.000000-100000002004087730000.000000)+3.14=3.140000
// 100000002004087730000.000000+(-100000002004087730000.000000+3.14)=0.000000
typedef struct {
int a[2];
double d;
}struct_t;
double memoryRefBug(int i) {
volatile struct_t s;
s.d = 3.14;
s.a[i] = 1073741824;
printf("%d -> %lf\n",i,s.d);
return s.d;
}
int main() {
memoryRefBug(0);
memoryRefBug(1);
memoryRefBug(2);
memoryRefBug(3);
memoryRefBug(4);
memoryRefBug(5);
memoryRefBug(6);
memoryRefBug(7);
}
// output:
// 0 -> 3.140000
// 1 -> 3.140000
// 2 -> 3.140000
// 3 -> 2.000001
// 4 -> 3.140000
// 5 -> 3.140000
// 6 -> 3.140000
结构体和数组类似,元素的存储是紧挨着的。a[1] | a[2] | d
通过a[i]取,a[2]开始,就是引用的d中的值了进行操作了。
#include
#include
void copyij(int src[2048][2048], int dst[2048][2048]) {
int i,j;
for (i = 0;i < 2048;i++)
for (j = 0;j < 2048;j++)
dst[i][j] = src[i][j];
}
void copyji(int src[2048][2048], int dst[2048][2048]) {
int i,j;
for (j = 0;j < 2048;j++)
for (i = 0;i < 2048;i++)
dst[i][j] = src[i][j];
}
int main() {
int src[2048][2048],dst[2048][2048];
int i,j;
for (j = 0;j < 2048;j++)
for (i = 0;i < 2048;i++)
src[i][j] = i;
int start,end1,end2;
start = GetTickCount();
copyij(src,dst);
end1 = GetTickCount();
copyji(src,dst);
end2 = GetTickCount();
printf("copyij: %d, copyji:%d", end1 - start, end2 - end1);
}
// output:
// copyij: 0, copyji:0
结果分析:
教材显示copyij() 的性能远远好于 copyji()。
为什么我运行的没有这个问题。难道是这个已经修复了?
问题:
在windows下报错:“Process finished with exit code -1073741571 (0xC00000FD)”
致的原因是StackOverflow(栈区溢出)
将数组设置为500长度就没事了。
linux 代码:
#include
#include
#include
int main() {
int src[2048][2048],dst[2048][2048];
int i,j;
for (j = 0;j < 2048;j++)
for (i = 0;i < 2048;i++)
src[i][j] = i;
clock_t start,end1,end2;
start = clock();
copyij(src,dst);
end1 = clock();
copyji(src,dst);
end2 = clock();
printf("copyij: %d, copyji:%d", ((double )(end1 - start))/CLOCKS_PER_SEC, ((double )(end2 - end1))/CLOCKS_PER_SEC);
}
报错:”Segmentation fault“
分析:
Segmentation fault就是指访问的内存超出了系统所给这个程序的内存空间。一般是随意使用野指针或者数组、数组越界。
处理方案:
将数组设置为500长度。
结果:
copyij: 0, copyji:3668
c语言遍历
对c语言而言,数组在内存中是按行储存的,按行遍历时可以由指向数组第一个数的指针一直往下走,就可以遍历完整个数组,而按列遍历则要获得指向每一列的第一行的元素的指针,然后每次将指针指下一行,
CPU高速缓存
缓存从内存中抓取一般都是整个数据块,所以它的物理内存是连续的,几乎都是同行不同列的,而如果内循环以列的方式进行遍历的话,将会使整个缓存块无法被利用,而不得不从内存中读取数据,而从内存读取速度是远远小于从缓存中读取数据的。
分页调度
物理内存是以页的方式进行划分的,当一个二维数组很大是如 int[128][1024],假设一页的内存为4096个字节,而每一行正好占据内存的一页,如果以列的形式进行遍历,就会发生128*1024次的页面调度,而如果以行遍历则只有128次页面调度,而页面调度是有时间消耗的,因而调度次数越多,遍历的时间就越长。
局部性原理: CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中。
三种不同类型的局部性:时间局部性,空间局部性,顺序局部性。
二维数组按行和按列遍历效率哪个高?