gpt4 book ai didi

directx - 加载/存储到 XMFLOAT4 比使用 XMVECTOR 更快?

转载 作者:行者123 更新时间:2023-12-02 20:58:40 28 4
gpt4 key购买 nike

我正在浏览 DirectX Math/XNA Math 库,当我读到 XMVECTOR 的对齐要求时,我很好奇(现在是 DirectX::XMVECTOR) ,以及在执行数学运算时如何使用 XMFLOAT* 作为成员,使用 XMLoad*XMStore*。我对权衡特别好奇,所以我做了一个实验,就像我相信许多其他人所做的那样,并进行了测试,以了解为每个操作加载和存储向量会损失多少。这是生成的代码:

#include <Windows.h>

#include <chrono>
#include <cstdint>
#include <DirectXMath.h>
#include <iostream>

using std::chrono::high_resolution_clock;

#define TEST_COUNT 1000000000l

int main(void)
{
DirectX::XMVECTOR v1 = DirectX::XMVectorSet(1, 2, 3, 4);
DirectX::XMVECTOR v2 = DirectX::XMVectorSet(2, 3, 4, 5);
DirectX::XMFLOAT4 x{ 1, 2, 3, 4 };
DirectX::XMFLOAT4 y{ 2, 3, 4, 5 };

std::chrono::system_clock::time_point start, end;
std::chrono::milliseconds duration;

// Test with just the XMVECTOR
start = high_resolution_clock::now();
for (uint64_t i = 0; i < TEST_COUNT; i++)
{
v1 = DirectX::XMVectorAdd(v1, v2);
}
end = high_resolution_clock::now();
duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

DirectX::XMFLOAT4 z;
DirectX::XMStoreFloat4(&z, v1);
std::cout << std::endl << "z = " << z.x << ", " << z.y << ", " << z.z << std::endl;
std::cout << duration.count() << " milliseconds" << std::endl;

// Now try with load/store
start = high_resolution_clock::now();
for (uint64_t i = 0; i < TEST_COUNT; i++)
{
v1 = DirectX::XMLoadFloat4(&x);
v2 = DirectX::XMLoadFloat4(&y);

v1 = DirectX::XMVectorAdd(v1, v2);
DirectX::XMStoreFloat4(&x, v1);
}
end = high_resolution_clock::now();
duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

std::cout << std::endl << "x = " << x.x << ", " << x.y << ", " << x.z << std::endl;
std::cout << duration.count() << " milliseconds" << std::endl;
}

运行调试构建会产生输出:

z = 3.35544e+007, 6.71089e+007, 6.71089e+007
25817 milliseconds

x = 3.35544e+007, 6.71089e+007, 6.71089e+007
84344 milliseconds

好吧,大约慢三倍,但是有人真的认真对待调试版本的性能测试吗?以下是我进行发布构建时的结果:

z = 3.35544e+007, 6.71089e+007, 6.71089e+007
1980 milliseconds

x = 3.35544e+007, 6.71089e+007, 6.71089e+007
670 milliseconds

就像魔术一样,XMFLOAT4 的运行速度几乎快了三倍!不知何故,形势发生了逆转。看看代码,这对我来说毫无意义;第二部分运行第一部分运行的命令的超集!一定是出了什么问题,或者是我没有考虑到的事情。很难相信编译器能够将第二部分比更简单且理论上更高效的第一部分优化九倍。我唯一合理的解释涉及 (1) 缓存行为,(2) XMVECTOR 无法利用的一些疯狂的无序执行,(3) 编译器正在进行一些疯狂的优化, (4) 直接使用 XMVECTOR 会产生一些隐含的低效率问题,而在使用 XMFLOAT4 时可以对其进行优化。也就是说,编译器从内存加载和存储 XMVECTOR 的默认方式比 XMLoad*XMStore* 效率低。我尝试检查反汇编,但我对 X86 和/或 SSE2 不太熟悉,并且 Visual Studio 做了一些疯狂的优化,使得很难跟踪源代码。我还尝试了 Visual Studio 性能分析工具,但这没有帮助,因为我不知道如何让它显示反汇编而不是代码。我从中得到的唯一有用的信息是,第一次调用 XMVectorAdd 约占所有周期的 48.6%,而第二次调用 XMVectorAdd 约占所有周期的 4.4%所有周期。

编辑:经过更多调试后,下面是在循环内运行的代码的程序集。对于第一部分:

002912E0  movups      xmm1,xmmword ptr [esp+18h]     <-- HERE
002912E5 add ecx,1
002912E8 movaps xmm0,xmm2 <-- HERE
002912EB adc esi,0
002912EE addps xmm0,xmm1
002912F1 movups xmmword ptr [esp+18h],xmm0 <-- HERE
002912F6 jne main+60h (0291300h)
002912F8 cmp ecx,3B9ACA00h
002912FE jb main+40h (02912E0h)

第二部分:

00291400  add         ecx,1  
00291403 addps xmm0,xmm1
00291406 adc esi,0
00291409 jne main+173h (0291413h)
0029140B cmp ecx,3B9ACA00h
00291411 jb main+160h (0291400h)

请注意,这两个循环确实几乎相同。唯一的区别是第一个 for 循环似乎是执行加载和存储的循环!由于 x 和 y 位于堆栈上,Visual Studio 似乎进行了大量优化。将它们都更改为位于堆上(因此必须进行写入),并且机器代码现在相同。一般情况是这样吗?使用存储类真的没有负面影响吗?我想除了完全优化的版本之外。

最佳答案

如果你定义

DirectX::XMVECTOR v3 = DirectX::XMVectorSet(2, 3, 4, 5);

并使用 v3 代替 v1 结果:...

 for (uint64_t i = 0; i < TEST_COUNT; i++)
{
v3 = DirectX::XMVectorAdd(v1, v2);
}

使用 XMLoadFloat4 和 XMStoreFloat4 获得的代码比第二部分代码更快

关于directx - 加载/存储到 XMFLOAT4 比使用 XMVECTOR 更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23419258/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com