- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
很奇怪,我一直认为我们应该总是将 high chance 子句放在嵌套 if-els 的前面,直到今天。
简要设置:
数组 Zoo[]
包含 5 类的 10,000 个对象,基于权重,例如4,3,2,1,0(表示 4000 只猫、3000 只狗、2000 只鸡、1000 只兔子、0 只猫头鹰)并且可以洗牌或不洗牌(完全按顺序)。
然后使用if-else
检查每个数组成员。
结果:时间(毫秒)
Weights 43210 01234 22222 43210 01234 22222
Shuffle Yes Yes Yes No No No
Polymorphism 101 100 107 26 26 27
If Else 77 28 59 17 16 17
If Else Reverse 28 77 59 16 17 16
Switch 21 21 21 18 19 18
当我看到 If-Else
反转比 if-else
好得多时,它引起了我的注意。这里 if-else
检查 Cat->Dog->Chicken->Rabbit->Owl,反向版本以相反的顺序检查它们。
此外,有人可以解释一下在非 shuffle 版本中每个方法都有很大的改进吗? (我假设是由于内存中的缓存或更好的命中率?)
Weights 27 9 3 1 0 0 1 3 9 27 27 9 3 1 0 0 1 3 9 27
Shuffle Yes Yes No No
Polymorphism 84 82 27 27
If Else 61 20 17 16
If Else Reverse 20 60 16 17
Switch 21 21 18 18
代码如下:
class Animal : AnimalAction
{
public virtual int Bart { get; private set; }
public int Type { get; private set; }
public Animal(int animalType)
{
this.Type = animalType;
}
}
interface AnimalAction
{
int Bart { get; }
}
class Cat : Animal
{
public Cat()
: base(0)
{
}
public override int Bart
{
get
{
return 0;
}
}
}
class Dog : Animal
{
public Dog()
: base(1)
{
}
public override int Bart
{
get
{
return 1;
}
}
}
class Chicken : Animal
{
public Chicken()
: base(2)
{
}
public override int Bart
{
get
{
return 2;
}
}
}
class Rabbit : Animal
{
public Rabbit()
: base(3)
{
}
public override int Bart
{
get
{
return 3;
}
}
}
class Owl : Animal
{
public Owl()
: base(4)
{
}
public override int Bart
{
get
{
return 4;
}
}
}
class SingleDispatch
{
readonly Animal[] Zoo;
int totalSession;
SingleDispatch(int totalSession, int zooSize)
{
this.totalSession = totalSession;
Zoo = new Animal[zooSize];
int[] weights = new int[5] { 0, 1, 2, 3, 4 };
int totalWeights = weights.Sum();
int[] tiers = new int[4];
int accumulated = 0;
for (int i = 0; i < 4; i++)
{
accumulated += weights[i] * zooSize / totalWeights;
tiers[i] = accumulated;
}
for (int i = 0; i < tiers[0]; i++)
{
Animal nextAnimal = new Cat();
Zoo[i] = nextAnimal;
}
for (int i = tiers[0]; i < tiers[1]; i++)
{
Animal nextAnimal = new Dog();
Zoo[i] = nextAnimal;
}
for (int i = tiers[1]; i < tiers[2]; i++)
{
Animal nextAnimal = new Chicken();
Zoo[i] = nextAnimal;
}
for (int i = tiers[2]; i < tiers[3]; i++)
{
Animal nextAnimal = new Rabbit();
Zoo[i] = nextAnimal;
}
for (int i = tiers[3]; i < zooSize; i++)
{
Animal nextAnimal = new Owl();
Zoo[i] = nextAnimal;
}
Zoo.FisherYatesShuffle();
}
public static void Benchmark()
{
List<Tuple<string, double>> result = new List<Tuple<string, double>>();
SingleDispatch myBenchmark = new SingleDispatch(1000, 10000);
result.Add(TestContainer.RunTests(10, myBenchmark.SubClassPoly));
result.Add(TestContainer.RunTests(10, myBenchmark.Ifelse));
result.Add(TestContainer.RunTests(10, myBenchmark.IfelseReverse));
result.Add(TestContainer.RunTests(10, myBenchmark.Switch));
foreach (var item in result)
{
Console.WriteLine("{0,-30}{1:N0}", item.Item1, item.Item2);
}
Console.WriteLine();
}
void SubClassPoly()
{
long sum = 0;
for (int i = 0; i < totalSession; i++)
{
foreach (var myAnimal in Zoo)
{
sum += myAnimal.Bart;
}
}
}
void Ifelse()
{
long sum = 0;
for (int i = 0; i < totalSession; i++)
{
foreach (var myAnimal in Zoo)
{
if (myAnimal.Type == 0)
{
sum += 0;
}
else if (myAnimal.Type == 1)
{
sum += 1;
}
else if (myAnimal.Type == 2)
{
sum += 2;
}
else if (myAnimal.Type == 3)
{
sum += 3;
}
else
{
sum += 4;
}
}
}
}
void IfelseReverse()
{
long sum = 0;
for (int i = 0; i < totalSession; i++)
{
foreach (var myAnimal in Zoo)
{
if (myAnimal.Type == 4)
{
sum += 4;
}
else if (myAnimal.Type == 3)
{
sum += 3;
}
else if (myAnimal.Type == 2)
{
sum += 2;
}
else if (myAnimal.Type == 1)
{
sum += 1;
}
else
{
sum += 0;
}
}
}
}
void Switch()
{
long sum = 0;
for (int i = 0; i < totalSession; i++)
{
foreach (var myAnimal in Zoo)
{
switch (myAnimal.Type)
{
case 0:
sum += 0;
break;
case 1:
sum += 1;
break;
case 2:
sum += 2;
break;
case 3:
sum += 3;
break;
case 4:
sum += 4;
break;
default:
break;
}
}
}
}
}
最佳答案
分支预测。 http://igoro.com/archive/fast-and-slow-if-statements-branch-prediction-in-modern-processors/
对于非洗牌的情况,它更容易理解。假设我们有一个非常简单的预测器,它猜测下一个结果将与上一个结果相同:
例如(c=猫,d=狗,o=猫头鹰)
动物:CCCCC DDDDD OOOOO
预测:*CCCC CDDDD DOOOO
正确:NYYY NYYY NYYYY
如您所见,只有当动物发生变化时,预测才会出错。因此,对于每种类型的 1000 只动物,预测器在超过 99% 的时间里都是正确的。
但是,预测器并不是真的那样工作,
真正发生的事情**是每个 if 分支都被预测为真或假。假设您的示例中的 (40%,30%,20%,10%,0%) 分布:
if (Animal.Type == MostCommonType) true 不到一半的时间 (40%) 100 个中有 40 个 (40+30+20+10+0)else if (animal.Type == SecondMostCommonType)//50% 的时间为真,60 次中有 30 次 (30+20+10 + 0)else if (animal.Type == ThirdMostCommonType)//66% 的时间为真 30 次中有 20 次 (20+10)else if (animal.Type == FourtMostCommonType)//true 100% 的时间 10 out of 10 (10 +0)
40%、50% 和 60% 的几率并没有给预测器带来多少可操作的机会,唯一好的预测 (100%) 是在最不常见的类型和最不常见的代码路径上。
但是,如果您反转 if 顺序:
if (animal.Type == FifthMostCommonType)//100% 的错误 100 个中有 0 个 (40+30+20+10+0)else if (animal.Type == FourtMostCommonType)//90% 的时间错误 100 次中有 10 次 (40+30+20+10)else if (Animal.Type == MostCommonType)//错误 77% 的时间 20 out of 90 (40+30+20+)else if (animal.Type == SecondMostCommonType)//57% 的时间为真,70 次中有 30 次 (40+30)else if (animal.Type == ThirdMostCommonType)//100% 正确 40 out of 40 (40+)
几乎所有的比较都是高度可预测的。
预测下一个动物不会是最不常见的动物将比任何其他预测都更正确。
简而言之,在这种情况下错过分支预测的总成本高于进行更多分支(即 if 语句)的成本
我希望这能稍微澄清一下。如果有任何不清楚的地方,请告诉我,我会尽力澄清。
**不是真的,但更接近事实。
编辑:
较新处理器中的分支预测器相当复杂,您可以在 http://en.wikipedia.org/wiki/Branch_predictor#Static_prediction 查看更多详细信息
混洗通过移除相似数据组并使每个猜测或预测可能是正确的来混淆预测器。想象一副全新的纸牌。一个 friend 拿起每张牌,让你猜它是红色还是黑色。
在这一点上,一个相当好的算法是猜测最后一张牌是什么。你几乎每次都会猜对。 > 90%
然而,在洗牌后,该算法只能给出 50% 的准确率。事实上,没有任何算法会给您带来明显优于 50% 的效果。 (据我所知,计算剩下的红色和黑色的数量是在这种情况下获得优势的唯一方法。)
编辑:重新分类
我猜这是因为 CPU/L1/2/etc 缓存未命中。由于每个类都将返回值实现为常量,即 return 0,因此返回值是函数的一部分。我怀疑如果您如下所示重新实现该类,您会在每次调用时强制缓存未命中,并看到相同(糟糕的)性能是否被打乱。
class Rabbit : Animal
{
int bartVal; // using a local int should force a call to memory for each instance of the class
public Rabbit():base(3)
{
bartVal = 3;
}
public override int Bart
{
get
{
return bartVal;
}
}
}
关于c# - 为什么将 High chance 子句放在嵌套 If else 的前面会降低性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10142883/
所以我试图设置“内容”类的高度,但它似乎不起作用。我对嵌套 DIV 非常陌生,我已经尝试了我在谷歌搜索中发现的修复程序,但似乎没有任何效果。帮助?
好的,所以我一直在四处寻找,但找不到这个问题的答案。但是,我需要将一个 View 嵌套在另一个 View 中。 我有一个 $layout 正在使用我拥有的 default.layout Blade 文
好的,所以我一直在四处寻找,但找不到这个问题的答案。但是,我需要将一个 View 嵌套在另一个 View 中。 我有一个 $layout 正在使用我拥有的 default.layout Blade 文
基本上,我的问题很简单,但它需要知道 Struts 1.1 并且还活着的人。 我尝试构建的伪代码看起来像这样: IF element.method1 = true THEN IF element
我正在尝试将 Excel 嵌套 IF 语句转换为代码语言,但我不确定我是否正确执行此操作,希望能得到一些帮助 这是Excel语句: =IF(D3="Feather",IF(OR(I3>1000,R3=
如果我们创建两个或三个评论并对其进行多次回复,则“有用”链接在单击时会导致问题,它会对具有相同编号的索引执行 ng-click 操作,从而显示具有相同索引的所有文本。如何解决此嵌套问题,以便在单击链接
我在项目中使用Scala,想与Stripe集成,但它只提供Java API。例如,要创建 session ,我使用: val params = new util.HashMap[String, Any
以下代码有一个 Div,其中连续包含四个较小的 Div。四个 Div 中的每一个还包含一个较小的 Div,但此 Div 未显示。我尝试了各种显示和位置组合,看看 div 是否会出现。 classGoa
我在这里有一个问题,循环是: for (i=0; i < n; ++i) for (j = 3; j < n; ++j) { ...
我正在尝试编写代码来显示具有奇数宽度的形状。形状完成后,将其放置在外部形状内。用户将能够输入用于形状的字符和行数。我希望生成一个形状,并通过 for 循环生成一个外部形状。 ***** .
$(".globalTabs").each(function(){ var $globalTabs = $(this); var parent = $globalTabs.parent
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 9 年前。 Improve th
所以我在这个问题上遇到了一些麻烦,因为变量 i。我只是不确定在第二个 while 循环中如何处理它。对于我的外循环,我知道它将运行 log_4(n^2) 次迭代。对于内部 while 循环,我计算的迭
我似乎找不到在枚举上应用多个 if/then 逻辑的工作方式。 anyOf 不应用条件逻辑,而是表示如果其中任何一个匹配则很好。 allOf 再次不应用条件逻辑,而是测试属性/必填字段的超集。 这是一
如何访问 ReaderT 的内部 monad。 在我的例子中,我有类型: newtype VCSSetupAction a = VCSSetupAction (ReaderT (Maybe VCSCo
这个问题在这里已经有了答案: Add leading zeroes/0's to existing Excel values to certain length (7 个回答) 7年前关闭。 我正在寻
我已经绑定(bind)了很多 AND/OR 函数的组合并且没有运气。 这是我需要创建的: 在 B 列中,我有公司 ID,范围从两个数字字符到六个数字字符。 我需要在 B 列中的每个公司 ID 之前的每
我是 VBA 新手,在尝试编写的宏中使用 If 语句时遇到了一些困难。每个月我都会收到一份 Excel 报告,其中列出了我们公司的哪些员工执行了某些任务。我正在编写的宏旨在将每个员工的数据复制并粘贴到
如果在 B 列中找到单元格 A1 中的值,则使用文本 321 填充除非在 C 列中找到单元格 A1 中的值,在这种情况下填充文本 121反而。如果单元格 A1 的内容不在 B 列或 C 列中,则使用
我有几十万个地址。其中一些在整数之后有粒子。如 4356 A Horse Avenue , 其他格式正常4358 Horse Avenue .有些有“A”,有些有“B”。我正在尝试删除整数和粒子之间的
我是一名优秀的程序员,十分优秀!