Linux 系统中的算法技巧与性能优化
引言
Linux 系统以其开源、稳定和高度可定制的特性,在服务器端、嵌入式设备以及开发环境中得到了极为广泛的应用。对于开发者而言,不仅要掌握在 Linux 环境下实现各类算法的方法,更要知晓如何利用系统特性对算法进行优化,以提升程序的整体性能。本文将深入探讨在 Linux 系统中实现算法的相关技巧,以及如何通过多种途径对算法性能进行调优。
利用 Linux 系统特性优化算法
内存管理与大页内存(Huge Pages)
在处理大规模数据或者算法对内存访问频繁的场景下,如机器学习中的数据处理、复杂的图算法等,内存管理的优化显得尤为重要。Linux 系统提供的大页内存(Huge Pages)机制能够显著提升内存访问效率。
传统的内存分页机制中,内存以较小的页面(如 4KB)为单位进行管理,这会导致大量的页表条目,增加内存寻址的开销。而大页内存则使用更大的页面大小(如 2MB 或 1GB),大大减少了页表条目的数量,降低了内存寻址的开销,进而提升内存访问的速度。
要查看当前系统的大页内存配置,可以使用以下命令:
TypeScript
取消自动换行复制
cat /proc/sys/vm/nr_hugepages
若要临时分配大页内存(需要 root 权限),例如分配 1024 个大页(假设每个大页为 2MB),可以执行:
TypeScript
取消自动换行复制
echo 1024 > /proc/sys/vm/nr_hugepages
在程序中使用大页内存,可以通过posix_memalign或mmap接口来申请。例如,使用mmap函数将文件映射到内存进行直接操作,示例代码如下:
TypeScript
取消自动换行复制
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
int fd = open("test_file", O_RDWR);
if (fd == -1) {
perror("open");
return 1;
}
off_t size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
char *addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
// 在这里对addr指向的内存进行操作
if (munmap(addr, size) == -1) {
perror("munmap");
}
close(fd);
return 0;
}
CPU 亲和性(CPU Affinity)
对于多线程算法或者并行计算任务,如并行排序算法、矩阵并行运算等,线程在不同 CPU 核心间频繁迁移会导致缓存失效,从而降低算法性能。Linux 系统的 CPU 亲和性机制可以解决这一问题。
CPU 亲和性允许将特定的线程或进程绑定到指定的 CPU 核心上,这样线程在执行过程中始终在同一核心上运行,避免了因核心迁移导致的缓存失效,提高了缓存命中率,进而提升算法的执行效率。
可以使用taskset命令将进程绑定到指定的 CPU 核心。例如,将名为program的程序绑定到 CPU 核心 0 - 3 上运行,可以执行:
TypeScript
取消自动换行复制
taskset -c 0-3./program
在代码中,也可以使用 sched_setaffinity API 来实现动态绑定。以下是一个简单的示例代码,展示了如何在 C 语言中使用 sched_setaffinity将当前进程绑定到 CPU 核心 1 上:
TypeScript
取消自动换行复制
#define _GNU_SOURCE
#include <stdio.h>
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
cpu_set_t mask;
CPU_ZERO(&mask);
CPU_SET(1, &mask);
if (sched_setaffinity(0, sizeof(mask), &mask) == -1) {
perror("sched_setaffinity");
return 1;
}
// 进程的主要逻辑代码
return 0;
}
文件 I/O 优化
在处理大规模文件数据的算法场景中,如数据清洗算法、日志分析算法等,文件 I/O 的性能对整个算法的执行效率有着关键影响。
一种优化方式是使用异步 I/O(AIO)或内存映射文件(mmap)。异步 I/O 允许在进行文件 I/O 操作时,程序无需等待 I/O 操作完成,可以继续执行其他任务,从而提高程序的并发性能。内存映射文件则将文件直接映射到内存地址空间,程序可以像访问内存一样访问文件内容,减少了用户态与内核态之间的数据拷贝,提高了数据访问速度。
例如,使用mmap将文件映射到内存进行读写操作的示例代码如下:
TypeScript
取消自动换行复制
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
int fd = open("test_file", O_RDWR);
if (fd == -1) {
perror("open");
return 1;
}
off_t size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
char *addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap");
close(fd);
return 1;
}
// 对addr指向的内存进行读写操作,如同操作文件内容
if (munmap(addr, size) == -1) {
perror("munmap");
}
close(fd);
return 0;
}
此外,批量读写函数(如readv/writev)可以减少系统调用的次数。因为每次系统调用都伴随着一定的开销,减少系统调用次数能够提高 I/O 操作的效率。
对于顺序读写的场景,增大readahead缓冲区也能提升性能。readahead机制会预读文件的后续内容到内存缓冲区中,当程序需要读取后续数据时,可以直接从内存中获取,减少磁盘 I/O 操作。可以使用以下命令增大readahead缓冲区:
TypeScript
取消自动换行复制
blockdev --setra 131072 /dev/sda1
上述命令将/dev/sda1设备的预读缓冲区设置为 64MB(131072 个块,每块 512B)。
性能分析与调优工具
性能剖析工具(Profiler)
perf
perf是 Linux 系统原生的强大性能分析工具,它能够对 CPU 占用情况、缓存命中率、函数调用栈等多个方面进行详细分析。
使用perf进行性能分析主要分为两个步骤。首先,通过perf record命令记录程序运行时的性能数据。例如,要对名为algorithm_program的程序进行性能数据记录,可以执行:
TypeScript
取消自动换行复制
perf record -g./algorithm_program
其中,-g选项用于记录函数调用栈信息,这对于后续分析性能瓶颈所在的函数非常有帮助。
记录完成后,使用perf report命令生成分析报告,该报告将详细展示程序中各个函数的 CPU 使用情况、调用次数等信息,帮助开发者快速定位性能瓶颈。
TypeScript
取消自动换行复制
perf report
valgrind
valgrind是一款功能强大的内存调试和性能分析工具,其callgrind子工具在性能分析方面表现出色,尤其适用于程序调试阶段。
使用valgrind的callgrind子工具分析程序性能的命令如下:
TypeScript
取消自动换行复制
valgrind --tool=callgrind./program
执行上述命令后,valgrind会对program的运行过程进行监测,并生成详细的性能分析报告。该报告可以帮助开发者了解程序中各个函数的执行时间、调用关系以及缓存使用情况等,从而针对性地进行性能优化。
代码优化技巧
编译器优化选项
GCC 和 Clang 等编译器提供了丰富的优化选项,合理使用这些选项能够显著提升生成代码的性能。
常见的优化选项包括-O2、-O3和-Ofast。-O2选项开启了一系列基本的优化,如循环展开、公共子表达式消除等,能够在一定程度上提高代码执行效率,同时编译时间和生成代码的体积也相对较为平衡。-O3选项在-O2的基础上进一步加强优化,包括更多的指令级并行优化等,能带来更高的性能提升,但可能会增加编译时间和生成代码的体积。-Ofast选项则在-O3的基础上,启用了一些可能不符合标准但能进一步提升性能的优化,如对数学函数的优化等,但可能会导致代码在某些情况下的行为与标准略有不同。
此外,-march=native选项可以针对当前 CPU 架构优化指令集。不同的 CPU 架构支持不同的指令集扩展,如常见的 AVX、SSE 等。使用该选项,编译器会根据当前运行的 CPU 架构生成最适合的指令集代码,充分发挥硬件的性能优势。例如,使用 GCC 编译 C 语言程序并开启优化选项的命令如下:
TypeScript
取消自动换行复制
gcc -O3 -march=native -o algorithm algorithm.c
上述命令将algorithm.c文件编译成可执行文件algorithm,并启用了-O3优化和针对本地 CPU 架构的指令集优化。
向量化编程(Vectorization)
向量化编程利用 SIMD(Single Instruction, Multiple Data)指令集,如 AVX2、AVX - 512 等,能够并行处理多个数据元素,从而大大提高计算密集型算法的执行效率。
编译器在一定程度上可以自动进行向量化优化,开发者可以通过检查-ftree - vectorize选项来开启或查看编译器的自动向量化功能。例如,使用 GCC 编译时加上-ftree - vectorize选项:
TypeScript
取消自动换行复制
gcc -O3 -ftree - vectorize -o algorithm algorithm.c
此外,开发者也可以手动编写内联汇编或使用编译器提供的 intrinsics 函数来实现向量化编程。以使用 AVX2 指令集进行简单的向量加法为例,使用 intrinsics 函数的示例代码如下:
TypeScript
取消自动换行复制
#include <immintrin.h>
#include <stdio.h>
void vector_add(float *a, float *b, float *result, int n) {
int i;
for (i = 0; i < n; i += 8) {
__m256 va = _mm256_loadu_ps(a + i);
__m256 vb = _mm256_loadu_ps(b + i);
__m256 vr = _mm256_add_ps(va, vb);
_mm256_storeu_ps(result + i, vr);
}
}
int main() {
const int n = 16;
float a[n] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f};
float b[n] = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f, 16.0f};
float result[n];
vector_add(a, b, result, n);
for (int i = 0; i < n; i++) {
printf("%f ", result[i]);
}
printf("\n");
return 0;
}
上述代码中,通过_mm256_loadu_ps、_mm256_add_ps和_mm256_storeu_ps等 intrinsics 函数,利用 AVX2 指令集并行地对两个浮点数数组进行加法运算,相比传统的循环加法,性能有显著提升。
多线程与并行计算
Linux 系统对多线程和并行计算提供了良好的支持,利用多核处理器的并行计算能力可以极大地加速算法的执行。
在 C++ 中,可以使用 C++11 引入的线程库来实现多线程编程。例如,下面是一个简单的多线程计算数组元素和的示例代码:
TypeScript
取消自动换行复制
#include <iostream>
#include <thread>
#include <vector>
void sum_array_part(const std::vector<int>& arr, int start, int end, int& partial_sum) {
partial_sum = 0;
for (int i = start; i < end; ++i) {
partial_sum += arr[i];
}
}
int main() {
const int num_threads = 4;
const int arr_size = 1000000;
std::vector<int> arr(arr_size);
for (int i = 0; i < arr_size; ++i) {
arr[i] = i + 1;
}
std::vector<std::thread> threads;
std::vector<int> partial_sums(num_threads, 0);
int step = arr_size / num_threads;
for (int i = 0; i < num_threads; ++i) {
int start = i * step;
int end = (i == num_threads - 1)? arr_size : (i + 1) * step;
threads.emplace_back(sum_array_part, std::ref(arr), start, end, std::ref(partial_sums[i]));
}
for (auto& thread : threads) {
thread.join();
}
int total_sum = 0;
for (int sum : partial_sums) {
total_sum += sum;
}
std::cout << "Total sum: " << total_sum << std::endl;
return 0;
}
上述代码将数组分成多个部分,每个部分由一个线程进行求和计算,最后将各个部分的和累加得到最终结果,充分利用了多核处理器的并行计算能力,相比单线程计算大大提高了计算速度。
此外,还可以使用 OpenMP 等并行计算框架来简化并行程序的开发。OpenMP 提供了一系列的编译指导语句,使得开发者可以轻松地将串行代码转换为并行代码。例如,使用 OpenMP 对上述数组求和代码进行改写:
TypeScript
取消自动换行复制
#include <iostream>
#include <vector>
#include <omp.h>
int main() {
const int arr_size = 1000000;
std::vector<int> arr(arr_size);
for (int i = 0; i < arr_size; ++i) {
arr[i] = i + 1;
}
int total_sum = 0;
#pragma omp parallel for reduction(+ : total_sum)
for (int i = 0; i < arr_size; ++i) {
total_sum += arr[i];
}
std::cout << "Total sum: " << total_sum << std::endl;
return 0;
}
在上述代码中,通过#pragma omp parallel for reduction(+ : total_sum)这条 OpenMP 指导语句,编译器会自动将循环并行化,各个线程并行地计算数组元素的和,并通过reduction子句将各个线程的部分和累加起来得到最终结果,大大简化了并行程序的编写过程。
总结
在 Linux 系统中实现和优化算法需要综合运用系统特性和各种工具。通过合理利用内存管理机制、CPU 亲和性以及文件 I/O 优化技巧,可以有效提升算法在数据处理和资源利用方面的效率。同时,借助性能剖析工具如perf和valgrind,以及编译器优化选项和向量化编程等代码优化技巧,能够深入分析性能瓶颈并针对性地进行优化。此外,充分发挥 Linux 系统对多线程和并行计算的支持,利用多核处理器的性能优势,能够显著加速算法的执行。掌握这些在 Linux 系统中的算法技巧,对于开发者提升程序性能、高效解决实际问题具有重要意义,有助于在各种计算场景中充分发挥 Linux 系统的强大功能。
相关文章:

Linux 系统中的算法技巧与性能优化
引言 Linux 系统以其开源、稳定和高度可定制的特性,在服务器端、嵌入式设备以及开发环境中得到了极为广泛的应用。对于开发者而言,不仅要掌握在 Linux 环境下实现各类算法的方法,更要知晓如何利用系统特性对算法进行优化,以提升…...

【C++系列】模板类型特例化
1. C模板类型特例化介绍 定义:模板类型特例化(Template Specialization)是C中为模板的特定类型提供定制实现的机制,允许开发者对通用模板无法处理的特殊类型进行优化或特殊处理。 产生标准: C98/03…...

K8S认证|CKS题库+答案| 7. Dockerfile 检测
目录 7. Dockerfile 检测 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、修改 Dockerfile 3)、 修改 deployment.yaml 7. Dockerfile 检测 免费获取并激活 CKA_v1.31_模拟系统 题目 您必须在以…...
JAVA 对象 详解
对象 对象结构: 对象头(元数据和指向class的指针)、实例数据、对齐填充 数组对象: 对象头(元数据和指向class的指针)、数组长度、数组数据、对齐填充 对象创建: 一、当Java虚拟机遇到一条…...
MATLAB实战:四旋翼姿态控制仿真方案
以下是一个基于MATLAB/Simulink的四旋翼姿态控制仿真方案。本方案使用简化姿态动力学模型,并设计PID控制器进行稳定控制。 1. 四旋翼姿态动力学模型 核心方程:I * ω̇ ω (I * ω) τ 其中: I diag([Ixx, Iyy, Izz]) 为转动惯量矩阵 …...

基于Scala实现Flink的三种基本时间窗口操作
目录 代码结构 代码解析 (1) 主程序入口 (2) 窗口联结(Window Join) (3) 间隔联结(Interval Join) (4) 窗口同组联结(CoGroup) (5) 执行任务 代码优化 (1) 时间戳分配 (2) 窗口大小 (3) 输出格式…...

c++对halcon的动态链接库dll封装及调用(细细讲)
七个部分(是个大工程) 一,halcon封装函数导出cpp的内容介绍 二,c++中对halcon环境的配置 三,在配置环境下验证halcon代码 四,dll项目创建+环境配置 五,编辑dll及导出 六,调用打包好的动态链接库的配置 七,进行测试 一,halcon的封装及导出cpp的介绍 1,我这里…...

【优选算法】分治
一:颜色分类 class Solution { public:void sortColors(vector<int>& nums) {// 三指针法int n nums.size();int left -1, right n, i 0;while(i < right){if(nums[i] 0) swap(nums[left], nums[i]);else if(nums[i] 2) swap(nums[--right], num…...
QGraphicsView中鼠标点击与移动事件传递给MainWindow
在Qt图形应用程序开发中,QGraphicsView和QGraphicsScene框架提供了强大的2D图形显示功能。然而,当我们需要在主窗口(MainWindow)中处理这些视图中的鼠标事件。 问题背景 在典型的Qt图形应用程序架构中: MainWindow └── QGraphicsView└── QGraphicsScene└── QGra…...

【图片识别改名】如何批量将图片按图片上文字重命名?自动批量识别图片文字并命名,基于图片文字内容改名,WPF和京东ocr识别的解决方案
应用场景 在日常工作和生活中,我们经常会遇到需要对大量图片进行重命名的情况。例如,设计师可能需要根据图片内容为设计素材命名,文档管理人员可能需要根据扫描文档中的文字对图片进行分类命名。传统的手动重命名方式效率低下且容易出错&…...

RabbitMQ 的高可用性
RabbitMQ 是比较有代表性的,因为是基于主从(非分布式)做高可用的RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模式。 单机模式 单机模式,生产几乎不用。 普通集群模式(无高可用性) 普通集群模…...
DAY 48 随机函数与广播机制
知识点回顾: 随机张量的生成:torch.randn函数卷积和池化的计算公式(可以不掌握,会自动计算的)pytorch的广播机制:加法和乘法的广播机制 ps:numpy运算也有类似的广播机制,基本一致 作…...
计算机基础知识(第五篇)
计算机基础知识(第五篇) 架构演化与维护 软件架构的演化和定义 软件架构的演化和维护就是对架构进行修改和完善的过程,目的就是为了使软件能够适应环境的变化而进行的纠错性修改和完善性修改等,是一个不断迭代的过程࿰…...
从零开始制作小程序简单概述
以下是结合案例的“从零制作小红书风格小程序”的全流程指南,采用小红书爆款笔记的结构呈现,并附CSDN参考资源👇: 一、核心开发步骤(附工具推荐) 账号与定位 ✅ 注册类型选择:个人店(…...

AI架构师修炼之道
1 AI时代的架构革命 与传统软件开发和软件架构师相比,AI架构师面临着三重范式转换: 1.1 技术维度,需处理异构算力调度与模型生命周期管理的复杂性; 1.2 系统维度,需平衡实时性与资源约束的矛盾; 1.3 价…...
三十五、面向对象底层逻辑-Spring MVC中AbstractXlsxStreamingView的设计
在Web应用开发中,大数据量的Excel导出功能是常见需求。传统Apache POI的XSSF实现方式在处理超大数据集时,会因全量加载到内存导致OOM(内存溢出)问题。Spring MVC提供的AbstractXlsxStreamingView通过流式处理机制,有效…...
Unity的日志管理类
脚本功能: 1,打印日志到控制台 2,显示日志到UI Text 3,将日志写入本地文件 这对unity开发安卓平台来说很有用 using System; using System.IO; using System.Text; using UnityEngine; using UnityEngine.UI;public class FileLo…...
【PhysUnits】17.2 配套变量结构体 Var(variable.rs)
一、源码 这段代码定义了一个泛型结构体 Var,用于封装数值类型并提供各种运算操作。 /** 变量结构体 Var* 该结构体泛型参数 T 需满足 Numeric 约束*/use core::ops::{Neg, Add, Sub, Mul, Div, AddAssign, SubAssign, MulAssign}; use crate::constant::Integer;…...

iview组件库:当后台返回到的数据与使用官网组件指定的字段不匹配时,进行修改某个属性名再将response数据渲染到页面上的处理
1、需求导入 当存在前端需要的数据的字段渲染到表格或者是一些公共的表格组件展示数据时的某个字段名与后台返回的字段不一致时,那么需要前端进行稍加处理,而不能直接this.list res.data;这样数据是渲染不出来的。 2、后台返回的数据类型 Datalist(pn) …...

服务器 | Centos 9 系统中,如何部署SpringBoot后端项目?
系列文章目录 虚拟机 | Ubuntu 安装流程以及界面太小问题解决 虚拟机 | Ubuntu图形化系统: open-vm-tools安装失败以及实现文件拖放 虚拟机 | Ubuntu操作系统:su和sudo理解及如何处理忘记root密码 文章目录 系列文章目录前言一、环境介绍二、 使用syst…...
qt network 整体框架
以下是 Qt 网络模块中 QNetworkInterface、TCP、UDP 及相关类的层次关系图及说明: 一、Qt 网络模块层次结构 ┌─────────────────────────────────────────────────────────────┐ │ QtNetwork 模…...
C++ map基础概念、map对象创建、map赋值操作、map大小操作、map数据插入、map数据删除、map数据修改、map数据统计
map的使用频率很高,仅次于vector,先了解下pair的概念: pair 概念: template<class _Ty1, class Ty2> struct pair{ _Ty1 first; // 这两个可以是任意的类型 _Ty2 second; }; eg:pair<int, int> p(13,…...

(2025)Windows修改JupyterNotebook的字体,使用JetBrains Mono
(JetBrains Mono字体未下载就配置,这种情况我不知道能不能行,没做过实验,因为我电脑已经下载了,不可能删了那么多字体做实验,我的建议是下载JetBrains Mono字体,当你使用VsCode配置里面的JetBrains字体也很有用) 首先参考该文章下载字体到电脑上 VSCode 修改字体为JetBrains …...

小番茄C盘清理:专业高效的电脑磁盘清理工具
在使用电脑的过程中,我们常常会遇到系统盘空间不足、磁盘碎片过多、垃圾文件堆积等问题,这些问题不仅会导致电脑运行缓慢,还可能引发系统崩溃。为了解决这些问题,小番茄C盘清理应运而生。它是一款专业的C盘清理软件,能…...
CSS 预处理器与工具
目录 CSS 预处理器与工具1. Less主要特性 2. Sass/SCSS主要特性 3. Tailwind CSS主要特性 4. 其他工具PostCSSCSS Modules 5. 选择建议 CSS 预处理器与工具 1. Less Less 是一个 CSS 预处理器,它扩展了 CSS 语言,添加了变量、嵌套规则、混合࿰…...

AUTOSAR实战教程--标准协议栈实现DoIP转DoCAN的方法
目录 软件架构 关键知识点 第一:PDUR的缓存作用 第二:CANTP的组包拆包功能 第三:流控帧的意义 配置过程 步骤0:ECUC模块中PDU创建 步骤1:SoAD模块维持不变 步骤2:DoIP模块为Gateway功能添加Connection 步骤3:DoIP模块为Gateway新增LA/TA/SA 步骤4:PDUR模…...

【MySQL系列】MySQL 导出表数据到文件
博客目录 一、使用 SELECT INTO OUTFILE 语句基本语法参数详解注意事项实际示例 二、使用 mysqldump 工具基本语法常用选项实际示例 三、使用 MySQL Workbench 导出导出步骤高级选项 四、其他导出方法1. 使用 mysql 命令行客户端2. 使用 LOAD DATA INFILE 的逆向操作3. 使用编程…...

vue3:十五、管理员管理-页面搭建
一、页面效果 实现管理员页面,完成管理员对应角色的中文名称显示,实现搜索栏,表格基本增删改查,分页等功能 二、修改问题 1、修改搜索框传递参数问题 (1)问题图示 如下图,之前搜索后,传递的数据不直接是一个value值,而是如下图的格式 查询可知这里传递的数据定义的是…...
学习使用YOLO的predict函数使用
YOLO的 result.py #2025.1.3 """ https://docs.ultralytics.com/zh/modes/predict/#inference-arguments 对yolo 目标检测、实例分割、关键点检测结果进行说明https://docs.ultralytics.com/reference/engine/results/#ultralytics.engine.results.Masks.xy 对…...
零基础在实践中学习网络安全-皮卡丘靶场(第十四期-XXE模块)
本期内容涉及到很多前面的内容,因此复习后可以更好的了解本期内容 介绍 XXE -"xml external entity injection"即"xml外部实体注入漏洞"。 概括一下就是"攻击者通过向服务器注入指定的xml实体内容,从而让服务器按照指定的配置进行执行,导…...