【Tool】 记录各种用到的工具

记录性能优化、环境模拟等各种用到的工具

References

汇编/嵌入式编程工具

在 Linux 环境下进行汇编或嵌入式编程时,涉及的工具和程序非常广泛,包括编译器、调试工具、构建系统、性能分析工具等。下面是一些常见的汇编或嵌入式编程工具的详细介绍:

汇编工具 (Assembly Tools)

GAS (GNU Assembler)

GAS 是 GNU 编译器集合(GCC)的一部分,专门用于将汇编语言转换成机器代码(即目标文件)。它支持多种体系结构,适用于嵌入式系统开发,通常与 GCC 配合使用。

常用选项:

  • -o <file>: 指定输出文件。
  • -g: 生成调试信息。
  • -D <macro>: 定义宏。

示例:

1
as -o main.o main.s

交叉编译工具链 (Cross Compiler Toolchain)

GCC (GNU Compiler Collection)

GCC 是用于 C、C++、Fortran 等语言的编译器,常用于嵌入式编程中。它可以生成目标平台的代码,并支持交叉编译(cross-compilation),即在一种平台上为另一种平台编译代码。

常用选项:

  • -o <file>: 输出文件。
  • -mcpu=<target>: 指定目标架构(如 ARM、MIPS)。
  • -m32-m64: 设置生成的代码是 32 位或 64 位。

示例:

1
arm-none-eabi-gcc -o my_program.elf main.c

Clang

Clang 是 LLVM 项目的一部分,作为 GCC 的替代品,Clang 提供了高效的编译功能,并且具有更加友好的错误报告。它同样支持交叉编译,特别适合现代嵌入式编程和集成开发环境(IDE)中使用。

常用选项:

  • -target <target>: 指定目标平台。
  • -o <file>: 输出文件。

示例:

1
clang -target arm-none-eabi -o my_program.elf main.c

Binutils

Binutils 是一组二进制工具,包括汇编器、链接器、调试器等,广泛用于嵌入式系统开发。ldas 工具是其中最常用的,负责汇编、链接以及生成目标文件。

常用工具:

  • as:汇编源文件。
  • ld:链接目标文件生成最终的可执行文件。
  • objcopy:将目标文件转换为不同格式。

示例:

1
2
as -o main.o main.s
ld -o my_program.elf main.o

调试工具 (Debugging Tools)

GDB (GNU Debugger)

GDB 是一个功能强大的调试工具,适用于 C/C++ 等程序的调试。在嵌入式开发中,GDB 通常配合交叉编译工具链和硬件调试器(如 JTAG、SWD)一起使用。

常用命令:

  • run:启动程序。
  • break <line>:在指定行设置断点。
  • step:单步执行,进入函数。
  • next:单步执行,不进入函数。

示例:

1
gdb my_program.elf

OpenOCD

OpenOCD 是一个用于与目标硬件(如 ARM 处理器)进行通信的调试工具。它支持通过 JTAG 或 SWD 接口进行调试,可以与 GDB 配合使用。

常用命令:

  • targets:列出连接的目标。
  • reset halt:复位并停止目标。
  • flash write_image erase <file> 0x0:将固件烧录到设备。

示例:

1
openocd -f interface/stlink-v2.cfg -f target/stm32f4x.cfg

JLink 是 SEGGER 提供的一个商业级调试器,支持 JTAG 和 SWD 接口,用于调试各种嵌入式设备。它提供高性能的调试功能,广泛用于工业和开发中。

主要功能:

  • 支持高速调试,能够快速读写内存和寄存器。
  • 与多种 IDE(如 Keil、IAR、Eclipse)兼容。
  • 提供强大的脚本支持和自动化功能。

常用命令:

  • connect: 连接目标硬件。
  • r: 重置目标设备。
  • loadfile : 加载二进制文件到目标设备。

示例:

1
JLinkExe -device STM32F407VG -if SWD

LLDB

LLDB 是 LLVM 提供的调试工具,类似于 GDB,但更加现代化。它具有更高效的性能,特别适用于基于 Clang 的编译工具链。

常用命令:

  • run:启动程序。
  • breakpoint set:设置断点。
  • step:单步执行。

示例:

1
lldb my_program.elf

构建工具 (Build Tools)

Make

Make 是一个非常常用的构建工具,使用 Makefile 管理项目的编译过程。它会根据 Makefile 中的规则自动执行编译、链接等操作,尤其适合嵌入式项目。

常用命令:

  • make:构建项目。
  • make clean:清理编译结果。

示例:

1
make -f Makefile

CMake

CMake 是一个跨平台的自动化构建工具,可以生成适用于不同平台的构建文件(如 Makefile、Ninja 文件等)。它在现代嵌入式开发中非常流行,支持复杂的项目构建配置。

常用命令:

  • cmake .:生成构建文件。
  • make:执行构建。

示例:

1
2
cmake .
make

其他工具

nm

nm 是一个用于列出二进制文件(例如可执行文件、共享库、目标文件等)符号表的工具。它显示文件中定义和引用的符号,包括函数、变量等。通过 nm,用户可以查看符号的类型和地址信息。

常用选项:

  • nm : 列出指定文件的符号。
  • -g: 只显示全局符号。
  • -n 或 –numeric-sort: 按地址排序符号。

示例:

1
nm my_program|grep func

Objdump

objdump 用于反汇编和查看目标文件的详细信息。它可以显示汇编代码、符号表、段信息等,帮助开发人员理解程序的低级结构。

常用命令:

  • objdump -d <file>:反汇编文件。
  • objdump -t <file>:显示符号表。

示例:

1
objdump -d my_program

Readelf

readelf 用于显示 ELF 文件的详细信息,提供比 objdump 更加专注于 ELF 文件结构的查看。它支持查看 ELF 头、段、节区、符号表等信息。

常用命令:

  • readelf -h <file>:显示 ELF 文件头信息。
  • readelf -S <file>:显示节区信息。

示例:

1
readelf -h my_program

热点分析

热点分析通常通过使用 性能分析工具 来实现,工具会提供每个函数、方法、代码块的执行时间、调用次数、CPU 占用率等信息,帮助开发人员识别耗时最多的部分。

常见的热点分析方法

  • 调用图(Call Graph):通过调用图分析函数之间的调用关系,找到调用最频繁的部分。
  • 性能剖析(Profiling):通过工具生成程序运行时的性能数据,分析哪些函数或代码块占用了最多的时间或资源。
  • 热代码路径(Hot Code Path)分析:关注那些频繁执行的路径或分支,优化这些路径的性能。
  • 内存热点分析:分析程序中哪些数据结构或对象频繁创建、销毁,导致内存管理不善或频繁的垃圾回收。

实际操作

  • 函数级分析:分析程序中的每个函数,找出耗时最多的函数并进行优化。
  • 多线程/并发分析:对于并发程序,热点分析还要考虑线程的执行时间、锁竞争和同步问题,识别线程间的性能瓶颈。
  • 内存分析:分析内存的分配和释放,找出内存泄漏或频繁的内存分配导致的性能瓶颈。

性能分析工具

gprof:GNU profile工具

  • 适用语言:C、C++、Pascal、Fortran
  • 介绍:用于程序的性能优化以及程序瓶颈问题的查找和解决。通过分析应用程序运行时产生的“flat profile”,可以得到每个函数的调用次数,每个函数消耗的处理器时间,也可以得到函数的“调用关系图”,包括函数调用的层次关系,每个函数调用花费了多少时间。
  • 缺点:对并行程序支持较差,不能提供细粒度的分析,主要适用于函数级别的性能分析。

使用步骤:

  • 1、用gcc、g++、xlC编译程序时,使用-pg参数,如:g++ -pg -o test test.cpp。编译器会自动在目标代码中插入用于性能测试的代码片断,这些代码在程序运行时采集并记录函数的调用关系和调用次数,并记录函数自身执行时间和被调用函数的执行时间。
  • 2、执行编译后的可执行程序,如:./test。该步骤运行程序的时间会稍慢于正常编译的可执行程序的运行时间。程序运行结束后,会在程序所在路径下生成一个缺省文件名为gmon.out的文件,这个文件就是记录程序运行的性能、调用关系、调用次数等信息的数据文件。
  • 3、使用gprof命令来分析记录程序运行信息的gmon.out文件,如:gprof test gmon.out。则可以在显示器上看到函数调用相关的统计、分析信息。

Perf

  • 适用语言: C, C++
  • 平台: Linux
  • 特点: Perf 是 Linux内置的性能分析工具,可用于分析 CPU 使用率、内存访问、系统调用等。它是一个命令行工具。适用于深度的 Linux 系统级性能分析。
  • 缺点:需要一定的学习成本,报告可能较为复杂。

Perf是一个很大的工具,此处仅展示分析某个应用的的用法。 使用步骤(使用gprof的那个可执行文件)

  • 1、perf record ./test,部分性能参数需要root权限
  • 2、perf report

References

perf简单例子-程序调用栈火焰图

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
perf record -F 99 -p 2347 -g -- sleep 30
# perf record表示采集系统事件, 没有使用 -e 指定采集事件, 则默认采集 cycles(即 CPU clock 周期), -F 99 表示每秒 99 次, -p 2347 是进程号, 即对哪个进程进行分析, -g 表示记录调用栈, sleep 30 则是持续 30 秒.
# 统计每个调用栈出现的百分比, 然后从高到低排列
perf report -n –stdio
# 解析perf收集的信息
perf script -i perf.data &> perf.unfold
# 生成折叠后的调用栈
# 使用开源软件:https://github.com/brendangregg/FlameGraph.git
./stackcollapse-perf.pl perf.unfold &> perf.folded
# 生成火焰图
./flamegraph.pl perf.folded > perf.svg

perf简单例子-分析热点函数、指令

1
2
3
4
5
6
7
8
# 通过-g选项保留源代码信息
gcc -g test.c -o test
# 通过perf record命令对test程序采样,-g表示采样调用栈
perf record -F 999 ./test
# 查看热点分布
perf report
# 查看热点函数testA中的热点指令及语句
perf annotate --stdio --symbol=testA

Intel VTune Profiler

  • 适用语言: 多语言支持
  • 平台: Windows、Linux
  • 特点: Intel VTune Profiler 是一个功能强大的性能分析工具,可用于分析 CPU 使用率、内存访问、多线程性能等。适用于 Intel 处理器。
  • 可以看到 perf 看不到L3cache 等硬件特性

references

TAU

  • 适用语言: C、C++、python
  • 官网:https://www.cs.uoregon.edu/research/tau/home.php
  • 特点: 是一个面向MPI与OpenMP并行程序的profiler,在目前看到的OpenMPI的Profiler中算是比较健全的一个。相比于Intel的vtune面向OpenMPI的时候会有些限制,TAU可以根据不同的MPI发行版重新编译。

references


GPU 分析工具

官方全部工具

cuda-gdb

Nsight Compute

  • nvprof,计算能力8.0以下使用
  • 注意系统要求(如win11 ws2才支持):system-requirementsunknown-error-on-device-0-when-running-ncu-on-wsl
  • 用于深入分析单个 CUDA 内核的性能瓶颈,帮助你优化内核代码。
  • 通常,你可以使用 Nsight Systems 先找到瓶颈的 CUDA 内核,然后使用 Nsight Compute 对这些内核进行详细的性能分析。

使用方案:

Nsight System

  • 用于高层次的系统级性能分析,帮助你识别整个应用的瓶颈,例如 GPU 内核启动延迟、数据传输等问题。
  • UserGuide

使用方案:

  • 类似Nsight Compute,但支持jupyter等
  • 可以支持应用执行中的很多系统调用情况

ComputeSanitizer

https://docs.nvidia.com/compute-sanitizer/ComputeSanitizer/index.html#id1

  • 功能正确性检查工具,包含:memcheck、racecheck、initcheck、synccheck等
  • cuda12.0以下内存检查使用CUDA-MEMCHECK,以上使用ComputeSanitizer

HPCtoolkit

  • 适用语言: C、C++、CUDA
  • 官网:HPCToolkit
  • 特点:支持CUDA

内存分析工具

  • gdb:-g源码调试

tsan

TSan(ThreadSanitizer)是一个用于检测多线程程序中的 数据竞争线程安全问题 的工具。它是由 Google 开发的,用于帮助开发者发现并修复多线程程序中的并发问题,这些问题可能导致难以复现的错误和难以调试的行为。

1. 什么是数据竞争? 数据竞争是指多个线程并发地访问同一块内存区域,并且至少有一个线程在写入该内存区域,而其他线程可能在读或写该内存。数据竞争通常会导致不可预测的程序行为,比如程序崩溃、结果错误等。数据竞争的问题尤其难以发现,因为它们通常依赖于程序执行的特定时序和上下文。

2. TSan 的功能 ThreadSanitizer 是一种 动态检测工具,它能够监测并发程序中的线程交互,并在检测到数据竞争时,给出详细的报告。它通过 **插桩(Instrumentation) **方式,插入检查代码,追踪每个线程对共享内存的访问,以此来检测潜在的数据竞争。

具体功能包括:

  • 检测数据竞争:在多线程程序中,TSan 能够发现不同线程对同一内存位置的并发访问(读-写或写-写),并且报告潜在的数据竞争。
  • 报告细节:当 TSan 检测到数据竞争时,会提供详细的错误报告,包含竞争发生的栈信息、线程信息、访问的内存位置等,帮助开发者定位问题。
  • 跨平台支持:TSan 支持 Linux、macOS、Android 和其他平台,通常与 Clang 和 GCC 编译器兼容。

3. 如何使用 TSan TSan 是通过编译器插件实现的,因此需要在编译程序时启用它。以下是启用 TSan 的基本步骤:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 1、编译时启用 TSan,使编译器将 TSan 插桩到代码中,在程序运行时启用线程安全检查。
g++ -fsanitize=thread -g test.cpp -o test
# 2、运行时,程序将被 TSsan 监控,检测线程间的竞争。
./test
# 如果程序中有数据竞争,TSan 会输出类似以下的报告:
ThreadSanitizer: data race in function 'foo' at address 0x601000000020
  #0 0x7f89b5cb5e6f in foo
  #1 0x7f89b5cb5e79 in bar
  #2 0x7f89b5cb5f89 in main
TSan 会提供详细的栈跟踪,指出哪些线程、哪些内存地址、在哪些函数中发生了数据竞争。

4. TSan 的工作原理 TSan 通过对程序进行 插桩,在程序中每次内存访问(读/写)时插入检查代码,追踪每个线程对内存的访问。它会记录每个线程对共享内存的访问并进行比较,以判断是否存在数据竞争。

主要机制:

  • 内存访问追踪:TSan 会追踪每个线程对内存地址的访问情况,记录访问的时间戳和线程标识。
  • 同步原语检测:TSan 会检查线程之间的同步操作(如 mutex、lock、atomic)是否正确使用,确保线程安全。
  • 数据竞争检测:如果两个线程访问同一内存位置,并且至少一个是写操作,TSan 会标记为潜在的数据竞争。

5. TSan 检测的线程安全问题 除了检测数据竞争,TSan 还可以帮助识别以下并发编程中的常见问题:

  • 死锁:如果两个线程因相互等待而导致死锁,TSan 也可以通过检测锁的顺序和依赖关系来帮助识别死锁。
  • 非原子操作:在多线程环境中,如果某些操作不是原子的,可能会导致竞态条件。TSan 可以通过对同步操作的检查,帮助发现这些问题。
  • 不当的内存同步:如果线程没有适当的同步机制(如 mutex 或 atomic)来协调对共享数据的访问,可能会出现竞态条件,TSan 会标记这些不安全的内存访问。

6. TSan 的优点和限制

  • 优点:能够捕获到很难复现的多线程问题,提供详细的报告,包括访问的内存位置、线程栈、数据竞争的上下文,帮助开发者快速定位并修复问题。
  • 限制:会有一定的性能开销(开发阶段使用),可能不会检测所有类型的并发问题,特别是某些边缘情况或者深度依赖于硬件的并发问题。

AddressSanitizer (ASan)

ASanAddressSanitizer)是一个用于检测 内存错误 的强大工具,特别是针对 缓冲区溢出堆栈溢出使用后释放(use-after-free)等常见内存问题。ASan 是由 Google 开发的,作为一个 编译时检测工具,它可以在程序运行时检测出许多类型的内存错误,并提供详细的错误报告。ASan 可以用于 CC++ 等语言,广泛应用于开发和测试阶段,帮助开发者发现和修复难以调试的内存错误。

  1. ASan 的功能 ASan 的核心功能是通过 内存访问跟踪 来检测程序中的各种内存错误。它能有效检测以下几类常见的内存问题:
  • 缓冲区溢出(Buffer Overflow):当程序写入超出分配内存的区域时,会导致数据损坏或程序崩溃。
  • 堆栈溢出(Stack Overflow):当程序的栈内存超出预定范围时,可能会覆盖局部变量或函数返回地址。
  • 使用后释放(Use-After-Free):指在内存被释放后,程序仍然访问该内存。
  • 内存泄漏(Memory Leak):指程序分配了内存但没有释放,导致内存消耗不断增加。
  • 双重释放(Double Free):指在释放内存后再次释放该内存,可能导致程序崩溃。
  • 未初始化内存读取(Use of Uninitialized Memory):程序读取未初始化的内存内容,可能导致不可预测的行为。
  1. ASan 的工作原理 ASan 通过 编译器插桩(Instrumenting Compiler)和 运行时库(Runtime Library)的配合工作来检测内存错误。
  • 编译时插桩:在程序的源代码编译过程中,ASan 会插入额外的检查代码,这些代码会在程序运行时检查每个内存访问,确保它们在合法的内存范围内。
  • 内存分配替换:ASan 会替换程序的 内存分配函数(如 mallocfreenewdelete)来监控内存的分配和释放操作。
  • 内存红区(Redzones):ASan 在每个内存块的前后插入一些特殊的 “红区”(Redzones),这些区域用于检测 越界访问。如果程序试图访问红区,ASan 会报告错误。
  • 运行时检测:当程序访问非法内存时,ASan 会触发 运行时错误检测,并输出详细的错误信息(如错误的内存地址、堆栈信息等)。
  1. 如何使用 ASan 要启用 AddressSanitizer,您需要在编译时添加 -fsanitize=address 选项,并启用调试信息 -g(以便于调试)。
1
g++ -fsanitize=address -g -o test test.cpp
  1. ASan 错误报告 当 ASan 检测到内存错误时,它会生成详细的错误报告。该报告通常包含以下信息:
  • 错误类型:如 use-after-freebuffer overflowstack overflow 等。
  • 错误位置:报告发生错误的内存地址,指出程序在哪里进行非法内存访问。
  • 调用栈:ASan 会提供程序的调用栈信息,帮助开发者快速定位问题。
  • 内存布局:显示内存分配情况,包括程序访问的内存区域和红区位置。
  1. ASan 检测的内存问题 ASan 可以检测的内存问题包括:
  • 堆栈溢出(Stack Overflow):当局部变量超出栈边界时,ASan 会报告堆栈溢出。
  • 缓冲区溢出(Buffer Overflow):当访问超出数组或缓冲区的范围时,ASan 会检测到缓冲区溢出。
  • 使用后释放(Use-After-Free):在内存被释放后,如果程序继续使用该内存,ASan 会报告此错误。
  • 内存泄漏(Memory Leak):ASan 可以检测到程序中未释放的内存(通过启用 -fsanitize=address 和使用 ASAN_OPTIONS=detect_leaks=1)。
  • 双重释放(Double Free):当程序尝试两次释放同一块内存时,ASan 会报告此问题。
  • 未初始化内存访问(Use of Uninitialized Memory):当程序访问未初始化的内存时,ASan 会报告此错误。
  1. ASan 的优点和缺点
  • 优点:高效的内存错误检测、易于使用、详细的错误报告、广泛支持。
  • 缺点:性能开销、仅支持动态检测、依赖编译器。

valgrind

  • 平台:Linux / macOS / Windows(通过 Cygwin)
  • 用途:Valgrind 是一款开源的动态分析工具,广泛用于 内存分析,如查找内存泄漏、内存越界等问题。 功能:
  • Memcheck:用于检测内存泄漏、越界访问和未初始化的内存读取。
  • Cachegrind:用于缓存行为分析,评估 CPU 缓存的命中率。
  • Callgrind:支持函数级别的性能分析,提供详细的 CPU 性能数据。
  • Helgrind:用于检测并发程序中的数据竞争。 适用场景:适用于内存优化、程序调试和多线程程序的性能分析。 使用方式:通过命令行运行程序时加上 valgrind,比如 valgrind –leak-check=full ./my_program。 优点:强大的内存分析功能,能够检测很多潜在的错误。 缺点:运行时开销较大,程序执行速度可能会减慢。

eBPF (Extended Berkeley Packet Filter)

  • 类型:内核性能分析工具
  • 功能:eBPF 可以用于监控系统的 CPU 使用情况、内存分配、I/O 性能、网络流量 等。
  • 使用场景:eBPF 适用于 Linux 系统的全栈性能分析,特别是在容器化环境中(如 Kubernetes、Docker)。
  • 优点:能够高效且低开销地进行性能分析,实时提供系统各个层次的性能数据。
  • 缺点:需要一定的学习成本,并且工具的设置可能比较复杂。

在现代计算中,性能优化是提高程序效率、响应时间、资源利用率等重要方面的核心工作。不同类型的性能瓶颈可以通过不同的优化策略来解决,常见的优化策略包括并行度优化数据传输优化存储器访问优化向量化优化负载均衡优化多线程扩展性优化。下面将详细介绍每个优化策略。


性能优化策略

1. 并行度优化(Parallelism Optimization)

并行度优化是指将计算任务拆分成多个独立的子任务,利用多核处理器或多台机器的计算能力来加速计算过程。并行度优化主要关注如何高效地将任务分解并利用多个计算资源。

核心策略:

  • 任务划分:将计算任务划分为多个相对独立的子任务,确保每个子任务都能并行执行。划分可以基于数据分割或功能划分。
  • 并行模型选择:选择合适的并行编程模型,如多线程、分布式计算、SIMD(单指令多数据)等,依赖于硬件架构和应用的需求。
  • 粒度控制:控制任务的划分粒度,避免过多的细小任务带来的上下文切换开销。任务太小可能引发更多的线程启动和调度开销,反而会降低性能。
  • 避免线程同步问题:在并行化时,尽量减少线程间的同步需求,如锁的竞争等,因为锁竞争会增加线程等待时间,影响性能。

实例:

  • 多核处理器利用:将计算密集型任务分配给不同的 CPU 核心。
  • GPU 加速:使用图形处理单元(GPU)进行并行计算,例如深度学习中广泛使用的并行训练。

2. 数据传输优化(Data Transfer Optimization)

数据传输优化关注的是如何减少计算过程中数据的传输开销,尤其是在多核、多节点或大规模并行计算环境中,数据传输的延迟和带宽限制可能成为性能瓶颈。

核心策略:

  • 减少数据传输量:尽量减少进程之间、计算节点之间的通信量。可以通过局部计算、减少数据的复制或压缩数据传输来减少带宽消耗。
  • 数据预取:根据访问模式预测数据的需求,提前加载数据到缓存中,从而减少等待时间。
  • 内存映射与共享内存:使用共享内存或内存映射文件来避免频繁的进程间通信,特别是在多进程或多线程的应用程序中。
  • 局部性优化:将数据分配到物理内存的本地区域,减少跨节点或跨芯片的数据传输。

实例:

  • 在多节点集群中,避免每次计算都从主存储器加载大量数据,而是通过缓存和局部数据共享来减少传输。
  • 在 GPU 和 CPU 之间,使用较小的批次数据传输,以减少 GPU 与主机之间的通信开销。

3. 存储器访问优化(Memory Access Optimization)

存储器访问优化主要目的是减少内存访问延迟,提高内存带宽的利用率。内存访问模式的不合理会造成严重的性能瓶颈,尤其是对于大规模数据的计算密集型任务。

核心策略:

  • 数据局部性优化:通过优化数据访问模式,提高数据在缓存中的命中率。可分为时间局部性(重复访问相同数据)和空间局部性(访问数据时的空间邻近性)。
  • 缓存优化:优化程序数据结构,使数据在缓存中更容易命中,从而减少访问主内存的次数。使用预取技术和合理的缓存对齐可以显著提高缓存命中率。
  • 避免不必要的内存访问:减少冗余的内存访问,如不必要的内存复制或多次访问相同的数据。
  • 非一致性存储模型优化:在多处理器系统中,保持各个缓存一致性可能导致额外的开销,优化缓存一致性协议和访问策略可以提升性能。

实例:

  • 优化矩阵运算时,按行或按列的顺序访问数据,以提高缓存命中率。
  • 使用 NUMA(Non-Uniform Memory Access)架构时,避免频繁地访问远程内存,尽量保持计算和数据存储在同一个节点的本地内存中。

4. 向量化优化(Vectorization Optimization)

向量化是指将标量操作转换为向量操作,在单条指令中处理多个数据元素。现代处理器,尤其是具有SIMD(单指令多数据)指令集的处理器,能够通过向量化提升计算效率。

核心策略:

  • 利用SIMD指令:使用 SIMD 指令集(如 AVX、SSE、NEON 等)对数据进行向量化操作,在单个指令周期内处理多个数据元素。
  • 编译器自动向量化:现代编译器(如 GCC、Clang、Intel Compiler)能够自动识别可以向量化的循环,并进行相应优化。
  • 手动优化:在一些复杂的场景中,可以手动编写 SIMD 代码,通过内联汇编或编写特定的 SIMD 库来实现向量化优化。
  • 数据对齐:确保数据存储在合适的内存地址对齐,以便在向量化时避免额外的开销。

实例:

  • 在图像处理、科学计算等应用中,使用 SIMD 向量化技术对每个像素或数据点执行并行操作(如加法、乘法等)。

5. 负载均衡优化(Load Balancing Optimization)

负载均衡优化是指在多处理器、多核心或分布式系统中,合理分配计算任务,以避免某些处理器过载或闲置,从而提高计算资源的利用率。

核心策略:

  • 任务划分:合理划分任务,将计算负载均匀地分配给不同的计算单元。划分粒度要合适,避免过细的划分导致调度开销。
  • 动态负载均衡:在运行时动态调整任务的分配,以应对负载变化和计算资源的不均衡。例如,在多核环境中,动态地将任务从负载较重的核心转移到空闲的核心上。
  • 数据局部性和负载均衡的结合:在多核或多节点环境中,除了考虑负载均衡,还要考虑任务和数据的局部性,避免数据传输引发的性能瓶颈。

实例:

  • 在分布式计算中使用负载均衡策略,避免某些计算节点过载,其他节点空闲。
  • 在多核处理器上,使用调度算法动态调整任务负载。

6. 多线程扩展性优化(Multithreading Scalability Optimization)

多线程扩展性优化关注的是如何使程序在多核或多处理器系统上运行时能够保持良好的性能提升,尤其是在线程数增加时,如何避免性能的下降。

核心策略:

  • 避免线程竞争:合理设计程序,减少线程间的资源竞争。过多的锁、临界区和线程同步会导致线程间的阻塞,从而影响程序的扩展性。
  • 线程数的调优:选择合适的线程数,避免过多线程带来的上下文切换开销。通常,线程数不应超过处理器核心数。
  • 工作窃取(Work Stealing):在多线程应用中,可以使用工作窃取算法,通过让空闲线程从负载较重的线程中窃取任务,平衡负载,提升扩展性。
  • 任务划分粒度:避免过小或过大的任务粒度,过小的任务会增加线程调度开销,过大的任务则可能导致资源利用率不足。

实例:

  • 在多核机器上,动态调整线程数,以适应任务的计算需求和机器的硬件能力。
  • 在并行计算中,使用线程池和任务队列来有效管理线程的创建和销毁。

环境模拟

docker

qemu

docs

qemu安装ARM

  • QEMU启动ARM64 Linux内核
  • 大致思路是:
    • 安装qemu-system-aarch64(ARM-64bit)模拟器;
    • 安装aarch64-linux-gnu(ARM-64bit)交叉编译器;
    • 交叉编译linux源码,得到ARM64 Linux内核镜像;
    • 交叉编译busybox源码,使用busybox制作initramfs;
    • 最后使用qemu-system-aarch64启用ARM64 Linux内核;

环境管理/运维

微服务

  • k8s

python

  • conda

其他

内容比较compare

代码调用关系

  • cflow:静态分析工具,用来生成 C/C++ 程序的调用图。
  • Callgrind:动态函数分析
Licensed under CC BY-NC-SA 4.0
最后更新于 Mar 16, 2025 19:51 +0800
loveleaves
使用 Hugo 构建
主题 StackJimmy 设计