为什么if1比iftrue更快

问题描述

我正在尝试用JavaScript和画布制作Conway的生活游戏,我有一个1280x720的矩阵,用于存储单元格数据,我当前的数据存储方式为1 =有效,0 =无效,然后当我检查细胞是否还活着时,我只是做:if(matrix[i][j]) 我很好奇这是否可以改善,并在 https://jsbench.me/复制了一个类似的场景,并注意到如果使用“ true / false”,整个​​过程要慢+ -11%,为什么会这样呢?不是应该更快吗?

示例基准,只需将1更改为true即可测试其他情况

let array = []
for(let i = 0; i<1000000; i++){
   array.push(1)
}
let sum = 0
for(let i = 0; i<1000000;i++){
    if(array[i]){
        sum++
    }
}

解决方法

您看到的性能差异并非严格地取决于if语句的评估,而是由于数组元素种类可从中访问值(1true)。 V8引擎区分arrays of different element kinds1的数组将被视为PACKED_SMI_ELEMENTS,而true的数组将被视为PACKED_ELEMENTS。因此,使用布尔元素的版本会慢一些。

作为说明,这是在数组元素类型之间应用的相对性能优化的格,在左上角具有最佳性能,在右下角具有最差性能:

element kinds lattice

然后here's a benchmark将您的两个测试与我根据my comment below添加的测试进行比较:

benchmark results

,

(这里是V8开发人员。)

简而言之,1/0版本更快,因为数组的元素种类有助于if语句完成较少的工作。

长版: 正如@PatrickRoberts指出的那样,V8跟踪存储在数组中的值的类型。这种机制是粗粒度的,它仅区分“仅整数”,“仅双精度”和“任何”。 if(array[i])在知道数组仅包含整数时,可以简单地与0进行比较以查看是否应采用该分支。它没有比这更快的速度。但是,如果数组包含“ anything”(包括true),则根据JavaScript的语义,V8必须检查加载的值是否为“ true-ish”,即在条件上下文中评估为true。相反,即检查虚假值实际上更容易/更快,因此V8检查:值false是吗?是""吗?是数字(可能是0)吗?是BigInt(可能为0n)吗?是document.all(古代特别有趣的特殊遗物)吗?其他所有取值为true在这种情况下,立即检查true是“聪明” /幸运的事情,但是引擎不知道这一点,因此这种启发式方法在一般。

(请注意,得出if(1)if(true)更快的结论是错误的-特别重要的是,条件数组中的值是从数组中加载的,并且该数组跟踪可能值的范围,这会影响随后需要或不需要对已加载值进行的检查。当您使用常量1true时,两个评估的速度相同(实际上,在大多数情况下,优化编译器会完全删除它们,因为if(true)是正确的,du!)。

这就是说,您看到的大多数差异并不是由于此,因为测试在第一个循环中花费了90%以上的时间来填充数组。将数组从长度0增加到一百万意味着需要重复扩展其后备存储,这意味着将分配新的后备存储并复制所有现有元素。这是仅整数元素具有速度优势的另一种操作:它们可以使用批量复制操作,以与CPU访问内存一样快的速度移动数据。但是,在“任何东西”数组中,垃圾收集器必须执行一次附加遍历,以查看是否有任何值是它感兴趣的引用。在这种情况下,所有值都是true标记,但不是,但是GC如果不检查就无法知道。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...