软件系统优化实验A1笔记
实验系统
- 虚拟机环境:VMware® Workstation 17 Player
- 操作系统:Ubuntu 22.04 server
- 虚拟硬件:4核心 4G内存
- 软件版本:
- gcc:11.4.0
- clang:14.0.0
- python:3.10.12
- java:openjdk 21.0.3
- Linux perf:5.15.152
系统安装与准备
问题与解决方法
- 遇到报错:hub_ext_port_status
- 解决方法:在菜单 > 管理 > 虚拟机设置 > USB 控制器 > 与虚拟机共享蓝牙设备一栏将勾选去掉即可。
- 虚拟机的输入有较大延迟,并且字体过小
- 解决方法:使用Xshell连接SSH进行操作
常用工具命令操作联系
1 | uname -a |
内核名称 | 主机名称 | 内核发行号 | 内核版本 | 主机的硬件架构 | 处理器类型 | 硬件平台 | 操作系统名称 |
---|---|---|---|---|---|---|---|
Linux | ubuntu | 5.15.0-112-generic | #122-Ubuntu SMP Thu May 23 07:48:21 UTC 2024 | x86_64 | x86_64 | x86_64 | GNU/Linux |
1 | sysctl -a |
- 功能:列出系统内核的所有参数
- 与/proc/sys的关系:该文件夹下有多个子文件夹,每个子文件夹下以文件的形式存放有若干参数。该命令遍历/proc/sys文件夹,将其中的信息以“子文件夹.子项目名称”的形式逐条输出。
1 | top |
- 该命令进入交互式界面,按q退出。
- 该命令用于实时显示进程的动态。
PID | USER | PR | NI | VIRT | RES | SHR | S | %CPU | %MEM | TIME+ | COMMAND |
---|---|---|---|---|---|---|---|---|---|---|---|
进程ID | 进程所有者的用户名 | 优先级 | nice值(负值表示搞优先级,正值表示低优先级) | 进程使用的虚拟内存总量(KB) | 进程使用的未被换出的物理内存大小(KB) | 共享内存大小(KB) | 进程状态 | 进程上次更新到现在的CPU时间占用百分比 | 物理内存占比 | 进程使用的CPU时间总计(单位1/100秒) | 命令名 |
1 | dmidecode |
- 用于在Linux下获取硬件信息,遵循SMBIOS/DMI标准,可获取包括BIOS、系统、主板、处理器、内存、缓存等等硬件信息。
- 以内存为例,能获取到如下信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20Handle 0x0222, DMI type 17, 34 bytes
Memory Device
Array Handle: 0x0025
Error Information Handle: No Error
Total Width: 32 bits
Data Width: 32 bits
Size: No Module Installed
Form Factor: DIMM
Set: None
Locator: NVD #63
Bank Locator: NVD #63
Type: Other
Type Detail: Unknown
Speed: Unknown
Manufacturer: Not Specified
Serial Number: Not Specified
Asset Tag: Not Specified
Part Number: Not Specified
Rank: Unknown
Configured Memory Speed: Unknown
1 | numactl -H |
NUMA(Non-Uniform Memory Access,非一致性内存访问)是一种用于多处理器计算机系统的内存架构。与传统的一致性内存访问(Uniform Memory Access, UMA)不同,NUMA架构中的处理器访问内存的速度取决于内存的物理位置。
在NUMA系统中,整个系统的内存被划分为若干个区域,每个区域(通常称为“节点”)都由一个或多个处理器和相应的本地内存组成。处理器访问其本地内存的速度较快,而访问其他节点的内存则较慢。因此,内存访问时间在不同的节点之间是不一致的。
NUMA的主要特点和优势包括:
提高可扩展性:通过将内存和处理器分布在多个节点上,可以更容易地扩展系统的处理能力和内存容量。
降低内存访问延迟:处理器可以更快地访问其本地内存,从而提高系统的性能。
资源共享:多个处理器可以共享整个系统的内存资源,提高了资源利用率。
然而,NUMA架构也带来了编程上的挑战。开发者需要优化程序,以尽量使处理器访问本地内存,从而减少跨节点访问带来的性能开销。
- 显示当前系统的 NUMA 拓扑结构。-H 参数(全写为 —hardware)用于打印出系统的 NUMA 硬件配置,包括节点的数量、每个节点的内存大小、节点之间的距离矩阵以及每个节点上的 CPU 列表。
- available: NUMA 节点的数量。
- node 0 cpus: 节点 0 所使用的cpu。
- node 0 size: 节点 0 的内存大小。
- node 0 free: 节点 0 的空闲内存大小。
- node distances: 节点之间的距离矩阵。
1 | lscpu |
lscpu 是一个简洁的命令行工具,用于显示 CPU 架构的信息。它汇总了关于系统 CPU 的各种信息,输出更加人性化和结构化。其内容包括但不限于:
- 架构(Architecture): 显示 CPU 的架构类型(例如 x86_64)。
- CPU 操作模式(CPU op-mode(s)): 表示 CPU 可以运行的操作模式(32-bit, 64-bit)。
- 字节序(Byte Order): 显示 CPU 的字节序(Little Endian 或 Big Endian)。
- CPU 数量(CPU(s)): 显示系统中的物理 CPU 核心数量。
- 每个插槽的核心数(Core(s) per socket): 显示每个物理插槽中的核心数量。
- 每个核心的线程数(Thread(s) per core): 显示每个核心中的线程数量。
- 插槽数(Socket(s)): 显示系统中的物理 CPU 插槽数量。
- NUMA 节点数(NUMA node(s)): 显示系统中的 NUMA 节点数量。
- CPU 最大频率(CPU max MHz): 显示 CPU 的最大频率。
- L1d/L1i/L2/L3 缓存大小(L1d/L1i/L2/L3 cache): 显示不同级别缓存的大小。
cat /proc/cpuinfo 直接从内核的 /proc 文件系统中读取信息,输出的信息较为详细和冗长。
1 | free |
用于显示系统的内存使用情况,包括物理内存、交换空间和缓冲区缓存的使用情况。
total:总的内存量(包括物理内存和交换空间)。
- used:已经使用的内存量。
- free:空闲的内存量。
- shared:共享内存量,主要用于 tmpfs(临时文件系统)。
- buff/cache:缓冲区和缓存的内存量。
- available:可用的内存量,这个值是基于当前内存使用情况计算出的一个估计值,表示新的应用程序可以使用的内存量。
1 | vmstat 1 |
这些命令用于监控和分析系统性能,每条命令监控不同的系统资源。参数 1 表示每隔 1 秒刷新一次信息。
vmstat 命令显示虚拟内存统计信息,包括内存、分页、CPU 活动等。
- procs:进程信息
- r:运行队列中的进程数
- b:处于不可中断睡眠状态的进程数
- memory:内存信息
- swpd:已使用的交换空间(KB)
- free:空闲内存(KB)
- buff:用于缓冲的内存(KB)
- cache:用于缓存的内存(KB)
- swap:交换空间信息
- si:从交换区调入内存的量(KB/s)
- so:从内存调出到交换区的量(KB/s)
- io:I/O 信息
- bi:块设备读取的块数(每秒)
- bo:块设备写入的块数(每秒)
- system:系统信息
- in:每秒中断数
- cs:每秒上下文切换数
- cpu:CPU 信息
- us:用户态 CPU 时间百分比
- sy:系统态 CPU 时间百分比
- id:空闲时间百分比
- wa:等待 I/O 时间百分比
- st:被盗用的时间百分比(虚拟化环境下)
- procs:进程信息
mpstat 命令显示各个 CPU 的统计信息。
- CPU:CPU 编号,all 表示所有 CPU 的平均值
- %usr:用户态 CPU 时间百分比
- %nice:用于 niced 进程的 CPU 时间百分比
- %sys:系统态 CPU 时间百分比
- %iowait:等待 I/O 完成的时间百分比
- %irq:硬中断处理时间百分比
- %soft:软中断处理时间百分比
- %steal:被其他虚拟机窃取的时间百分比
- %gnice:在 nice 优先级下运行的虚拟 CPU 的时间百分比。
- %guest:运行虚拟 CPU 的时间百分比
- %idle:空闲时间百分比
pidstat 命令显示每个进程的统计信息。
- UID:进程所有者的用户ID
- PID:进程ID
- %usr:用户态 CPU 时间百分比
- %system:系统态 CPU 时间百分比
- %guest:运行虚拟 CPU 的时间百分比
- %CPU:CPU 时间百分比(用户态 + 系统态)
- CPU:运行进程的 CPU 编号
- Command:命令名称
iostat 命令显示 I/O 设备的统计信息。-x 表示显示扩展的设备统计信息,-z 表示跳过所有零值的报告。
- avg-cpu:平均 CPU 使用情况
- %user:用户态 CPU 时间百分比
- %nice:用于 niced 进程的 CPU 时间百分比
- %system:系统态 CPU 时间百分比
- %iowait:等待 I/O 完成的时间百分比
- %steal:被其他虚拟机窃取的时间百分比
- %idle:空闲时间百分比
- Device:设备名称
- r/s:每秒读请求数
- w/s:每秒写请求数
- rkB/s:每秒读的千字节数
- wkB/s:每秒写的千字节数
- rrqm/s:每秒合并的读请求数
- wrqm/s:每秒合并的写请求数
- r_await:读请求的平均等待时间(毫秒)
- w_await:写请求的平均等待时间(毫秒)
- aqu-sz:平均请求队列长度
- rareq-sz:平均读请求大小(千字节)
- wareq-sz:平均写请求大小(千字节)
- svctm:平均服务时间(毫秒)
- %util:设备利用率百分比(I/O 请求的时间占比)
- avg-cpu:平均 CPU 使用情况
1 | sar -n DEV 1 |
sar 命令用于收集、报告和保存系统活动信息。sar -n DEV 1 命令的作用是监控网络接口的流量和性能指标。
- -n DEV:表示收集和报告网络设备(DEV)的统计信息。
- 1:表示每隔 1 秒刷新一次信息。
输出字段解释: - XX:XX:XX AM:时间戳,显示数据采集的时间点。
- IFACE:网络接口名称(如 eth0, lo 等)。
- rxpck/s:每秒接收的数据包数(Packets received per second)。
- txpck/s:每秒发送的数据包数(Packets transmitted per second)。
- rxkB/s:每秒接收的千字节数(Kilobytes received per second)。
- txkB/s:每秒发送的千字节数(Kilobytes transmitted per second)。
- rxcmp/s:每秒接收的压缩数据包数(Compressed packets received per second)。
- txcmp/s:每秒发送的压缩数据包数(Compressed packets transmitted per second)。
- rxmcst/s:每秒接收的多播数据包数(Multicast packets received per second)。
- %ifutil:接口利用率百分比(Interface utilization percentage),即接口的带宽使用情况。
实验1
相关知识
预处理
使用“#”符号定义的预处理指令,如常量宏、内联函数、ifdef宏等。可以使用clang -E对源代码进行预处理(输出到控制台)。
const char、char const、char const、const char const的区别
- const char 和 char const:
- 这两种写法是等价的,表示一个指向 const char 的指针。
- 这个指针可以改变它所指向的对象,但不能通过这个指针修改对象的值。
- char* const:
- 表示一个指向 char 的常量指针。
- 这个指针本身是常量,不能改变它的指向,但可以通过这个指针修改它所指向的对象的值。
- const char* const:
- 表示一个指向 const char 的常量指针。
- 这个指针本身是常量,不能改变它的指向,也不能通过这个指针修改它所指向的对象的值。
- 总结:“*”前的const决定指针变量对其指向目标的修改权限,变量名前的const决定对该变量的修改权限。
gdb使用
1 | gdb --args ./matrix_multiply # 运行gdb |
实验结果
Write-up 2
1 | int main(int argc, char* argv[]) { // What is the type of argv? |
- argv是什么类型?
- char* argv[]定义了argv是一个以char指针为基本类型的数组。简单来说,可以将其看做一个字符串的数组。
- printf(“char d = %c\n”, d)的输出结果是?
- char d = 6
- pcp = argv有效
- pcp是一个二阶指针,argv是一个char*的数组,而数组变量名即为首地址的指针,因此类型匹配。
- pcc2 的类型是什么?
- pcc2 的类型是 const char。const char 和 char const* 是等价的,表示一个指向常量字符的指针(即通过该指针不能修改所指向的字符数据)。
- *pcc = ‘7’无效
- pcc 是一个指向 const char 的指针,不能通过这个指针修改它所指向的字符数据。
- pcc = *pcp有效
- pcp 的类型是 char,pcc 是 const char*(指向常量字符的指针),类型匹配。
- pcc = argv[0]有效
- argv[0] 的类型同样是是 char*,类型匹配。
- cp = *pcp无效
- cp 是一个常量指针,意味着这个指针本身不能在初始化后被修改。
- cp = *argv无效
- 原因同上。
- *cp = ‘!’有效
- cp 指向 char,虽然指针本身是常量,但它所指向的数据可以被修改。
- cpc = *pcp无效
- cpc 是一个常量指针,意味着这个指针本身不能在初始化后被修改。
- cpc = argv[0]无效
- 原因同上;cpc 是一个常量指针,不能重新赋值。
- *cpc = ‘@’无效
- cpc 是一个指向 const char 的指针,所以它所指向的字符数据不能被修改。
Write-up 3
将sizes练习中的类型对应的指针大小打印出来。
定义的PRINT_SIZE
宏如下:
1 |
修改后的打印指针大小的代码如下:
1 | PRINT_SIZE("int pointer", int *); |
打印的结果如下:
1 | size of int pointer : 8 bytes |
不难看出,指针的大小通常与其指向元素的类型无关,与操作系统的访存字长一致。
Write-up 4
1 |
|
Write-up 5
输入make clean; make
指令后的结果:
1 | rm -f testbed.o matrix_multiply.o matrix_multiply .buildmode \ |
对已生成文件进行清理并重新生成可执行文件。
Write-up 6
得到的ASan的错误输出如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18=================================================================
==5675==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 32 byte(s) in 2 object(s) allocated from:
#0 0x55f87f2df32e in __interceptor_malloc (/home/xby/hw1/matrix-multiply/matrix_multiply+0xa232e) (BuildId: e55aea457cfc44e1756fef8c1effbed264376bf8)
#1 0x55f87f31ad5a in make_matrix /home/xby/hw1/matrix-multiply/matrix_multiply.c:40:24
#2 0x7fe96a486d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
Indirect leak of 128 byte(s) in 8 object(s) allocated from:
#0 0x55f87f2df32e in __interceptor_malloc (/home/xby/hw1/matrix-multiply/matrix_multiply+0xa232e) (BuildId: e55aea457cfc44e1756fef8c1effbed264376bf8)
#1 0x55f87f31adf7 in make_matrix /home/xby/hw1/matrix-multiply/matrix_multiply.c:50:36
Indirect leak of 64 byte(s) in 2 object(s) allocated from:
#0 0x55f87f2df32e in __interceptor_malloc (/home/xby/hw1/matrix-multiply/matrix_multiply+0xa232e) (BuildId: e55aea457cfc44e1756fef8c1effbed264376bf8)
#1 0x55f87f31ada2 in make_matrix /home/xby/hw1/matrix-multiply/matrix_multiply.c:47:32
#2 0x7fe96a486d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
SUMMARY: AddressSanitizer: 224 byte(s) leaked in 12 allocation(s).
说明make_matrix生成的矩阵均发生了内存泄漏。
Write-up 7
代码修改:在make_matrix
函数中对新创建的矩阵添加初始化的操作。
1 | // Allocates a row-by-cols matrix and returns it |
运行./matrix_multiply -p
后得到的结果:
1 | Setup |
经验算,结果正确。
Write-up 8
代码修改:在进程结束前,对三个矩阵进行销毁:
1 | free_matrix(A); |
使用valgrind
再次运行得到的结果:
1 | ==6130== |