Haxe:将变量映射到两个值之一的效率
Haxe: Efficiency of mapping a variable to one of two values
I feel this question is too general and poorly asked. Improvement suggestions would be appreciated.
假设我有一个整型变量,我需要将其转换为另外两个值之一。如果变量为0,它会保持0,否则,它会变成1。我可以想到两种方法来做到这一点。
方法 1: 内联 if/else 赋值。
function runFunc(input:Int):Void
{
<script>
}
for (index in 0...5)
{
runFunc(if (index == 0) {0;} else {1;});
}
方法二:除法四舍五入
function runFunc(input:Int):Void
{
<script>
}
for (index in 0...5)
{
runFunc(Math.round(index / index));
}
方法 1 更标准化,适用于其他数据类型和其他值,但 方法 2 似乎需要更少的处理能力(特别是如果这需要几乎不断地完成)。
假设方法 2 不会有舍入问题,这两种方法之间的时间差异是否足以考虑使用一种而不是另一种? if/else 语句和 Math.round() 等不同的东西如何影响处理时间?
嗯,首先,您的 方法 2 被零除。所以它甚至不是一个有效的解决方案。
但是,我假设您想要一个答案 "in general"。而且,当然,这类问题带有大量 "it depends" 资格。这取决于 CPU 的类型、编程语言、编译器可能进行的优化、运行时优化等。
不过一般来说,相关操作的开销顺序大致是:
- 逻辑和算术运算最便宜
- 其中,整数运算比浮点运算成本更低。
- 分支(ifs 和循环)成本适中。
- 除法比较贵。
- 函数调用非常昂贵。
基本上,这是因为(分别):
- 这就是 CPU 所做的,并且它们经过优化可以快速完成
- 分支代表两条可能的路径,这会减慢 CPU 并行化和优化。
- 除法是一个多步骤过程。
- 函数调用必须压入和弹出堆栈。
因此,我可能会打赌您上面的 方法 1。而且,为了清楚起见,我将在您的第一个示例中使用等效的三元运算符编写 if 语句:
for (index in 0...5)
{
runFunc( index==0 ? 0 : 1 );
}
这非常适合映射一个简单的布尔值(索引为 0 或不为 0)。但是当映射变得更复杂时呢?在将一个值映射到另一个值时,有几种结构通常可以提供良好的性能:查找 table 或哈希映射。
如果您的键是整数,并且介于某些最小值和最大值之间,则查找 table 很有用。如果您的键是可哈希的(通常 Ints、String 或 Object 可以用作哈希键。请看 haxe.ds.IntMap, haxe.ds.StringMap and haxe.ds.ObjectMap。)
示例 1: 一个常见的查询 table 可能会将数字整数 "day of the week" 映射到当天使用的单词。假设 day:Int
始终为 0-6,一天 int 到字符串查找 table 将是:
var day_name_lut = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" ];
所以现在 trace('Today is a '+day_name_lut[Date.now().getDay()]);
tell us 今天是什么日子。这是为了 非常 低成本将整数映射到字符串。比函数调用或 IntMap<String>
.
便宜
示例 2: A sample StringMap 可能用于通过字符串值存储某些对象(例如,可能 Person
通过它们的 name
.) 这将允许我们稍后按姓名查找人员:
var people_by_name = new StringMap<Person>();
var joe = new Person("Joe");
people_by_name.set(joe.name, joe);
var bob = new Person("Bob");
people_by_name.set(bob.name, bob);
这对于缓存昂贵的操作非常常见。 Imagine caching an HTTP response 由其 URL。第二次从 StringMap 中查找比再次返回获取响应要便宜 。
---更新---
我也注意到你说的"inline if/else assignment",参考这个:
runFunc(if (index == 0) {0;} else {1;});
请注意,实际编写 if 内联是那种绝对没有性能差异的事情。它的性能与以下完全相同:
var tmp = if (index == 0) {0;} else {1;}
runFunc(tmp);
这些也完全一样:
runFunc( if (index == 0) 0 else 1);
runFunc( (index == 0) ? 0 : 1);
这些类型的语义差异对代码的最终执行没有任何影响。处理器仍然需要计算并临时存储调用函数的值,无论是内联编写、存储在 tmp
变量中,还是使用 if/else 或三元运算符编写分支。编译器和虚拟机经过优化,可以获取您的(可能可变的)代码并将其归结为针对目标的最佳指令 CPU。
二进制运算 index & 1
怎么样?
I feel this question is too general and poorly asked. Improvement suggestions would be appreciated.
假设我有一个整型变量,我需要将其转换为另外两个值之一。如果变量为0,它会保持0,否则,它会变成1。我可以想到两种方法来做到这一点。
方法 1: 内联 if/else 赋值。
function runFunc(input:Int):Void
{
<script>
}
for (index in 0...5)
{
runFunc(if (index == 0) {0;} else {1;});
}
方法二:除法四舍五入
function runFunc(input:Int):Void
{
<script>
}
for (index in 0...5)
{
runFunc(Math.round(index / index));
}
方法 1 更标准化,适用于其他数据类型和其他值,但 方法 2 似乎需要更少的处理能力(特别是如果这需要几乎不断地完成)。
假设方法 2 不会有舍入问题,这两种方法之间的时间差异是否足以考虑使用一种而不是另一种? if/else 语句和 Math.round() 等不同的东西如何影响处理时间?
嗯,首先,您的 方法 2 被零除。所以它甚至不是一个有效的解决方案。
但是,我假设您想要一个答案 "in general"。而且,当然,这类问题带有大量 "it depends" 资格。这取决于 CPU 的类型、编程语言、编译器可能进行的优化、运行时优化等。
不过一般来说,相关操作的开销顺序大致是:
- 逻辑和算术运算最便宜
- 其中,整数运算比浮点运算成本更低。
- 分支(ifs 和循环)成本适中。
- 除法比较贵。
- 函数调用非常昂贵。
基本上,这是因为(分别):
- 这就是 CPU 所做的,并且它们经过优化可以快速完成
- 分支代表两条可能的路径,这会减慢 CPU 并行化和优化。
- 除法是一个多步骤过程。
- 函数调用必须压入和弹出堆栈。
因此,我可能会打赌您上面的 方法 1。而且,为了清楚起见,我将在您的第一个示例中使用等效的三元运算符编写 if 语句:
for (index in 0...5)
{
runFunc( index==0 ? 0 : 1 );
}
这非常适合映射一个简单的布尔值(索引为 0 或不为 0)。但是当映射变得更复杂时呢?在将一个值映射到另一个值时,有几种结构通常可以提供良好的性能:查找 table 或哈希映射。
如果您的键是整数,并且介于某些最小值和最大值之间,则查找 table 很有用。如果您的键是可哈希的(通常 Ints、String 或 Object 可以用作哈希键。请看 haxe.ds.IntMap, haxe.ds.StringMap and haxe.ds.ObjectMap。)
示例 1: 一个常见的查询 table 可能会将数字整数 "day of the week" 映射到当天使用的单词。假设 day:Int
始终为 0-6,一天 int 到字符串查找 table 将是:
var day_name_lut = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday" ];
所以现在 trace('Today is a '+day_name_lut[Date.now().getDay()]);
tell us 今天是什么日子。这是为了 非常 低成本将整数映射到字符串。比函数调用或 IntMap<String>
.
示例 2: A sample StringMap 可能用于通过字符串值存储某些对象(例如,可能 Person
通过它们的 name
.) 这将允许我们稍后按姓名查找人员:
var people_by_name = new StringMap<Person>();
var joe = new Person("Joe");
people_by_name.set(joe.name, joe);
var bob = new Person("Bob");
people_by_name.set(bob.name, bob);
这对于缓存昂贵的操作非常常见。 Imagine caching an HTTP response 由其 URL。第二次从 StringMap 中查找比再次返回获取响应要便宜 。
---更新---
我也注意到你说的"inline if/else assignment",参考这个:
runFunc(if (index == 0) {0;} else {1;});
请注意,实际编写 if 内联是那种绝对没有性能差异的事情。它的性能与以下完全相同:
var tmp = if (index == 0) {0;} else {1;}
runFunc(tmp);
这些也完全一样:
runFunc( if (index == 0) 0 else 1);
runFunc( (index == 0) ? 0 : 1);
这些类型的语义差异对代码的最终执行没有任何影响。处理器仍然需要计算并临时存储调用函数的值,无论是内联编写、存储在 tmp
变量中,还是使用 if/else 或三元运算符编写分支。编译器和虚拟机经过优化,可以获取您的(可能可变的)代码并将其归结为针对目标的最佳指令 CPU。
二进制运算 index & 1
怎么样?