大于/小于的 switch 语句

所以我想使用这样的 switch 语句:

so I want to use a switch statement like this:

switch (scrollLeft) {
  case (<1000):
   //do stuff
   break;
  case (>1000 && <2000):
   //do stuff
   break;
}

现在我知道这些语句 (<1000) 或 (>1000 && <2000) 都不起作用(对于不同的原因,很明显).我要问的是最有效的方法.我讨厌使用 30 个 if 语句,所以我宁愿使用 switch 语法.有什么我可以做的吗?

Now I know that either of those statements (<1000) or (>1000 && <2000) won't work (for different reasons, obviously). What I'm asking is the most efficient way to do just that. I hate using 30 if statements, so I'd rather use the switch syntax. Is there anything that I can do?

推荐答案

当我查看其他答案中的解决方案时,我看到了一些我知道对性能不利的事情.我打算将它们放在评论中,但我认为最好对其进行基准测试并分享结果.你可以自己测试一下.下面是我在每个浏览器中最快运行后归一化的结果(ymmv).

When I looked at the solutions in the other answers I saw some things that I know are bad for performance. I was going to put them in a comment but I thought it was better to benchmark it and share the results. You can test it yourself. Below are my results (ymmv) normalized after the fastest operation in each browser.

这是 2021 年 5 月 5 日的结果

Here is the results from 2021-MAY-05

<头>
测试Chrome火狐歌剧边缘勇敢节点
1.0倍15 毫秒14 毫秒17 毫秒17 毫秒16 毫秒14 毫秒
如果-立即1.001.001.001.001.001.00
如果间接2.201.212.062.182.191.93
立即切换2.071.431.711.712.191.93
开关范围3.602.002.472.652.882.86
开关范围22.071.361.821.711.941.79
切换间接数组2.931.572.532.472.752.50
阵列线性开关2.733.292.122.122.382.50
数组二进制开关5.806.075.245.245.445.37

2021 年的测试在 Windows 10 64bit 上执行,以下版本:Chrome 90.0.4430.212、Firefox 89.0b13、Opera 76.0.4017.123em>、Edge 90.0.818.62、Brave 1.24.85、Node 16.1.0(在 WSL 下运行)

The tests in 2021 where performed on Windows 10 64bit with the following versions: Chrome 90.0.4430.212, Firefox 89.0b13, Opera 76.0.4017.123, Edge 90.0.818.62, Brave 1.24.85, and Node 16.1.0 (was run under WSL)

Apple 不会更新 Windows 版 Safari,所以它仍然是 5.1.7.我在这次测试中将其更改为 Brave.

Apple doesn't update Safari for Windows, so it is still 5.1.7. I changed it to Brave in this test.

这是 2012 年 9 月 4 日的结果,用于历史比较:

Here is the results from 2012-September-04, for historical comparison:

<头>
测试Chrome火狐歌剧MSIESafari节点
1.0倍37 毫秒73 毫秒68 毫秒184 毫秒73 毫秒21 毫秒
如果-立即1.01.01.02.61.01.0
如果间接1.21.83.33.82.61.0
立即切换2.01.12.01.02.81.3
开关范围38.110.62.67.320.910.4
开关范围231.98.32.04.59.56.9
切换间接数组35.29.64.25.510.78.6
阵列线性开关3.64.14.510.04.72.7
数组二进制开关7.86.79.516.015.04.9

2012 年在 Windows 7 32bit 上进行的测试,使用以下版本:Chrome 21.0.1180.89m、Firefox 15.0、Opera 12.02,MSIE 9.0.8112,Safari 5.1.7.Node 是在 Linux 64 位机器上运行的,因为 Windows 的 Node 上的计时器分辨率是 10 毫秒而不是 1 毫秒.

The tests in 2012 where performed on Windows 7 32bit with the folowing versions: Chrome 21.0.1180.89m, Firefox 15.0, Opera 12.02, MSIE 9.0.8112, Safari 5.1.7. Node was run on a Linux 64bit box because the timer resolution on Node for Windows was 10ms instead of 1ms.

这是所有测试环境中最快的方法,除了 ... drumroll MSIE!(惊喜,惊喜).

This is the fastest method in all tested environments, except in ... drumroll MSIE! (surprise, surprise).

这是推荐的实现方式.

if (val < 1000) { /*do something */ } else
if (val < 2000) { /*do something */ } else
...
if (val < 30000) { /*do something */ } else

如果间接

这是 switch-indirect-array 的变体,但使用 if-statements 代替,并且在所有测试引擎中都更快.

if-indirect

This is a variant of switch-indirect-array but with if-statements instead and is faster in all tested engines.

2021 年比最快的测试慢 20-120%(2012 年:0-280%).与 2012 年 (1.2) 相比,Chrome 在 2021 年 (2.20) 花费的时间更长

In 2021 it was 20-120% (2012: 0-280%) slower than the fastest test. Chrome takes longer time in 2021 (2.20) than in 2012 (1.2)

values=[
   1000,  2000, ... 30000
];
if (val < values[0]) { /* do something */ } else
if (val < values[1]) { /* do something */ } else
...
if (val < values[29]) { /* do something */ } else

立即切换

当您可以进行计算以获取索引时,此方法有效.

switch-immediate

This works when you can do a calculation to get an index.

在 2021 年,它比 if-immediate 慢 40-120%(2012 年:0-180%),但实际上是最快的 MSIE 除外.

In 2021 it was 40-120% (2012: 0-180%) slower than if-immediate, except in MSIE where it actually was the fastest.

switch (Math.floor(val/1000)) {
  case 0: /* do something */ break;
  case 1: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

开关范围

这很慢,因为引擎必须为每种情况比较两次值.

switch-range

It is slow because the engine has to compare the value twice for each case.

在 2021 年,它比最快的测试慢 1-2.6(2012 年:1.6-38)倍.Chrome 从 38 到 3.6 的改进最大,但仍然是测试最慢的引擎.

In 2021 it was 1-2.6 (2012: 1.6-38) times slower than the fastest test. Chrome has made the biggest improvement from 38 to 3.6, but is still the slowest tested engine.

switch (true) {
  case (0 <= val &&  val < 1000): /* do something */ break;
  case (1000 <= val &&  val < 2000): /* do something */ break;
  ...
  case (29000 <= val &&  val < 30000): /* do something */ break;
}

开关范围2

这是 switch-range 的变体,但每个案例只有一次比较,因此速度更快.case 语句的顺序很重要,因为引擎将按源代码顺序测试每个案例 ECMAScript 2020 13.12.9

switch-range2

This is a variant of switch-range but with only one compare per case and therefore faster. The order of the case statement is important since the engine will test each case in source code order ECMAScript 2020 13.12.9

2021 年比最快的测试慢 36-107%,但在 2012 年慢了 1-31 倍.在这次测试中表现最差的仍然是 Chrome,但已经从 32 倍提升到了 2 倍.

In 2021 it was 36-107% slower than the fastest test, but in 2012 it was 1-31 times slower. It is still Chrome who has the worst performance on this test, but it has improved from 32 to 2 times.

switch (true) {
  case (val < 1000): /* do something */ break;
  case (val < 2000): /* do something */ break;
  ...
  case (val < 30000): /* do something */ break;
}

切换间接数组

在这个变体中,范围存储在一个数组中.

switch-indirect-array

In this variant the ranges is stored in an array.

在 2021 年,它比最快的测试慢了 57-193%(2012 年:3-35 倍).所有测试引擎的性能都有所提高,虽然 Chrome 仍然是最慢的,但它已从 35 提高到 2.93.

In 2021 it was 57-193% (2012: 3-35 times) slower than the fastest test. The perormance has improved in all tested engines, and while Chrome is still the slowest, it has improved from 35 to 2.93.

values=[1000,  2000 ... 29000, 30000];

switch(true) {
  case (val < values[0]): /* do something */ break;
  case (val < values[1]): /* do something */ break;
  ...
  case (val < values[29]): /* do something */ break;
}

数组线性搜索

在这个变体中,范围存储在一个数组中.

array-linear-search

In this variant the ranges is stored in an array.

在 2021 年,它比最快的测试慢了 57-193%(2012 年:3-35 倍).所有测试引擎的性能都有所提高,虽然 Chrome 仍然是最慢的,但它已从 35 提高到 2.93.

In 2021 it was 57-193% (2012: 3-35 times) slower than the fastest test. The perormance has improved in all tested engines, and while Chrome is still the slowest, it has improved from 35 to 2.93.

values=[1000,  2000 ... 29000, 30000];

for (sidx=0, slen=values.length; sidx < slen; ++sidx) {
  if (val < values[sidx]) break;
}

switch (sidx) {
  case 0: /* do something */ break;
  case 1: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

数组二进制开关

这是 array-linear-switch 的变体,但带有二进制搜索.不幸的是,它比线性搜索慢.我不知道这是我的实现还是线性搜索更优化.也可能是键空间太小.

array-binary-switch

This is a variant of array-linear-switch but with a binary search. Unfortunately it is slower than the linear search. I don't know if it is my implementation or if the linear search is more optimized. It could also be that the keyspace is to small.

在 2021 年,这比 2012 年慢了 4-5 倍(2012 年:4-16 倍).请勿使用.

In 2021 this was 4-5 (2012: 4-16) times slower. Do not use.

values=[0, 1000,  2000 ... 29000, 30000];

while(range) {
  range = Math.floor( (smax - smin) / 2 );
  sidx = smin + range;
  if ( val < values[sidx] ) { smax = sidx; } else { smin = sidx; }
}

switch (sidx) {
  case 0: /* do something */ break;
  ...
  case 29: /* do something */ break;
}

结论

如果性能很重要,请使用 if-statements 或 switch,并带有立即值.

Conclusion

If performance is important, use if-statements or switch, with immediate values.

相关文章