将数据从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));
}
这是最有效的方法吗?
答案 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 output和quick 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上)