typeof 比文字比较快吗?

Roy*_*son 3 javascript performance

当检查一个值是否x为布尔值时,是

typeof x === 'boolean'快于x === true || x === false或反之亦然?

我预计文字比较会更快,但似乎 typeof 比较几乎快两倍。

旁注:我知道这对于几乎任何实际目的来说都不重要。

这是基准代码(免责声明:我不知道如何进行基准测试): https: //jsperf.com/check-if-boolean-123

Jon*_*lms 6

这取决于。

为了给出绝对的答案,我们必须在每种可能的浏览器/架构组合上编译每段代码/观察解释器。然后我们可以给出一个绝对的答案哪个操作需要更少的处理器周期,其他一切都纯粹是猜测。这就是我现在正在做的事情:

天真的方法

我们假设引擎不执行任何优化。他们只是执行规范中定义的每一步。然后对于每个测试用例都会发生以下情况:

typeof x === 'boolean'

x(1)查找的类型。由于引擎可能将通用“JavaScript 值”表示为具有指向实际数据的指针和值类型的枚举的结构,因此获取描述该类型的字符串可能是在类型 -> 类型字符串映射中查找。

(2) 现在我们有两个字符串值,我们必须对其进行比较 ( 'boolean' === 'boolean')。首先,必须检查类型是否相等。这可能是通过比较两个值的类型字段和按位相等(意思是:一个处理器操作)来完成的。

(3) 最后必须比较该值是否相等。对于字符串,这意味着迭代两个字符串并将字符相互比较。

x === true || x === false

(1) 首先,必须如上所述比较xtruexad的类型。false

(2) 其次,必须比较这些值,对于布尔值来说,这是按位质量(意思是:一个处理器操作)。

(3) 最后一步是or表达式。给定两个值,首先必须检查它们的真实性(这对于布尔值来说非常容易,但我们仍然必须再次检查这些值是否真的是布尔值),然后可以进行或操作(按位或,意思是:一个处理器操作)。

那么哪一个更快呢?如果我不得不猜测,第二种方法,因为第一种方法必须执行字符串相等,这可能需要更多迭代。

最佳方法

一个非常聪明的编译器可能会意识到,typeof x === 'boolean'只有当 x 的类型是布尔值时,这才是正确的。所以它可以优化为(C++伪代码):

  result = JSValue( JSType::Boolean, x->type == JSType::Boolean)
Run Code Online (Sandbox Code Playgroud)

这只是一些处理器操作,所以速度非常快。与简单的方法相比,我们节省了字符串比较和多次类型检查。引擎会做这样的优化吗?可能是因为typeof检查很常见,并且很容易优化(所以这是一个轻松的胜利)。

我们可以优化吗x === true || x === false?嗯,是的,我们知道 的类型truefalse这可以归结为(C++ 伪代码):

var result = JSValue(JSType:Boolean, x->type == JSType:Boolean && !x->value || x->type == JSType:Boolean && x->value)
Run Code Online (Sandbox Code Playgroud)

就不能进一步优化吗?难道编译器看不出x->value!x->value是互斥的,因此它实际上与上面的代码完全相同。编译器会做这种优化吗?我不知道。这绝对不是那么容易,而优化x || !x实际上是开发人员可以采取的一种简单的优化。

因此,在一个完美的世界中,如果有一个非常周到的编译器,第一个编译器会更快。

但编译器会优化那么多吗?这取决于。如今,引擎首先采用简单的方法(因为编译也花费时间),并且只有在函数变热(经常被调用)时才会进行优化。您的测试用例就是这种情况(这就是第二个测试用例更快的原因)。现实世界的代码是否变得热门实际上取决于用例。

带走

在我的设备上,第一个版本执行 451,701,256 次操作/秒(!!)。这么快吗?是的!

第二个版本的执行速度为 198,308,952 ops/s。是不是慢了一倍?是的。慢吗?不!可能还有其他代码片段会消耗更多的处理器周期。

进一步的想法

一个非常常见的优化是在推导参数的数据类型后编译函数。这意味着如果你这样做

const check = it => typeof it === "boolean";

for(let i = 0; i < 100000; i++) check(true); // make function hot
Run Code Online (Sandbox Code Playgroud)

该函数可能会被编译为

boolean check(/*deduced datatype*/ boolean it) {
  return true; // <- even faster than anything above
}
Run Code Online (Sandbox Code Playgroud)

现在如果你打电话会发生什么check("ouch")?好吧,在函数的入口点,类型被断言,断言失败,引擎必须回退到解释/重新编译函数。因此,使用布尔值调用函数 100000 次,然后使用字符串调用函数 100000 次可能比随机使用字符串/布尔值调用函数 200000 次更快。编写性能测试时请记住这一点。