3

我们有一个使用 LibTorch 1.5.0 的工作库,使用 CUDA 10.0 构建,可以按预期运行。

出于各种与 PyTorch 无关的原因,我们正在努力升级到 CUDA 10.2。我们注意到,当我们在新编译的 LibTorch 上运行 LibTorch 推理时(编译时完全相同,除了更改为 CUDA 10.2),运行时间大约慢了 20 倍。我们还使用预编译的二进制文件对其进行了检查。

这是在 3 台不同的机器上使用 3 个不同的 GPU(Tesla T4、GTX980 和 P1000)进行测试的,并且在 CUDA 10.2(Windows 10 和 Ubuntu 16.04 上)上都给出了一致的 ~20 倍慢,所有这些都使用最新的驱动程序和 3 个不同的火炬脚本(相同架构)

我已将代码简化为极少,无需 Torch 以外的外部依赖项

int main(int argc, char** argv)
{
    // Initialize CUDA device 0
    cudaSetDevice(0);

    std::string networkPath = DEFAULT_TORCH_SCRIPT;
    if (argc > 1)
    {
        networkPath = argv[1];
    }
    auto jitModule = std::make_shared<torch::jit::Module>(torch::jit::load(networkPath, torch::kCUDA));
    if (jitModule == nullptr)
    {
        std::cerr << "Failed creating module" << std::endl;
        return EXIT_FAILURE;
    }

    // Meaningless data, just something to pass to the module to run on
    // PATCH_HEIGHT & WIDTH are defined as 256
    uint8_t* data = new uint8_t[PATCH_HEIGHT * PATCH_WIDTH * 3];
    memset(data, 0, PATCH_HEIGHT * PATCH_WIDTH * 3);
    
    auto stream = at::cuda::getStreamFromPool(true, 0);

    bool res = infer(jitModule, stream, data, PATCH_WIDTH, PATCH_HEIGHT);

    std::cout << "Warmed up" << std::endl;


    res = infer(jitModule, stream, data, PATCH_WIDTH, PATCH_HEIGHT);

    delete[] data;
    return 0;
}

// Inference function

bool infer(std::shared_ptr<JitModule>& jitModule, at::cuda::CUDAStream& stream, const uint8_t* inputData, int width, int height)
{
    std::vector<torch::jit::IValue> tensorInput;
    // This function simply uses cudaMemcpy to copy to device and create a torch::Tensor from that data 
    // I can paste it if it's relevant but didn't now to keep as clean as possible
    if (!prepareInput(inputData, width, height, tensorInput, stream))
    {
        return false;
    }
    // Reduce memory usage, without gradients
    torch::NoGradGuard noGrad;
    {
        at::cuda::CUDAStreamGuard streamGuard(stream);
        
        auto totalTimeStart = std::chrono::high_resolution_clock::now();

        jitModule->forward(tensorInput);
        // The synchronize here is just for timing sake, not use in production
        cudaStreamSynchronize(stream.stream());
        
        auto totalTimeStop = std::chrono::high_resolution_clock::now();
        printf("forward sync time = %.3f milliseconds\n",
            std::chrono::duration<double, std::milli>(totalTimeStop - totalTimeStart).count());
    }

    return true;
}

当使用使用 CUDA 10.0 编译的 Torch 编译它时,我们得到一个运行时18 ms,当我们使用使用 CUDA 10.2 编译的 Torch 运行它时,我们得到一个运行时430 ms

对此有什么想法吗?

这个问题也发布在PyTorch 论坛上。

GitHub 上的问题

更新

我使用两个 CUDA 分析了这个小程序

似乎两者都使用非常不同的内核

conv2d_grouped_direct_kernel在我的 P1000 上,96.5% 的 10.2 计算需要大约 60-100 毫秒

10.0 运行中的顶级内核在哪里

47.1% - cudnn::detail::implicit_convolve_sgemm (~1.5 ms)
23.1% - maxwell_scudnn_winograd_128x128_ldg1_ldg4_tile148n_nt (~0.4 ms)
8.5%  - maxwell_scudnn_128x32_relu_small_nn (~0.4ms)

所以很容易看出时差是从哪里来的。现在的问题是,为什么。

4

0 回答 0