【问题标题】:Compute Shader execution time between DirectX11 and OpenGL计算 DirectX11 和 OpenGL 之间的着色器执行时间
【发布时间】:2021-10-31 04:39:27
【问题描述】:

我正在学习 DirectX 和 OpenGL 中的计算着色器

我写了一些代码来测试计算着色器并检查执行时间。

但 DirectX 的执行时间和 Opengl 的有一些差异

和上图表示相差多少(左边是DirectX,右边是Opengl,时间代表纳秒)

甚至 DirectX 计算着色器都比 cpu 慢

这是我计算两个向量总和的代码 一个用于计算着色器,一个用于 cpu

        std::vector<Data> dataA(32);
        std::vector<Data> dataB(32);

        for (int i = 0; i < 32; ++i)
        {
            dataA[i].v1 = glm::vec3(i, i, i);
            dataA[i].v2 = glm::vec2(i, 0);

            dataB[i].v1 = glm::vec3(-i, i, 0.0f);
            dataB[i].v2 = glm::vec2(0, -i);
        }

        InputBufferA = ShaderBuffer::Create(sizeof(Data), 32, BufferType::Read, dataA.data());
        InputBufferB = ShaderBuffer::Create(sizeof(Data), 32, BufferType::Read, dataB.data());
        OutputBufferA =ShaderBuffer::Create(sizeof(Data), 32, BufferType::ReadWrite);

        computeShader->Bind();
        InputBufferA->Bind(0, ShaderType::CS);
        InputBufferB->Bind(1, ShaderType::CS);
        OutputBufferA->Bind(0,ShaderType::CS);

        // Check The Compute Shader Calculation time
        std::chrono::system_clock::time_point time1 = std::chrono::system_clock::now();
        RenderCommand::DispatchCompute(1, 1, 1);
        std::chrono::system_clock::time_point time2 = std::chrono::system_clock::now();
        std::chrono::nanoseconds t =time2- time1;
        QCAT_CORE_INFO("Compute Shader time : {0}", t.count());
        
        // Check The Cpu Calculation time
        std::vector<Data> dataC(32);
        time1 = std::chrono::system_clock::now();
        for (int i = 0; i < 32; ++i)
        {
            dataC[i].v1 = (dataA[i].v1 + dataB[i].v1);
            dataC[i].v2 = (dataA[i].v2 + dataB[i].v2);
        }
        time2 = std::chrono::system_clock::now();
        t = time2 - time1;
        QCAT_CORE_INFO("CPU time : {0}", t.count() );

这里是 glsl 代码

#version 450 core
struct Data
{
    vec3 a;
    vec2 b;
};
layout(std430,binding =0) readonly buffer Data1
{
    Data input1[];
};

layout(std430,binding =1) readonly buffer Data2
{
    Data input2[];
};

layout(std430,binding =2) writeonly buffer Data3
{
    Data outputData[];
};

layout (local_size_x = 32, local_size_y = 1, local_size_z = 1) in;

void main()
{
  uint index = gl_GlobalInvocationID.x;

  outputData[index].a = input1[index].a + input2[index].a;
  outputData[index].b = input1[index].b + input2[index].b;
}

和 hlsl 代码


struct Data
{
    float3 v1;
    float2 v2;
};
StructuredBuffer<Data> gInputA : register(t0);
StructuredBuffer<Data> gInputB : register(t1);
RWStructuredBuffer<Data> gOutput : register(u0);

[numthreads(32,1,1)]
void CSMain(int3  dtid : SV_DispatchThreadID)
{
    gOutput[dtid.x].v1 = gInputA[dtid.x].v1 + gInputB[dtid.x].v1;
    gOutput[dtid.x].v2 = gInputA[dtid.x].v2 + gInputB[dtid.x].v2;
}

很简单的代码不是吗?

但 Opengl 的性能时间比 DirectX 的时间快 10 倍

我不明白为什么会发生这种情况,有什么东西会降低性能吗??

这是当我创建 RWStructuredBuffer 时唯一与 StructuredBuffer 不同的代码是 BindFlags = D3D11_BIND_SHADER_RESOURCE

        desc.Usage = D3D11_USAGE_DEFAULT;
        desc.ByteWidth = size * count;
        desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
        desc.CPUAccessFlags = 0;
        desc.StructureByteStride = size;
        desc.MiscFlags = D3D11_RESOURCE_MISC_BUFFER_STRUCTURED;

        D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc;
        uavDesc.Format = DXGI_FORMAT_UNKNOWN;
        uavDesc.ViewDimension = D3D11_UAV_DIMENSION_BUFFER;
        uavDesc.Buffer.FirstElement = 0;
        uavDesc.Buffer.Flags = 0;
        uavDesc.Buffer.NumElements = count;

在 opengl 中我以这种方式创建 SSBO

    glGenBuffers(1, &m_renderID);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_renderID);
    glBufferData(GL_SHADER_STORAGE_BUFFER, int(size * count), pData, GL_STATIC_DRAW);

这是两个 API 中执行计算着色器的所有代码

每个结果都表明 opengl 比 directx 好

是什么属性造成了这种差异?

是在 Buffer 还是 ShaderCode 中?

【问题讨论】:

  • 您根本没有测量 GPU 执行着色器所需的时间,您只测量了 GPU 驱动程序执行您的命令所需的时间。你得到的数字完全没有意义。
  • 即使 如果 你会测量 GPU 时间,这些数字仍然没有意义,因为你基本上只测量调度一个计算命令的开销,这是 巨大的 与着色器的实际工作量相比(实际上什么都没有)
  • @derhass 我们可以重新提出这个问题吗?实际上有很多话要说,这将使整个社区受益
  • @mrvux yo 获得了我的重新投票,不知道为什么它首先获得了近距离投票,因为它在很大程度上是负责任的。

标签: c++ opengl directx directx-11


【解决方案1】:

首先,如 cmets 中所述,您测量的不是 GPU 执行时间,而是记录命令本身的时间(gpu 将在稍后执行它,然后决定刷新命令)。

为了测量GPU执行时间,你需要使用Queries

在您的情况下(Direct3D11,但对于 OpenGL 类似),您需要创建 3 个查询:

  • 2 必须是 D3D11_QUERY_TIMESTAMP 类型(用于测量开始和结束时间)
  • 1 必须是 D3D11_QUERY_TIMESTAMP_DISJOINT 类型(不相交的查询将指示时间戳结果不再有效,例如如果您的 gpu 的时钟频率发生变化)。不相交的查询还会为您提供转换为毫秒所需的频率。

所以要测量你的 gpu 时间(在设备上下文中,你的问题如下):

 d3d11DeviceContext->Begin(yourDisjointQuery);
 d3d11DeviceContext->Begin(yourFirstTimeStampQuery);

 Dispatch call goes here

 d3d11DeviceContext->Begin(yourSecondTimeStampQuery);
 d3d11DeviceContext->Begin(yourDisjointQuery);

请注意,时间戳查询只是调用 begin,这很正常,您只需询问“gpu 时钟”即可。

然后就可以调用了(顺序无所谓):

d3d11DeviceContext->GetData(yourDisjointQuery);
d3d11DeviceContext->GetData(yourSecondTimeStampQuery);
d3d11DeviceContext->GetData(yourFirstTimeStampQuery);

检查不相交的结果是否不相交,并从中获取频率:

double delta = end - start;
double frequency;
double ticks = delta / (freq / 10000000);

那么现在为什么“只”记录该命令会花费大量时间而不是在 CPU 上执行相同的计算。

您只需对 32 个元素执行少量加法,这对于 CPU 来说是极其微不足道且快速的操作。

如果你开始增加元素数量,那么 GPU 最终会接管。

首先,如果您创建的 D3D 设备带有 DEBUG 标志,请删除该标志以进行配置文件。某些驱动程序(尤其是 NVIDIA)在使用该标志时的命令记录性能很差。

其次,当您调用 Dispatch 时,驱动程序将执行大量检查(检查资源的格式是否正确、步幅是否正确、资源是否仍然存在......)。 DirectX 驱动程序往往会进行大量检查,因此它可能比 GL 驱动程序稍慢(但不会慢到最后一点)。

最后,GPU/驱动程序可能会对着色器进行预热(某些驱动程序会将 dx 字节码异步转换为它们的本地对应字节码,因此当您调用时

device->CreateComputeShader();

它可能会立即完成或放入队列中(AME 负责队列,请参阅此链接Gpu Open Shader Compiler controls)。 如果您在此任务有效处理之前调用 Dispatch,您也可能需要等待。

还要注意,现在大多数 GPU 在磁盘上都有缓存,所以第一次编译/使用也可能会影响性能。

所以你应该尝试多次调用Dispatch,并检查第一次调用后CPU时序是否不同。

【讨论】:

  • 抱歉检查晚了,谢谢!这对我有很大帮助,我测试了大量元素,发现 gpu 和 cpu 之间的计算时间存在巨大差异。我所做的每一次调度在 dx 和 gl 之间都有 10000 纳秒的差距,正如你所说,我认为这是一种调试的东西,你的回答很清楚 :)
猜你喜欢
  • 2017-05-20
  • 2022-07-20
  • 1970-01-01
  • 1970-01-01
  • 2016-04-08
  • 2017-02-24
  • 1970-01-01
  • 1970-01-01
  • 2016-04-23
相关资源
最近更新 更多