此循环在英特尔Conroe/Merom上每3个周期运行一次,imul按预期方式在吞吐量方面存在瓶颈.但是在Haswell/Skylake上,它每11个循环运行一次,显然是因为setnz al它依赖于最后一个循环imul.
; synthetic micro-benchmark to test partial-register renaming
mov ecx, 1000000000
.loop: ; do{
imul eax, eax ; a dep chain with high latency but also high throughput
imul eax, eax
imul eax, eax
dec ecx ; set ZF, independent of old ZF. (Use sub ecx,1 on Silvermont/KNL or P4)
setnz al ; ****** Does this depend on RAX as well as ZF?
movzx eax, al
jnz .loop ; }while(ecx);
Run Code Online (Sandbox Code Playgroud)
如果setnz al …
获取一个对齐的内存块有几个选项,但它们非常相似,问题主要归结为您所针对的语言标准和平台.
C11
void * aligned_alloc (size_t alignment, size_t size)
Run Code Online (Sandbox Code Playgroud)
POSIX
int posix_memalign (void **memptr, size_t alignment, size_t size)
Run Code Online (Sandbox Code Playgroud)
视窗
void * _aligned_malloc(size_t size, size_t alignment);
Run Code Online (Sandbox Code Playgroud)
当然,手动对齐也是一种选择.
英特尔提供另一种选择
英特尔
void* _mm_malloc (int size, int align)
void _mm_free (void *p)
Run Code Online (Sandbox Code Playgroud)
基于英特尔发布的源代码,这似乎是分配工程师喜欢的对齐内存的方法,但我找不到任何将其与其他方法进行比较的文档.我发现的最接近的只是承认存在其他对齐的内存分配例程.
要动态分配一段对齐的内存,请使用posix_memalign,它由GCC和Intel Compiler支持.使用它的好处是您不必更改内存处理API.您可以像往常一样使用free().但要注意参数配置文件:
int posix_memalign(void**memptr,size_t align,size_t size);
英特尔编译器还提供另一组内存分配API.C/C++程序员可以使用_mm_malloc和_mm_free来分配和释放对齐的内存块.例如,以下语句为8个浮点元素请求64字节对齐的内存块.
farray =(float*)__ mm_malloc(8*sizeof(float),64);
必须使用_mm_free释放使用_mm_malloc分配的内存.在使用_mm_malloc分配的内存上调用free或在使用malloc分配的内存上调用_mm_free将导致不可预测的行为.
从用户的角度来看,明显的区别是_mm_malloc需要直接的CPU和编译器支持以及分配的内存_mm_malloc必须被释放_mm_free.鉴于这些缺点,使用_mm_malloc?它的原因是什么?它有轻微的性能优势吗?历史事故?
我们一直是英特尔商店.所有开发人员都使用英特尔机器,最终用户的推荐平台是英特尔,如果最终用户希望在AMD上运行,那就是他们的了望.也许测试部门有一台AMD机器在哪里检查我们没有运送任何完全损坏的东西,但那是关于它的.
直到几年前我们才使用MSVC编译器,因为它并没有真正提供超出SSE级别的许多处理器调优选项,所以没有人担心代码是否有利于x86供应商而不是另一个.但是,最近我们一直在使用英特尔编译器.我们的东西肯定会从它(在我们的英特尔硬件上)获得一些显着的性能优势,并且它的矢量化功能意味着更少需要去asm/intrinsics.然而,人们开始对英特尔编译器是否真的不能为AMD硬件做得如此出色而感到有些紧张.当然,如果你进入英特尔CRT或IPP库,你会看到许多cpuid查询显然设置跳转表到优化的功能.看起来英特尔似乎不太可能为AMD芯片做任何好事.
任何有这方面经验的人都可以评论这在实践中是否是一个大问题?(我们还没有真正对AMD进行任何性能测试).
更新2010-01-04:支持AMD的需求从来没有变得足够让我自己做任何测试.还有在这个问题上一些有趣的阅读在这里,这里和这里虽然.
更新2010-08-09:看来英特尔-FTC和解有话要说这个问题-请参阅"编译器和肮脏的把戏"的部分文章.
在一个自学项目中,我借助以下代码测量内存的带宽(这里解释,整个代码在问题的最后跟随):
unsigned int doit(const std::vector<unsigned int> &mem){
const size_t BLOCK_SIZE=16;
size_t n = mem.size();
unsigned int result=0;
for(size_t i=0;i<n;i+=BLOCK_SIZE){
result+=mem[i];
}
return result;
}
//... initialize mem, result and so on
int NITER = 200;
//... measure time of
for(int i=0;i<NITER;i++)
resul+=doit(mem)
Run Code Online (Sandbox Code Playgroud)
BLOCK_SIZE以这种方式选择,每个整数加法获取整个64字节高速缓存行.我的机器(Intel-Broadwell)每个整数附加需要大约0.35纳秒,所以上面的代码可以使带宽饱和到高达182GB/s(这个值只是一个上限,可能很大,很重要的是不同尺寸的带宽比率).代码用g++和编译-O3.
改变向量的大小,我可以观察到L1(*) - ,L2-,L3-高速缓存和RAM-内存的预期带宽:
但是,我真的很难解释一下:L1高速缓存测量带宽的崩溃大小约为2 kB,这里的分辨率要高一些:
我可以在我有权访问的所有机器上重现结果(具有Intel-Broadwell和Intel-Haswell处理器).
我的问题:内存大小大约2 KB的性能崩溃是什么原因?
(*)我希望我理解正确,对于L1缓存而言,不是64字节,而是每次添加只有4个字节被读取/传输(没有更快的缓存,其中必须填充缓存行),因此L1的绘制带宽是只有上限而不是badwidth本身.
编辑:当选择内部for循环中的步长时
即当内环由约31-35步/读时组成时.这意味着崩溃不是由于内存大小,而是由于内循环中的步数.
可以通过分支未命中来解释,如@ user10605163的好答案所示.
列出重现结果
bandwidth.cpp:
#include <vector>
#include <chrono>
#include <iostream>
#include <algorithm>
//returns minimal …Run Code Online (Sandbox Code Playgroud) 我遇到了一个非常奇怪的问题,我试着寻找几天和几天的答案.我的游戏刚刚获得了一个新的粒子系统,但速度太慢而无法播放.不幸的是,BufferedImage转换非常慢.爆炸效果包括从.png文件加载的大约200个白色精灵,随机旋转,缩放和着色,以随机速度移动.
我试图通过三重/双重缓冲使性能更好,并遇到了一些问题.
我的第一次尝试是使用JPanel进行游戏.我在JFrame的类(Main)中设置缓冲区,然后在Game(扩展JPanel)类中完成绘图,但没有Graphics g = bufferstrategy.getDrawGraphics();. 然后,在绘图方法结束时,我显示缓冲区IF没有丢失.缓冲区总是"丢失",因为我没有使用它的Graphics对象进行绘图.但!游戏运行得和地狱一样快!在实际使用中没有缓冲!但是怎么样?
此尝试最终没有出现图形错误,并且具有巨大的性能提升 - 但仅限于nVidia/AMD显卡.英特尔GPU无法处理这个问题,屏幕闪烁白光.
所以,我最终设置并正确使用BufferStrategy.游戏类现在扩展画布,而不是JPanel中,作为正从一个JFrame的图形,并用它来画上一个JPanel在偏移结束了,因为它是在标题栏下绘制.仍然很快,修复60 FPS.
现在,当我在JFrame(Main类)中创建BufferStrategy时,根本没有图片.我通过在Game类(Canvas)中设置BufferStrategy来纠正这个问题.现在图片是正确的,但游戏本身就像蜗牛一样慢.一次爆炸使FPS下降至~10,但仅限于nVidia/AMD.具有讽刺意味的.即使是旧的英特尔GPU也能以60 FPS处理它,我在一台具有5到6年历史的集成英特尔GPU上以60 FPS的速度运行了10000粒子.发生爆炸时,我的卡会碰到高达100%的负载.
这是我的主要代码(整个代码不清楚且很长):
public class Game extends Canvas {
-snip-
public void tick() {
BufferStrategy bf = getBufferStrategy();
Graphics g = null;
try {
g = bf.getDrawGraphics();
paint(g);
} finally {
g.dispose();
}
if (!bf.contentsLost()) {
bf.show();
} else {
System.err.println("Buffer lost!");
}
Toolkit.getDefaultToolkit().sync();
}
public void setBuffers() {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
if (gc.getBufferCapabilities().isMultiBufferAvailable()) {
createBufferStrategy(3);
System.out.println("Triple buffering …Run Code Online (Sandbox Code Playgroud) 我正在尝试在两种算法之间做出决定.一个写入8个字节(两个对齐的4字节字)到2个高速缓存行,另一个写入3个整个高速缓存行.
如果CPU只将更改的8个字节写回内存,则第一个算法使用的内存带宽要少得多:8个字节对192个字节.如果CPU写入整个高速缓存行,则128和192字节之间的差异不那么显着.
那么Intel Xeon CPU如何写回内存?你会惊讶地发现在谷歌找到一个应该众所周知的答案是多么困难.
据我了解,写入进入存储缓冲区,然后进入缓存.当脏缓存行从缓存中逐出时,它们可能只被写入内存,但是英特尔是否跟踪缓存行的哪些部分是脏的,或者只是转储整个内容?我更怀疑他们跟踪缓存行粒度以下的事情.如果在高速缓存行被驱逐之前有任何事情进入内存,我也会感到非常惊讶.
我有一个新安装的Android Studio,在下载其组件后,我一直停留在设置向导上Running Intel® HAXM installer:
我该怎么办?如果我使用任务管理器结束Android Studio的任务,我所下载的所有组件都会丢失,因为取消Android Studio安装向导可能没有再次操作!(我没有成功取消它只是想自定义设置,我已经完成了它的任务.)