CPU合并写缓冲区简介
现代CPU采用了大量的技术来抵消内存访问带来的延迟。读写内存数据期间,CPU能执行成百上千条指令。
多级SRAM(Static Random-Access Memory 用作CPU的Cache)缓存是减小这种延迟带来的影响的主要手段。此外,SMP-对称多处理(Symmetrical Multi-Processing)系统采用消息传递协议来实现缓存之间的一致性。遗憾的是,现代的CPU实在是太快了,即使是使用了缓存,有时也无法跟上CPU的速度。因此,为了进一步减小延迟的影响,一些鲜为人知的缓冲区派上了用场。
本文将探讨“合并写存储缓冲区(write combining store buffers)”,以及如何写出有效利用它们的代码。
CPU缓存是一种高效的非链式结构的hash map,每个桶(bucket)通常是64个字节。这就是一个“缓存行(cache line)”。缓存行是内存交换的实际单位。例如,主存中地址A会映射到一个给定的缓存行C。
如果CPU需要访问的地址hash后的行尚不在缓存中,那么缓存中对应位置的缓存行会被清除,以便载入新的行。例如,如果我们有两个地址,通过hash算法hash到同一缓存行,那么新的值会覆盖老的值。
当CPU执行存储指令(store)时,它会尝试将数据写到离CPU最近的L1缓存。如果此时出现缓存未命中,CPU会访问下一级缓存。此时,无论是英特尔还是许多其它厂商的CPU都会使用一种称为“合并写(write combining)”的技术。
在请求L2缓存行的所有权尚未完成时,待存储的数据被写到处理器自身的众多跟缓存行一样大小的存储缓冲区之一。这些芯片上的缓冲区允许CPU在缓存子系统准备好接收和处理数据时继续执行指令。当数据不在任何其它级别的缓存中时,将获得最大的优势。
当后续的写操作需要修改相同的缓存行时,这些缓冲区变得非常有趣。在将后续的写操作提交到L2缓存之前,可以进行缓冲区写合并。 这些64字节的缓冲区维护了一个64位的字段,每更新一个字节就会设置对应的位,来表示将缓冲区交换到外部缓存时哪些数据是有效的。
也许你要问,如果程序要读取已被写入缓冲区的某些数据,会怎么样?我们的硬件工程师已经考虑到了这点,在读取缓存之前会先去读取缓冲区的。
这一切对我们的程序意味着什么?
如果我们能在缓冲区被传输到外部缓存之前将其填满,那么将大大提高各级传输总线的效率。如何才能做到这一点呢?好的程序将大部分时间花在循环处理任务上。
这些缓冲区的数量是有限的,且随CPU模型而异。例如在Intel CPU中,同一时刻只能拿到4个。这意味着,在一个循环中,你不应该同时写超过4个不同的内存位置,否则你将不能享受到合并写(write combining)的好处。
public final class WriteCombining {private static final int ITERATIONS = Integer.MAX_VALUE;private static final int ITEMS = 1 << 24;private static final int MASK = ITEMS - 1;private static final byte[] arrayA = new byte[ITEMS];private static final byte[] arrayB = new byte[ITEMS];private static final byte[] arrayC = new byte[ITEMS];private static final byte[] arrayD = new byte[ITEMS];private static final byte[] arrayE = new byte[ITEMS];private static final byte[] arrayF = new byte[ITEMS];public static void main(final String[] args) {for (int i = 1; i <= 3; i++) {out.println(i + " SingleLoop duration (ns) = " + runCaseOne());out.println(i + " SplitLoop duration (ns) = " + runCaseTwo());}int result = arrayA[1] + arrayB[2] + arrayC[3] + arrayD[4] + arrayE[5] + arrayF[6];out.println("result = " + result);}public static long runCaseOne() {long start = System.nanoTime();int i = ITERATIONS;while (--i != 0) {int slot = i & MASK;byte b = (byte) i;arrayA[slot] = b;arrayB[slot] = b;arrayC[slot] = b;arrayD[slot] = b;arrayE[slot] = b;arrayF[slot] = b;}return System.nanoTime() - start;}public static long runCaseTwo() {long start = System.nanoTime();int i = ITERATIONS;while (--i != 0) {int slot = i & MASK;byte b = (byte) i;arrayA[slot] = b;arrayB[slot] = b;arrayC[slot] = b;}i = ITERATIONS;while (--i != 0) {int slot = i & MASK;byte b = (byte) i;arrayD[slot] = b;arrayE[slot] = b;arrayF[slot] = b;}return System.nanoTime() - start;}
}
这个程序在我的Windows 7 64位英特尔酷睿i7860@2.8 GHz系统上产生的输出如下:
1 SingleLoop duration (ns) = 140197535451 SplitLoop duration (ns) = 89723686612 SingleLoop duration (ns) = 141624550662 SplitLoop duration (ns) = 88876105583 SingleLoop duration (ns) = 138009147253 SplitLoop duration (ns) = 7271752889
上面的例子说明:如果在一个循环中修改6个数组位置(内存地址),程序的运行时间明显长于将任务拆分的方式,即,先写前3个位置,再修改后3个位置。
通过拆分循环,我们做了更多的工作,但程序花费的时间更少!欢迎利用神奇的“合并写(write combining)”。通过使用CPU架构的知识,正确的填充这些缓冲区,我们可以利用底层硬件加速我们的程序。
不要忘了超线程(hyper-threading),可能会有2个线程竞争同一个核的缓冲区。
CPU合并写缓冲区简介相关推荐
- Java程序员需要掌握的计算机底层知识(一):CPU基本组成、指令乱序执行、合并写技术、非同一访问内存 NUMA
一些书籍 读书的原则:不求甚解,观其大略 你如果进到庐山里头,二话不说,蹲下头来,弯下腰,就对着某棵树某棵小草猛研究而不是说先把庐山的整体脉络跟那研究清楚了,那么你的学习方法肯定效率巨低而且特别痛苦, ...
- 合并写(write combining)
现代CPU采用了大量的技术来抵消内存访问带来的延迟.读写内存数据期间,CPU能执行成百上千条指令. 多级SRAM缓存是减小这种延迟带来的影响的主要手段.此外,SMP系统采用消息传递协议来实现缓存之间的 ...
- 5.合并写(write combining)[转载]
原文地址 译者:无叶 校对:丁一 现代CPU采用了大量的技术来抵消内存访问带来的延迟.读写内存数据期间,CPU能执行成百上千条指令. 多级SRAM缓存是减小这种延迟带来的影响的主要手段.此外,SMP系 ...
- JVM从入门到精通(三):热加载的实现原理,Java内存模型,缓存行,指令重排,合并写技术等
上节回顾:类加载机制 双亲委派机制 parent只是一个成员变量,不是继承关系. 上节课的遗留问题 parent是怎么指定的? 手动指定parent: 双亲委派机制可以被打破吗? 双亲委派机制是在Cl ...
- WC-Write Combining 合并写技术
WC-Write Combining 合并写技术 为了提高写效率: CPU在写入L1时,同时用WC写入L2 实验代码: public class WriteCombining {private sta ...
- 异构计算(CPU + GPU)编程简介
异构计算(CPU + GPU)编程简介 1.概念 所谓异构计算,是指CPU+ GPU或者CPU+ 其它设备(如FPGA等)协同计算.一般我们的程序,是在CPU上计算.但是,当大量的数据需要计算时,CP ...
- CPU+GPU异构计算编程简介
异构计算(CPU + GPU)编程简介 1. 概念 所谓异构计算,是指CPU+ GPU或者CPU+ 其它设备(如FPGA等)协同计算.一般我们的程序,是在CPU上计算.但是,当大量的数据需要计算时,C ...
- WCBuffer合并写
WCBuffer合并写: WriteCombliningBuffer的速度比L1还要快,只有4个字节的位置
- [c/c++]小议几个写缓冲区的函数
1. strncpy char *strncpy(char *dest, char *src, size_t num); strcpy没得说,它自动补NULL,而且你得给他足够大的空间,一般是读src ...
最新文章
- 计算机控制系统中的模拟输入通道需应用,远程西安交通大学17年3月课程考试《计算机控制技术》作业考核试题...
- 全新 DOCKER PALS 计划上线,带给您不一样的参会体验!...
- CodeForces - 55D Beautiful numbers
- python的celery的面试_python 面试
- [做题记录]AtCoder AGC做题记录
- Selenium自动写公众号文章
- 高性能分页REST API查询生成器
- 设计模式(五)学习----装饰模式
- CentOS7安装Oracle11.2.0.4
- Head first java(第一章进入Java的世界)
- java校园实习兼职
- python十六进制转换成八进制_怎么把十六进制转化为八进制
- php 判断是否是机器人,php实现判断访问来路是否为搜索引擎机器人的方法_PHP
- linux结束后台命令,linux 终端的后台任务结束详解
- 你都用 Python 来做什么 学Python能做什么
- PMP备考之路 - 敏捷实践第五讲(实施敏捷:在敏捷环境中交付)
- org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ‘****‘
- 让HR主动邀请你面试的简历,该如何写?
- windows10快速打开回收站(Recycle Bin)
- 【无限互联】mac系统读写NTFS
热门文章
- 异步fifo_同步FIFO设计实现
- mysql无法连接10061_Day062 连接数据库异常问题记录
- 根据类名找jar包findjar.com
- mysql并发死锁问题解决
- 数据库中表名、字段名、字符串大小写处理规则
- java+@api_java 常用API
- 计算机接口实验0832,现代计算机接口实验 (四)0832实验
- mysql主从io为no_mysql主从同步错误解决和Slave_IO_Running: NO
- 拉普拉斯变换公式表_复变函数之拉普拉斯变换小结
- NoClassDefFoundError: Could not initialize class com.fasterxml.jackson.databind.ObjectMapper