矢量移动构造函数慢于复制构造函数

时间:2017-01-26 05:12:47

标签: c++ csv c++11 vector move-semantics

我正在开发我的第一个C ++项目,这是一个CSV解析器(full source code here)。它正处于工作状态,现在我想进行基本的重构/提高性能。

目前解析器的工作方式是将每一行作为std::vector<std::string>返回,我认为每次我只有一个内部向量和内部时,而不是分配一个新的向量和一个新的字符串我保留记忆的字符串,我一次又一次地清除。

这很有用,我开始查看我可能正在进行内存分配的其他地方,我看到了这个复制内部向量的函数,然后将其清除:

auto add_row() -> std::vector<std::string> {
  auto row(m_bufvec);
  m_bufvec.clear();
  return row;
}

我认为如果我改变了这一行

auto row(m_bufvec);

auto row(std::move(m_bufvec));

它会导致某种速度提升,因为根据http://en.cppreference.com/w/cpp/container/vector/vector,它需要恒定的时间而不是线性的。令我惊讶的是,它使解析器显着变慢(根据我在time ./main.o上运行std::move的非常粗略的基准)。

我对优化,基准测试以及调优C ++代码所带来的一切都是全新的。也许这种优化即使有效也没用,但无论如何,我很好奇//renderer renderer = new THREE.WebGLRenderer( { canvas : spaceCanvas} ); renderer.setSize( 672, 472 ); renderer.shadowMap.enabled = true; renderer.shadowMap.renderReverseSided = false; //mainScene mainScene = new THREE.Scene(); mainCamera = new THREE.PerspectiveCamera( 45, window.innerWidth /window.innerHeight, 0.01, 2000 ); mainCamera.position.z = 2.25; //Lighting var ambLight = new THREE.AmbientLight( 0x222222 ); var light = new THREE.DirectionalLight( 0xffffff, 1 ); light.position.set(5,3,5); light.castShadow = true; light.shadow.camera.near = 0.01; light.shadow.camera.far = 15; light.shadow.camera.fov = 45; light.shadow.camera.left = -1; light.shadow.camera.right = 1; light.shadow.camera.top = 1; light.shadow.camera.bottom = -1; /*light.shadowCameraVisible = true;*/ light.shadow.bias = 0.001; light.shadow.mapSize.width = 672; light.shadow.mapSize.height = 472; mainScene.add(light, ambLight); 导致经济放缓的原因。我错过了什么吗?

2 个答案:

答案 0 :(得分:8)

复制bufvec时,其容量不变,但移动时,其容量将被清除。因此,稍后当你填写bufvec时,会进行对数的分配以再次扩展其容量,这样的分配很容易成为你的性能瓶颈。

移动版本使 功能更快。但它使其他代码变慢。微优化不能可靠地使程序更快。

OP编辑:

Cheers and hth. - Alf在移动后m_bufvec.reserve(row.size())的评论中提出的解决方案解决了问题,并确认上述推理是正确的。此外,它更有效率(虽然只是略微),因为

  

你避免复制[在bufvec中]的项目。如果项目是简单的整数值,那么这并不重要。如果物品是例如字符串,动态分配,那真的很重要。

答案 1 :(得分:1)

确实第一个版本预计会更快。原因是:

auto row(m_bufvec);

调用copy constuctor,它立即为row分配必要的内存。 bufvec也保留其分配的内存。因此,分配 per-element 最小化,这很重要,因为它们涉及大量的重定位

在第二个版本中,auto row(std::move(m_bufvec)); bufvec的内存由row拥有,此操作比复制构造函数更快。但是当bufvec丢失了已分配的内存时,当您稍后逐个元素填充它时,它将执行许多重新分配和(昂贵的)重定位。重新分配的数量通常与矢量的最终大小成对数。

修改

以上解释了主要问题中的“意外”结果。最后,事实证明,此操作的“理想”是移动然后立即保留:

auto row(std::move(m_bufvec);
m_bufvec.reserve(row.size());
return row;

这实现了三个目标:

  • 没有按元素分配

  • bufvec

  • 没有无用的初始化
  • 不会将m_bufvec中的元素无用地复制到row