Performance 硬件到软件泄漏抽象:什么硬件概念可以让我的软件运行得更快?
所以最近(今天)我学习了cpu分支预测的概念。 基本上,如果if语句的比较是可预测的,那么代码将运行得更快。 下面是我编写的一个程序(C#中的控制台应用程序),它演示了这个概念:Performance 硬件到软件泄漏抽象:什么硬件概念可以让我的软件运行得更快?,performance,optimization,hardware,Performance,Optimization,Hardware,所以最近(今天)我学习了cpu分支预测的概念。 基本上,如果if语句的比较是可预测的,那么代码将运行得更快。 下面是我编写的一个程序(C#中的控制台应用程序),它演示了这个概念: using System; using System.Collections.Generic; using System.Diagnostics; /* * * Below are the times for branch prediction / misfires in seconds: * * Predi
using System;
using System.Collections.Generic;
using System.Diagnostics;
/* *
* Below are the times for branch prediction / misfires in seconds:
*
* Predictable: 0.91
* Unpredictable: 1.61
*
* Summary: When the branch is predictable, the program runs 55% faster.
*/
namespace BranchPredictions
{
class Program
{
static void Main(string[] args)
{
const int MAX = 100000000; // The amount of branches to create
bool predictable = true; // When true the list isn't in a predictable order
var nums = new List<int>(MAX);
var random = new Random();
for (int i = 0; i < MAX; i++)
{
if (predictable)
{
nums.Add(i);
}
else
{
nums.Add(random.Next());
}
}
int count = 0;
var sw = Stopwatch.StartNew();
foreach (var num in nums)
{
if (num % 2 == 0) // Here is the branch
{
count++;
}
}
sw.Stop();
Console.WriteLine("Total count: {0}", count);
Console.WriteLine("Time taken: {0}", sw.Elapsed);
if (Debugger.IsAttached)
{
Console.Write("Press any key to continue..");
Console.ReadKey(true);
}
}
}
}
使用系统;
使用System.Collections.Generic;
使用系统诊断;
/* *
*以下是分支预测/缺火的时间(秒):
*
*可预测:0.91
*不可预测:1.61
*
*摘要:当分支是可预测的时,程序运行速度会加快55%。
*/
命名空间分支预测
{
班级计划
{
静态void Main(字符串[]参数)
{
const int MAX=100000000;//要创建的分支数量
bool pretable=true;//如果为true,则列表的顺序不可预测
var nums=新列表(最大值);
var random=新的random();
对于(int i=0;i
这让我大开眼界,通过了解一些硬件概念,我可以让某些代码运行得更快,而无需任何真正的代码更改
但这让我想知道,如果我意识到硬件调用是否也能让软件运行得更快,还有什么其他的原因
我使用windows和C#,但这些概念应该适用于所有计算机和语言。首先作为分支预测讨论的旁注——可以向编译器提示可预测性问题,以生成使用例如cmov的恒定速度代码。还可以经常将if-else结构转换为常量时间表达式,例如
count+=1-(num%2)
验证任何分支预测假设
其他主要的HW开发/考虑的概念是内存带宽和缓存。将大型计算(例如10000x1000阵列)拆分为1250x1250 x 8x8块利用缓存局部性的概念
当“char”足够时,不必使用“int”,通过“微管理”数组元素的大小来考虑内存带宽 缓存的N向关联性可能导致某些数组长度比其他数组长度快,因为某些内存地址在相同的缓存线上竞争;解决办法是过度分配 循环展开的作用通常更多地是创建独立的变量依赖流,而不是预测分支。如果不直接进行并行编程,在某些情况下,当某些指令等待前一个循环的结果时,在同一个循环中混合两个独立的处理任务可以为处理器找到有用的计算结果,从而使速度提高一倍或三倍。指令吞吐量和延迟解释了这一现象。查看缓存及其(有时看似疯狂的)影响。下面是一篇很好的文章,让您开始学习: 很多时候,缓存“就在那个里”,悄悄地让你们的程序运行得比一直使用dram快得多。但它的工作表现如何取决于您访问内存的方式。一些常见的编程结构有助于实现这一点(比如以简单的方式迭代数组),而其他常见的编程结构实际上很糟糕(比如遍历链表) 缓存的世界往往与传统智慧相反。例如,通常采用的“时间/内存”折衷方法也常常完全相反——更少的内存通常意味着更少的缓存未命中,这可能比执行更多的数学运算更重要(相比之下,数学运算的成本很小——在最后一级缓存未命中所需的时间内,您可以轻松地执行几百条简单指令). 而且,数组列表几乎总是比链表快,即使你做了很多插入和删除操作,这也是链表应该擅长的。链表不能很好地使用缓存——它们在指针上浪费了大量的缓存(每项1或2个),而且它们经常以不可预测的模式访问内存 开始时还有一个与直觉相反的简单效果,即如果您访问一些内存,很快再次访问它(或接近它,在同一缓存线中)基本上是免费的。代码通常通过删除冗余读取来进行“优化”,但这并不是真正的问题。真正的问题是每当读操作未命中缓存时。缓存命中并不是完全免费的,如果你做的足够多,你会看到它们的影响。但不要关注他们,关注失误 但缓存的世界更为奇怪。关联性效应会使以特定步幅访问数组的速度突然比稍微不同的步幅慢得多。这通常出现在矩阵中,在矩阵中迭代一列(如果它是行主矩阵),这转换为访问1D数组,跨距等于矩阵的宽度。某些宽度(通常是某些二次幂的倍数)突然使该过程出乎意料地缓慢 似乎这还不够,请注意,由于缓存的大小是有限的,因此将其用于一件事情意味着其他事情会发生。这会导致非局部效应。特别是,如果您调用一个子例程,那么以前在缓存中的某些内容现在可能不存在。这会导致这种情况