Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【每日一题】- 2020-05-14 - 为什么switch/case 的性能要比使用 mapping(对象)好? #127

Closed
azl397985856 opened this issue May 14, 2020 · 4 comments

Comments

@azl397985856
Copy link
Owner

如下代码,是使用了switch case 的redux 的部分代码:

const LOGIN_SUCCESS = "LOGIN_SUCCESS";
const LOGIN_FAILED = "LOGIN_FAILED";

const authState = {
  token: "",
  error: "",
};

function authReducer(state = authState, action) {
  switch (action.type) {
    case LOGIN_SUCCESS:
      return { ...state, token: action.payload };
    case LOGIN_FAILED:
      return { ...state, error: action.payload };
    default:
      return state;
  }
}

我们将核心代码改成mapping:

function authReducer(state = authState, action) {
  const mapping = {
    [LOGIN_SUCCESS]: { ...state, token: action.payload },
    [LOGIN_FAILED]: { ...state, error: action.payload }
  };

  return mapping[action.type] || state;
}

实现效果是一样的, 而且代码更简单明了。 通过benchmark测试,我们发现性能switch好很多,这是为什么?

附benchmark图:

Switch Mapping
43.033 71.609
40.247 64.800
37.814 66.123
37.967 63.871
37.616 68.133
38.534 69.483
37.261 67.353
41.662 66.113
38.867 65.872
37.602 66.873
@feikerwu
Copy link
Contributor

贴个benchmark:
node 版本: v10.20.1
机器: 2.6 GHz 六核Intel Core i7 16G

  Switch Object
10 ^ 0 0.237ms 0.083ms
10 ^ 1 0.248ms 0.108ms
10 ^ 2 0.265ms 0.259ms
10 ^ 3 0.364ms 2.264ms
10 ^ 4 1.954ms 13.573ms
10 ^ 5 5.695ms 63.255ms
10 ^ 6 26.847ms 546.769ms
10 ^ 7 163.984ms 5161.224ms
10 ^ 8 1627.382ms 50421.401ms

代码

let count = Math.pow(10, 8)
let swicthCount = count
let objectCount = count

const LOGIN_SUCCESS = "LOGIN_SUCCESS";
const LOGIN_FAILED = "LOGIN_FAILED";

const authState = {
  token: "",
  error: "",
};

function authReducer(state = authState, action) {
  switch (action.type) {
    case LOGIN_SUCCESS:
      return { ...state, token: action.payload };
    case LOGIN_FAILED:
      return { ...state, error: action.payload };
    default:
      return state;
  }
}


function authReducerObject(state = authState, action) {
  const mapping = {
    [LOGIN_SUCCESS]: { ...state, token: action.payload },
    [LOGIN_FAILED]: { ...state, error: action.payload }
  };

  return mapping[action.type] || state;
}

console.time('switch')
while(swicthCount--) {
  const action = Math.random() < 0.5 ? LOGIN_SUCCESS : LOGIN_FAILED
  authReducer(authState, action)
}
console.timeEnd('switch')



console.time('object')
while(objectCount--) {
  const action = Math.random() < 0.5 ? LOGIN_SUCCESS : LOGIN_FAILED
  authReducerObject(authState, action)
}
console.timeEnd('object')

@wuxiangwa
Copy link

wuxiangwa commented May 14, 2020

某些浏览器应该对switch做过优化,但是我觉得switch随着case数越来越多,性能肯定会下来,最终应该还是不如mapping,还有一个每次都是创建mapping对象,如果这个对象只初始化一次,也会有所提高

@azl397985856
Copy link
Owner Author

azl397985856 commented May 18, 2020

简单来说, if else 会自顶向下计算,时间复杂度最坏的情况是 $O(N)$。 而 switch case 使用类似 hash 的方式, 就条件和对应语句汇编到底层代码中,当程序执行的时候可以根据数学运算在 $O(1)$ 的时间定位到对应的代码块。

当然这并不是绝对的, switch 的优化方式可以参考这篇文章:https://www.cnblogs.com/182-7167-2685/p/5374111.html

@stale
Copy link

stale bot commented Jul 17, 2020

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

@stale stale bot added the stale label Jul 17, 2020
@stale stale bot closed this as completed Jul 24, 2020
@azl397985856 azl397985856 pinned this issue Oct 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
每日一题认领区
Awaiting triage
Development

No branches or pull requests

3 participants