std :: vector <uint8_t>手动复制,而不是在启用C ++ 11/14时调用memcpy

时间:2017-02-01 21:15:41

标签: c++ c++11 gcc c++14 stdvector

使用gcc 4.9,使用Linaro工具链对ARM进行交叉编译,我发现在添加public class HandPanel extends JPanel { CardImage cardImage; public HandPanel(Card card) { cardImage = new CardImage(card); } @Override public Dimension getPreferredSize() { BufferedImage img = cardImage.getImage(); return img == null ? super.getPreferredSize() : new Dimension(img.getWidth(), img.getHeight()); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); BufferedImage img = cardImage.getImage(); g.drawImage(img, 0, 0, this); } } 时,vector.assign()的编译结果会以一种造成严重性能问题的方式发生变化。

我尝试了几种不同的方式来执行此分配+复制,但只要我使用-std=c++14执行此操作,所有这些方法都会遇到此性能问题。

我可以通过这个玩具示例重现问题:

VectorTest.h

std::vector

VectorTest.cpp

#include <stdint.h>
#include <stddef.h>
#include <vector>

struct VectorWrapper_t
{
    VectorWrapper_t(uint8_t const* pData, size_t length);
    std::vector<uint8_t> data;
};

gcc flags:

#include "VectorTest.h"

VectorWrapper_t::VectorWrapper_t(uint8_t const* pData, size_t length)
{
    data.assign(pData, pData + length);
}

查看程序集,我可以看到原因:原始版本(C ++ 03,我假设?)调用-std=c++14 \ -mthumb -march=armv7-a -mtune=cortex-a9 \ -mlittle-endian -mfloat-abi=hard -mfpu=neon -Wa,-mimplicit-it=thumb \ -O2 -g ,而C ++ 14版本则添加了一个额外的循环看起来像手动复制数据。查看gcc与memmove添加的.loc代码,此循环中的说明来自-fverbose-asmstl_construct.h

更改为gcc 5.2.1(使用C ++ 14),它的编译几乎与C ++ 03示例完全相同,除了stl_uninitialized.h而不是memcpy

我可以在此处使用memmove代替std::unique_ptr<uint8_t[]>来解决此问题。但是,我想深入研究这个问题的底部,以确定使用vector的其他地方是否存在性能问题以及如何解决它们(更新到gcc 5.2并不实用)。 / p>

所以我的问题是:为什么在C ++ 11/14下编译的方式不同?

供参考,vector报告:
gcc --version

这是生成的程序集gcc:

arm-linux-gnueabihf-gcc (Linaro GCC 4.9-2014.12) 4.9.3 20141205 (prerelease)

1 个答案:

答案 0 :(得分:11)

这是4.9.2版本中的GCC错误,请参阅PR 64476。默认-std=gnu++03模式与-std=c++14之间的区别在于,对于C ++ 11及更高版本,它可能具有不可分配的普通类型(因为它们可以被删除)赋值运算符),它导致std::uninitialized_copy的实现采用不同的(较慢的)代码路径。对可转让性的检查是错误的,这意味着当我们不需要时,我们采取了缓慢的路径。

两年前我为GCC 4.9.3修复了它,但是你的编译器基于4.9.2和4.9.3版本之间的快照,并且还有几周的时间来修复它。

你可以要求Linaro将他们的GCC 4.9编译器更新到4.9.4,或者至少应用补丁来修复这个bug。