CPU数据预取对软件性能的影响

2023-11-14

一、什么是预取

预取是指将内存中的指令和数据提前存放到cache(L1、L2、L3)中,从而加快处理器执行速度。

Cache预取可以通过硬件或者软件实现,也就是分为硬件预取和软件预取两类。

  • 硬件预取,是通过处理器中专门的硬件来实现的,该硬件监控正在执行程序中请求的指令或数据,识别下一个程序需要的流然后预取到处理器中。
  • 软件预取,是通过编译器分析代码然后在程序编译的过程中插入prefetch。这样在执行过程中在指定位置就会进行预取的动作。

本文讨论的预取指软件预取。

二、使用_mm_prefetch预取

win10下,vs2017直接#include <Windows.h>就可以使用此函数了。

函数原型如下:

void _mm_prefetch(char const *p, int sel);

从地址p处预取大小为cache line的一块数据至缓存。

参数sel指示预取方式:

  • _MM_HINT_T0,预取数据到所有缓存
  • _MM_HINT_T1,预取到L2,L3缓存,但是不到L1缓存
  • _MM_HINT_T2,仅预取数据到L3缓存
  • _MM_HINT_NTA,预取数据到非临时缓冲结构中,可以最小化对缓存的污染。

如果在CPU操作数据之前,我们就已经将数据主动加载到缓存中,那么就减少了由于缓存不命中,需要从内存取数的情况,这样就可以加速操作,获得性能上提升。使用主动缓存技术来优化内存拷贝。

我们编写一段测试代码,用来测试数据预取与不预取2种情况下的区别。

prefetchTest.cpp

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <Windows.h>
#include <emmintrin.h>

#define INT_COUNT        (5*1024*1024)

inline int calculate(int input)
{
	int val = (input % 99) * (input / 98);
	val = val ? val : 1;
	double n = sqrt(sqrt((double)(unsigned)input * 1.3));
	double m = sqrt(sqrt((double)(unsigned)val * 0.9));
	return (int)((double)input * (double)val * m / (n ? n : 1.1));
}

int run_withprefetch(const int *array, int size, int step, int prefetch)
{
	int result = 0;
	printf("run with prefetch(%d)...\n", prefetch);
	for (int i = 0; i < step; i++) 
	{
		for (int j = i; j < size; j += step) 
		{
			int k = j + step * prefetch;
			if (k < size) 
			{
				_mm_prefetch((char*)&array[k], _MM_HINT_T0);
//				_mm_clflush(&array[k]);
			}
			result += calculate(array[j]);
		}
	}
	return result;
}

int run(const int *array, int size, int step)
{
	int result = 0;
	printf("run...\n");
	for (int i = 0; i < step; i++) 
	{
		for (int j = i; j < size; j += step) 
		{
			result += calculate(array[j]);
		}
	}
	return result;
}

int main()
{
	int select;
	scanf("%d", &select);

	int* array = new int[INT_COUNT];
	for (int i = 0; i < INT_COUNT; i++)
	{
		array[i] = i;
	}
	long t1 = GetTickCount();

	int result;
	if (select == 0)
	{
		result = run(array, INT_COUNT, 1024);
	}
	else
	{
		result = run_withprefetch(array, INT_COUNT, 1024, 1);
	}

	long t2 = GetTickCount();
	std::cout << (t2 - t1) << "ms" << std::endl;
	std::cout << "result:" << result << std::endl;
	delete[] array;
}

编译执行,输入0表示执行不预取的计算逻辑,输出结果如下:

在这里插入图片描述

可以看到计算完毕消耗687ms。

重新执行,输入1表示执行预取计算逻辑,输出结果如下:

在这里插入图片描述

可以看到计算完毕消耗391ms,性能得到大幅提升。这就是数据预取的威力。

三、使用_mm_clflush清除cache line

win10下,vs2017直接#include <emmintrin.h>就可以使用此函数了。

函数原型如下:

void _mm_clflush(void const* p);

此函数可以手动清除p地址处cache line缓存(清除大小为line size)。

我们打开prefetchTest.cpp中“_mm_clflush(&array[k]);”代码的注释。
这样的话,就是先预取数据,然后立马反悔,清除cache line缓存,相当于是没有预取。

编译执行,输入1,输出结果如下:

在这里插入图片描述

可以看到计算完毕消耗937ms。相比于不预取时的687ms,反而更加耗时了,可能是因为手动清除cache line比较影响性能吧。

四、总结

注 意,CPU对数据操作拥有绝对自由!使用预取指令只是按我们自己的想法对CPU的数据操作进行补充,有可能CPU当前并不需要我们加载到缓存的数据,这样,我们的预取指令可能会带来相反的结果,比如对于多任务系统,有可能我们冲掉了有用的缓存。不过,在多任务系统上,由于线程或进程的切换所花费的时间相对于预取操作来说太长了,所以可以忽略线程或进程切换对缓存预取的影响。

另外,数据预取只对那些内存读取是它瓶颈的程序才能起到很好的优化,毕竟只是加快了内存访问速度而已。那些对内存性能要求不高,对计算复杂度较高的程序,可能效果就不会那么明显。还有一些计算复杂度和内存性能要求都不高的程序,预取与不预取可能效果也不明显。

参考链接:

《CPU预取与性能简介》

《memory prefetch浅析》

《Intel 平台编程总结----缓存优化之数据预取》

《SSE中使用_mm_prefetch加速计算》

《clflush通过C函数使缓存行无效》



若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!

同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。

本文涉及工程代码,公众号回复:11PrefetchAndClflushTest,即可下载。

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CPU数据预取对软件性能的影响 的相关文章

  • R语言学习笔记:分析学生的考试成绩

    孩子上初中时拿到过全年级一次考试所有科目的考试成绩表 正好可以用于R语言的统计分析学习 为了不泄漏孩子的姓名 就用学号代替了 感兴趣可以下载测试数据进行练习 num class chn math eng phy chem politics
  • 范围for语句

    C 新标准提供的范围for语句 这种语句遍历给定序列中个元素并对序列中每一个值执行某种操作 其语法形式是 for declaration expression statement 其中 expression 部分是一个对象 用于表示一个序列
  • Vc/MFC中自定义消息及其PostMessage触发使用

    http blog csdn net ztz0223 article details 2058402 http blog csdn net a8082649 article details 7733527 http bbs csdn net
  • 编写程序模拟完成动态分区存储管理方式的内存分配和回收。

    usr bin python coding utf 8 class Table object 空闲分区表 0 开始地址 1 长度 freeTable 占用分区表 0 程序名 1 开始地址 2 长度 useTable def init sel
  • GDAL多光谱与全色图像融合简单使用

    目录 简述 C 代码 效果对比 GDAL融合效果和原始多光谱波段对比 GDAL融合效果和原始全色波段对比 ARCGIS融合效果与原始全色和多光谱对比 GDAL融合效果与ArcGIS融合效果对比 简述 最近在GDAL的代码中看见了gdalpa
  • 为何在新建STM工程中全局声明两个宏

    在uVision中新建STM32工程后 需要从STM32标准库中拷贝标准外设驱动到自己的工程目录中 此时需要在工程设置 gt C C 选项卡下的Define文本框中键入这两个全局宏定义 STM32F40 41xxx USE STDPERIP
  • c语言判断一个数是否为偶数

    include
  • R----dplyr包介绍学习

    dplyr包 plyr包的替代者 专门面对数据框 将ddplyr转变为更易用的接口 gt 来自dplyr包的管道函数 其作用是将前一步的结果直接传参给下一步的函数 从而省略了中间的赋值步骤 可以大量减少内存中的对象 节省内存 可惜的是应用范
  • 模板的完全特例化和部分特例化

    介绍 完全特例化就是类型完全明确的版本 而部分特例化指的是 只知道是几个参数的函数而不知道参数的类型 或者是只知道是引用或者是指针类型 而不知道具体是char 还是 int 模板特例化实例1 template
  • stat 函数解析

    stat 函数的简单使用 stat 函数是用来获取文件的各种属性的一个linux下的常用API函数 函数原型为int stat const char path struct stat buf stat定义如下 struct stat dev
  • 一个简单的参数帮助框架,c实现

    文章目录 具体实现如下 include
  • enable_shared_from_this使用介绍

    文章目录 enable shared from this定义 使用场合 源码实现 注意 enable shared from this定义 定义于头文件 template lt class T gt class enable shared
  • 【数据结构/C++】树和二叉树_二叉链表

    include
  • C 语言教程:数据类型和格式说明符

    C 语言中的数据类型 C 中的变量必须是指定的 数据类型 并且您必须在 printf 函数中使用 格式说明符 来显示它 创建变量 int myNum 5 整数 没有小数点 float myFloatNum 5 99 浮点数 char myL
  • C/C++编程:令人印象深刻的高级技巧案例

    C C 编程语言在软件开发领域有着悠久的历史 由于其高效 灵活和底层访问能力 至今仍然被广泛应用 本文将介绍一些在C C 编程中令人印象深刻的高级技巧 帮助读者提升编程水平 更加高效地使用这两种强大的编程语言 一 指针运算与内存管理 C C
  • C/C++编程中的算法实现技巧与案例分析

    C C 编程语言因其高效 灵活和底层的特性 被广大开发者用于实现各种复杂算法 本文将通过10个具体的算法案例 详细探讨C C 在算法实现中的技巧和应用 一 冒泡排序 Bubble Sort 冒泡排序 Bubble Sort 是一种简单的排序
  • C++ 字符串比较------strcmp函数和strncmp函数

    strcmp 函数原型 int strcmp const char str1 const char str2 功能 strcmp函数会按照字典顺序逐个比较两个字符串的字符 直到遇到不同的字符或者遇到字符串结束符 0 返回值 该函数返回值如下
  • C 语言运算符详解

    C 语言中的运算符 运算符用于对变量和值进行操作 在下面的示例中 我们使用 运算符将两个值相加 int myNum 100 50 虽然 运算符通常用于将两个值相加 就像上面的示例一样 它还可以用于将变量和值相加 或者将变量和另一个变量相加
  • C++中的引用

    一 引用的概念 引用不是新定义一个变量 而是给已有变量取一个别名 编译器不会为引用变量开辟内存空间 而和它引用的变量共用一块内存空间 注意 由于C 兼容C 所以 既可以是引用符号 也可以是取地址 int a 0 int b a cout l
  • Woocommerce:添加第二个电子邮件地址不起作用,除非收件人是管理员

    我尝试了多种方法来向 Woocommerce 电子邮件添加其他收件人 但它似乎仅适用于主要收件人是管理员的测试订单 这些是我尝试过的片段 如果订单的客户是管理员 则电子邮件将发送到这两个地址 如果订单包含客户电子邮件地址 则仅发送至该电子邮

随机推荐

  • PTA 1074 宇宙无敌加法器(Python3)

    地球人习惯使用十进制数 并且默认一个数字的每一位都是十进制的 而在 PAT 星人开挂的世界里 每个数字的每一位都是不同进制的 这种神奇的数字称为 PAT数 每个 PAT 星人都必须熟记各位数字的进制表 例如 0527 就表示最低位是 7 进
  • 内容安全策略 Content-Security-Policy

    一 作用 1 限制资源获取 限制网页当中一系列的资源获取的情况 从哪里获取 请求发到哪个地方 限制方式 default src限制全局的和链接有关的作用范围 根据资源类型 connect src img src等 限制资源范围 2 报告资源
  • kotlin编译报错问题和解决方案

    IDEA 编译springboot项目报错 Error Kotlin Module was compiled with an incompatible version of Kotlin The binary version of its
  • 网络同步与异步概念整理

    在网络同步中 有两种同步方式 分别为同步与异步 同步的操作指的是 当所有的操作请求都做完 才将结果返回给用户 用户才能进行下一个操作 这样就会让用户有一种卡顿的感觉 因为需要等待上一步操作的执行结果 异步操作指的是 用户的操作之间不需要进行
  • git相关命令

    git命令 首先在github页面上创建一个自己的分支 new branch 命名branch 1 git clone git 克隆项目 2 git init 生成本地 git 文件 3 git add 添加需要上传的文件 4 git co
  • 什么是Docker容器?一文带你了解,看完直接学会

    一 为什么需要Docker容器 1 引入 1 1麻烦的环境部署 1 在软件开发中 最麻烦的事情之一就是环境配置 在正常情况下 如果要保证程序能运行 我们需要设置好操作系统 以及各种库和组件的安装 2 举例来说 要运行一个Python程序 计
  • 双重求和∑∑的定义及性质

    目录 一 复习求和符号 二 二重求和的定义 三 双重求和 交换求和顺序 一 复习求和符号 自从约瑟夫 傅立叶于1820年引入求和符号 大写的希腊字母sigma 以来 求和 以及双重求和 在数学公式推导 命题证明中被经常使用 掌握它的定义和性
  • Linux笔记——chapter1 从登陆Linux开始

    1 1 X Window与命令行模式 X Window 图形化界面 CentOS默认至少提供GNOME KDE这两种图形界面 命令行模式 纯文本界面 通常我们也称命令行模式为终端界面 terminal或console 登录模式的切换 Lin
  • 【C++】-- 高并发内存池

    高并发内存池 项目介绍 池化技术 内存池 定长内存池的实现 整体框架 threadcache threadcache整体设计 threadcache哈希桶映射对齐规则 TLS无锁访问 centralcache centralcache整体设
  • React ajax

    目录 前置说明 常用的ajax请求库 axios 相关API 配置代理 方法一 配置代理 方法二 案例 github用户搜索 ES6小知识点 解构赋值 重命名 消息订阅与发布机制 fetch发送请求 关注分离的设计思想 list组件 sea
  • 计算机组成原理(一)

    文章目录 常识 第一章 进制转换 1 10与2进制互转 法一 短除法 法二 按权展开法表示十进制 2 10转其他进制 3 2转8进制 4 8转2进制 5 2转16进制 6 16转2进制 第二章 一 数据类型 原码 补码 1 模 2 n 1次
  • MySQL8.0.19修改root密码

    在MySQL 8 04前 执行 SET PASSWORD PASSWORD 新密码 但是MySQL8 0 4开始 这样默认是不行的 因为之前 MySQL的密码认证插件是 mysql native password 而现在使用的是 cachi
  • Python打包成exe文件操作

    脚本一般都会用到一些第三方包 比如开发的脚本或小工具 如果发送别人是不能直接用的 他还需要安装python解释器 甚至还要安装我们用的那些第三方包太麻烦了 我们这边直接打包成exe文件可供于别人使用 会更加方便 具体操作 1 首先就是安装p
  • 分苹果问题

    题目大意 有N个苹果 要把这些苹果粉给2个人 使得这两个人得到的苹果重量差最小 先求得N个苹果的重量总和 分成两堆 差值最小 则有一堆大于或等于SUM 2 有一堆小于等于SUM 2 所以有for j sum 2 j gt w i j 只要d
  • error C1076: compiler limit: internal heap limit reached 【UE4出现C1076错误的解决方法】

    如果编译后出现以下问题 导致这个问题的原因是 预分配 头内存不足 可以通过 Zm114 多分配一些
  • docker下交叉编译环境配置

    为什么在docker中搭建开发环境 Docker 是一个开源的应用容器引擎 让开发者可以打包他们的应用以及依赖包到一个可移植的容器中 然后发布到任何流行的 Linux 机器上 也可以实现虚拟化 容器是完全使用沙箱机制 相互之间不会有任何接口
  • matlab数学实验 课件,MATLAB数学实验课件.PPT

    摘要 第一章 Matlab入门 MATLAB数学实验 第八章 随机模拟和统计分析 第八章 随机模拟和统计分析 8 1 预备知识 概率和统计 8 2 概率和统计的MATLAB指令 8 3 计算实验 随机模拟 Monte Carlo算法 8 4
  • [Koishi] 实现简易QQ机器人

    以前使用的QQ机器人是千寻Bot为基础框架的 配置环境相较于Koishi复杂得多 在此记录一下使用Koishi的踩坑过程 目录 1 软件下载与安装 1 1下载 1 2安装 2 插件 2 1插件安装 2 2插件更新 2 3插件配置 2 3 1
  • python从MySQL数据库中读取数据

    import pymysql 连接数据库 link pymysql connect host 127 0 0 1 连接地址 连接本地默认 127 0 0 1 user root 用户名 passwd 密码 port 3306 端口 默认为3
  • CPU数据预取对软件性能的影响

    一 什么是预取 预取是指将内存中的指令和数据提前存放到cache L1 L2 L3 中 从而加快处理器执行速度 Cache预取可以通过硬件或者软件实现 也就是分为硬件预取和软件预取两类 硬件预取 是通过处理器中专门的硬件来实现的 该硬件监控