出人意料的性能测试

813 阅读4分钟

前言

在开发公司一次需求(就是一个3层或者4层嵌套的表单数据(都展示层输入框))的各种处理时,脑子灵光一闪,想到了一些提高性能的方案,但是苦于时间(其实是自己拖延),一直没有实际测试性能是否真的有提高。直到这几天才真正的去测试了一下,结果得出的结论出乎我的意料。

表单的数据结构

测试嘛,数据肯定不能用公司的,但是要接近公司的真实数据量,二话不多上,直接上 Mock.js ,配置如下:

{
    "data|4": [{
        "title": "@ctitle(5)",
        "content|25": [{
            "title": "@ctitle(5)",
            "content|25": [{
                "key": "@string(6)",
                "title": "@ctitle(5)",
                "value": "@word(3)"
            }]
        }]
    }]
}

上面配置只是简版的,实际开发中的数据还有很多其他信息,但是不影响我的测试。

测试代码

看到数据结构可能很多老哥都猜到我要测啥了,我们在每次修改输入值的时候都要遍历数据,我们测的就是每次修改导致遍历数组所需要的时间。首先展示的代码如下:


renderForm(data) {
    return <>
      {
        data.map((item, index) => {
          return <div  className="yijimokuai">
            <h1>{item.title}</h1>
            {
              item.content.map((_item, _index) => {
                return <div  className="erjimokuai">
                  <h2>{_item.title}</h2>
                  {
                    _item.content.map((__item,__index) => {
                      return <div >
                        <span>{__item.title}:</span>
                        {/* <input type="text" value={__item.value} onChange={(e) => this.handleChange1(e,__item.key)}/> */}
                        {/* <input type="text" value={__item.value} onChange={(e) => this.handleChange2(e,__item.key)}/> */}
                        {/* <input type="text" value={__item.value} onChange={(e) => this.handleChange3(e,[index,_index,__index])}/> */}
                        <input type="text" value={this.state.dict[__item.key]} onChange={(e) => this.handleChange4(e,__item.key)}/>                      
                      </div>
                    })
                  }
                </div>
              })
            }
          </div>
        })
      }
    </>
  }

有4种input,每种对应一个不同的Onchange方案:

  1. 方案1:没有优化
handleChange1(e,key){
    console.time('test1');
    let data = this.state.data;
    for (let i = 0,flen = data.length; i < flen ;i++) {
      for (let j = 0,slen = data[i].content.length; j< slen; j++) {
        for (let k = 0,klen = data[i].content[j].content.length; k< klen;k++) {
          if (data[i].content[j].content[k].key === key) {
            data[i].content[j].content[k].value = e.target.value
          }
        }
      }
    }
    this.setState({
      data
    },()=>{
      console.timeEnd('test1');
    })
  }
  1. 方案2:最简单的,加个break
handleChange2(e,key){
    console.time('test2');
    let data = this.state.data;
    for (let i = 0,flen = data.length; i < flen ;i++) {
      for (let j = 0,slen = data[i].content.length; j< slen; j++) {
        for (let k = 0,klen = data[i].content[j].content.length; k< klen;k++) {
          if (data[i].content[j].content[k].key === key) {
            data[i].content[j].content[k].value = e.target.value
            break;
          }
        }
      }
    }
    this.setState({
      data
    },()=>{
      console.timeEnd('test2');
    })
  }
  1. 方案3:直接传递数组下标
handleChange3(e,key) {
    console.time('test3');
    let data = this.state.data;
    data[key[0]].content[key[1]].content[key[2]].value = e.target.value;
    this.setState({
      data
    },() => {
      console.timeEnd('test3');
    })
  }
  1. 方案4:把数组中的值拷贝一份出来做一个字典,每次改值都在字典中改
handleChange4(e,key){
    console.time('test4');
    let dict = this.state.dict;
    dict[key] = e.target.value;
    this.setState({
      dict
    },() => {
      console.timeEnd('test4');
    })
  }

理论分析

理论上的时间复杂度 方案1、2不用说了其实都是一样的,O(n^3) 方案3理论上是O(1) 方案4理论上是O(n),且需要额外的空间复杂度O(n) 综合性能上的话应该是 方案3 > 方案4 > 方案2 > 方案1

开测

为了保证测试结果,每次都修改3个输入框的值,每次都是新增输入3个同样的字符,废话不多说,直接测

方案1

方案2:

方案3:

方案4:

纳尼?居然都差不多?

接下来用不同的浏览器测试,数据来源换成生成好的JSON(保住数据完全相同),增大数据量都试过,在浏览器不卡死能渲染的情况下,这几种情况下的渲染时间居然是不相上下。

分析

后来在每种方案调用的情况下都调用了一下console.time,发现4种方案消耗时间基本一致。就是说,方案1、2的遍历速度在我的电脑上遍历速度都非常快,导致了优化的效果并不明显。也就是说,在公司做的性能优化方案其实对于常见的电脑来说其实意义不大(测试的数据量已经大于公司的数据量了)。但是对于低端机器,又没有合适的工具可以进行测试。。。

后话

有心人可能发现我一开始的渲染代码中没有上key(故意删的),因为当React作diff时,只要子元素有key属性,便会去原v-dom树中相应位置(当前横向比较的层级)寻找是否有同key元素,比较它们是否完全相同,若是则复用该元素,免去不必要的操作。但是对于我们此种测试,上述操作反而是不必要的,因为我们数据是固定的,上key反而多做了一次遍历(上面说的都是我猜的原因),那么上了key之后的时间是多少呢?

想了那么久的数据结构,结果发现主流的电脑都可以忽略这点优化了,心情复杂。。。有没有大神告知一下是测试方法有问题还是真的机子太高端了,在这种数据级的情况下优化是没多大必要的?