将数据从未对齐结构的数组移动到C ++中的对齐数组

时间:2018-08-15 10:16:22

标签: c++ memory optimization

将数据从CameraSpacePoint数组移动到PointXYZ数组的最佳方法是什么?

struct CameraSpacePoint
{
    float X;
    float Y;
    float Z;
};

 __declspec(align(16))
 struct PointXYZ
 {
      float x;
      float y;
      float z;
 };

 constexpr int BIG_VAL = 1920 * 1080;

 CameraSpacePoint  camera_space_points[BIG_VAL];
 PointXYZ          points_xyz[BIG_VAL];

我的解决方案:

CameraSpacePoint* camera_space_points_ptr = &camera_space_points[0];
PointXYZ*         points_xyz_ptr          = &points_xyz[0];

for (int i = 0; i < BIG_VAL; ++i)
{
    memcpy(points_xyz_ptr++, camera_space_points_ptr++, sizeof(CameraSpacePoint));
}

这是最有效的方法吗?

2 个答案:

答案 0 :(得分:3)

与往常一样,可读性和可维护性胜过其他问题。写下您的意思,而不要解决什么不是问题:在优化之前先进行测量。

std::transform(camera_space_points, std::end(camera_space_points), points_xyz,
    [](auto c){
        return PointXYZ{c.X, c.Y, c.Z};
    });

这是您应始终默认编写的内容。通过他们的assembly outputquick benchmark,这与memcpy版本相当。

更令人费解的是,优化器确实擅长微优化简单代码,例如复制大量内存,而手动优化却很少见。

答案 1 :(得分:2)

另一种方法是确保复制16个字节的块。这样一来,就可以更好地优化指令,该指令可以一次复制16个字节(不存在任何乱序或其他不必要的复杂性)(如果它们存在于x64上,例如Xbox One和PC上,则可以提供帮助),从而一次复制16个字节。 PointXYZ将为16个字节,因此可以向它们写入16个字节。源包含12个字节的元素,因此每次以这种方式复制其中一个元素时,其中的下一个元素也会有4个字节,它们以目标PointXYZ的填充结尾,将被忽略。 last CameraSpacePoint不一定在其后有4个可读字节,它可能恰好在未映射/不可读的内存区域之前结束,因此我们需要注意不要进一步读取-除非那样数组可以扩展一点以保证内存存在。

例如:

auto dst = ::dst;
auto src = ::src;
for (int i = 0; i + 1 < BIG_VAL; ++i)
    std::memcpy(dst++, src++, 16);
// last point is special, since the src may not have 16 bytes left to read
std::memcpy(dst, src, sizeof(CameraSpacePoint));

(在godbolt上)