Lecture 01 Course Overview 课程简介
文章目录
- Lecture 01 Course Overview 课程简介
- 概述
- 不要抄袭
- 书籍推荐
- 课程内容
- 帮助
- 实验
- 课堂纪律
- 考试分数
- 课程
- 主题一:程序和数据
- 主题二:内存层次结构
- 主题三:例外的控制流程
- 主题四:虚拟内存
- 主题五:网络和并发
- 课程实例
- Great Reality #1
- Example 1: Is x2 ≥ 0?
- Example 2: Is (x + y) + z = x + (y + z)?
- 代码
- Great Reality #1
- 二维数组行遍历效率远高于列遍历效率
- 文档
概述
我的这些课程是通过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
- Highter level concepts 高层级概念
-
Recitations 背诵
所以不要忽略记忆。死记硬背也是基础。概念都不知道何来后面的。 -
Labs 实验
课程的灵魂,编程只有不断的实践,遇到问题,解决问题,才会有所成长。
背诵只是让你知道,实践才可以让你能够真正处理问题。 -
Exams 考试
帮助
网址:http://www.cs.cmu.edu/~123
实验
使用 Autolab 来做作业。
使用机器:
ssh shark.ics.cs.cmu.edu
课堂纪律
- 可以带电脑,但是不要用于通信等其他与课程无光用途。
认真听讲 - 不考勤,自愿来不来
考试分数
考试和实验室分数各分50%,期中考试20%,期末考试30%
课程
主题一:程序和数据
- 位操作,运算,汇编语言程序
- C 语言控制和数据结构描述
- 体系解雇和编译器
作业:
- L1(datalab): Manipulating bits(操作位)
- L2(bomblab): Defusing a binary bomb (拆除二进制炸弹)
- L3(attacklab): The basics of code injection attacks(代码注入)
主题二:内存层次结构
- 内存技术,内存层次,缓存,磁盘,局部性
- 体系结构和操作系统
作业:
- L4(cachelab): Building a cache simulator and optmizing for locality.
构建缓存模拟器并优化局部性。
学习如何在程序中利用局部性。
主题三:例外的控制流程
- 硬件,进程,进程控制,unix 信号
- 编译器,操作系统,体系结构。
作业:
L5(tshlab): Writing your own Unix shell
写一个自己的shell工具。
开始了解并发。
主题四:虚拟内存
- 虚拟内存,地址转换,动态存储分配
- 体系结构,操作系统
作业:
L6(malloclab): Writing your own malloc package
写一个自己的内存(malloc)包
获得对系统级编程的真实感受。
主题五:网络和并发
- 高级/低级 I/O, 网络编程
- 服务,web服务器
- 并发,并发服务器设计,线程
- I/O 多路复用与选择
- 网络,操作系统,体系结构
作业:
L7(proxylab): Writing your own Web proxy
写一个自己的Web代理
学习网络编程和更多关于并发和同步。
课程实例
Great Reality #1
Example 1: Is x2 ≥ 0?
Ints are not Integers, Floats are not Reals
- Example 1: Is x^2 >= 0 ?
float:是的
Int:
40000 * 40000 ➙ 1600000000
50000 * 50000 ➙ ??
Example 2: Is (x + y) + z = x + (y + z)?
Unsigned & Signed Int’s: Yes!
Float’s:
(1e20 + -1e20) + 3.14 --> 3.14
1e20 + (-1e20 + 3.14) --> ??
简单的理解:就是浮点运算时会进行舍入, (-1e20 + 3.14)
导致3.14丢失。
代码
Great Reality #1
- Example 1: Is x2 ≥ 0?
#include <stdio.h>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的边界了。
- Example 2: Is (x + y) + z = x + (y + z)?
#include <stdio.h>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
- Memory Referencing Bug
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中的值了进行操作了。
- Memory System Performance Example
#include <stdio.h>
#include <windows.h>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 <stdio.h>
#include <math.h>
#include <time.h>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访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中。
三种不同类型的局部性:时间局部性,空间局部性,顺序局部性。
文档
二维数组按行和按列遍历效率哪个高?