如果我为一个变量赋值,然后想为它分配第二个值,但只有当它满足条件时,使用简写if语句是否同样有效?这是一个例子.
这更有效率吗?
int x = GetInt();
if (x < 5)
x = 5;
Run Code Online (Sandbox Code Playgroud)
比这个
int x = GetInt();
x = x < 5 ? 5 : x;
Run Code Online (Sandbox Code Playgroud)
我想我真正要问的是,如果x不满足条件,那么x = xelse语句会影响性能吗?
Joe*_*orn 11
我喜欢这个版本:
int x = Math.Max(5, GetInt());
Run Code Online (Sandbox Code Playgroud)
但请记住,所有这些都是过早的优化.今天更快的速度可能会更快,因为Windows Update会更改框架以添加新的或不同的JIT优化.
如果你在一个大循环中运行这样的支票,我可能会花一些时间查看:
var items = Enumerable.Range(0, 1000000);
foreach(int item in items)
{
if (item % 3 == 0)
{
//...
}
else
{
//...
}
}
Run Code Online (Sandbox Code Playgroud)
检查循环的原因并不多,因为代码将尽可能快地运行很多次,更加强调小的低效率,但是因为你是否在整个循环中来回使用if或else改变.
我希望代码效率低下,因为现代cpu的功能称为分支预测.如果该内容if和else足够显著和足够的不同,你可以使代码运行很多移动做所有这些检查的(并且把所有的预测失败)在前端,然后运行速度更快的所有ifš在一起,随后所有的elses.它会更快,因为运行if和else的第二阶段的分支预测(运行起来可能要贵得多)将更加准确.
这是一个展示差异的小程序:
class Program
{
static int samplesize = 1000000;
//ensure these are big enough that we don't spend time allocating new buffers while the stopwatch is running
static Dictionary<int, string> ints = new Dictionary<int,string>(samplesize * 4);
static Dictionary<double,string> doubles = new Dictionary<double,string>(samplesize * 4);
static void Main(string[] args)
{
var items = Enumerable.Range(0, samplesize).ToArray() ;
var clock = new Stopwatch();
test1(items); //jit hit, discard first run. Also ensure all keys already exist in the dictionary for both tests
clock.Restart();
test1(items);
clock.Stop();
Console.WriteLine("Time for naive unsorted: " + clock.ElapsedTicks.ToString());
test2(items); //jit hit
clock.Restart();
test2(items);
clock.Stop();
Console.WriteLine("Time for separated/branch prediction friendly: " + clock.ElapsedTicks.ToString());
Console.ReadKey(true);
}
static void test1(IEnumerable<int> items)
{
foreach(int item in items)
{
//different code branches that still do significant work in the cpu
// doing more work here results in a larger branch-prediction win, to a point
if (item % 3 == 0)
{ //force hash computation and multiplication op (both cpu-bound)
ints[item] = (item * 2).ToString();
}
else
{
doubles[(double)item] = (item * 3).ToString();
}
}
}
static void test2(IEnumerable<int> items)
{
//doing MORE work: need to evaluate our items two ways, allocate arrays
var intItems = items.Where(i => i % 3 == 0).ToArray();
var doubleItems = items.Where(i => i % 3 != 0).ToArray();
// but now there is no branching... adding all the ints, then adding all the doubles.
foreach (var item in intItems) { ints[item] = (item * 2).ToString(); }
foreach (var item in doubleItems) { doubles[(double)item] = (item * 3).ToString(); }
}
}
Run Code Online (Sandbox Code Playgroud)
在我的机器上的结果是第二次测试,做了更多的工作,运行得更快:
天真未分类的
时间:1118652 分离/分支预测的时间友好:1005190
这里要带走的重要事情并不是你需要回过头来看看你的所有循环是否都能从分支预测中受益.这只是可以使性能结果让您大吃一惊的众多CPU功能之一.这里要记住的重要一点是,要确定代码的执行方式,您实际上需要衡量您的表现.如果你没有仔细构建这个天真的技术仍然可以赢(我的第一次尝试没有达到我预期的速度).
另外,我需要指出的是,在这些情况下并没有太大的区别.这种表现是否值得,或者你会在其他地方度过更好的时间吗?了解这一点的唯一方法实际上是衡量整个应用的性能,并找出真正花费时间的地方.它在哪里真的比应该的慢?这称为分析,有一些工具可以帮助您准确地完成这项工作.