为什么 PyTorch C++ 扩展比其等效的 numba 版本慢得多?

2023-12-20

我一直在尝试各种选项来加速 PyTorch 中的一些 for 循环逻辑。这样做的两个明显的选择是使用numba https://stackoverflow.com/a/75580380/1804173 or 编写自定义 C++ 扩展 https://pytorch.org/tutorials/advanced/cpp_extension.html.

作为一个例子,我从数字信号处理中选择了“可变长度延迟线”。使用简单的 Python for 循环可以简单但低效地编写此代码:

def delay_line(samples, delays):
    """
    :param samples: Float tensor of shape (N,)
    :param delays: Int tensor of shape (N,)
    
    The goal is basically to mix each `samples[i]` with the delayed sample
    specified by a per-sample `delays[i]`.
    """
    for i in range(len(samples)):
        delay = int(delays[i].item())
        index_delayed = i - delay
        if index_delayed < 0:
            index_delayed = 0

        samples[i] = 0.5 * (samples[i] + samples[index_delayed])

知道 for 循环在 Python 中的执行情况有多糟糕,我希望通过在 C++ 中实现相同的循环可以获得明显更好的性能。下列的教程 https://pytorch.org/tutorials/advanced/cpp_extension.html,我想出了从 Python 到 C++ 的直译:

void delay_line(torch::Tensor samples, torch::Tensor delays) {

  int64_t input_size = samples.size(-1);

  for (int64_t i = 0; i < input_size; ++i) {
    int64_t delay = delays[i].item<int64_t>();
    int64_t index_delayed = i - delay;
    if (index_delayed < 0) {
      index_delayed = 0;
    }

    samples[i] = 0.5 * (samples[i] + samples[index_delayed]);
  }
}

我还采用了 Python 函数并将其包装到各种 jit 装饰器中以获得该函数的 numba 和 torchscript 版本(请参阅我的其他answer https://stackoverflow.com/a/75580380/1804173有关 numba 包装的详细信息)。然后,我对所有版本执行了基准测试,这还取决于张量是驻留在 CPU 还是 GPU 上。结果相当令人惊讶:

╭──────────────┬──────────┬────────────────────╮
│ Method       │ Device   │   Median time [ms] │
├──────────────┼──────────┼────────────────────┤
│ plain_python │ CPU      │             13.481 │
│ torchscript  │ CPU      │              6.318 │
│ numba        │ CPU      │              0.016 │
│ cpp          │ CPU      │              9.056 │
│ plain_python │ GPU      │             45.412 │
│ torchscript  │ GPU      │             47.809 │
│ numba        │ GPU      │              0.236 │
│ cpp          │ GPU      │             31.145 │
╰──────────────┴──────────┴────────────────────╯

Notes: sample buffer size was fixed to 1024; results are medians of 100 executions to ignore artifacts from the initial jit overhead; input data creation and moving it to the device is excluded from the measurements; full benchmark script gist https://gist.github.com/bluenote10/3370da06204b94995614ed014410f6c2

最显着的结果:C++ 变体似乎出奇地慢。 numba 快两个数量级的事实表明问题确实可以更快地解决。事实上,C++ 变体仍然非常接近众所周知的缓慢的 Python for 循环,这可能表明有些事情不太正确。

我想知道什么可以解释 C++ 扩展的糟糕性能。第一个想到的就是缺少优化。不过,我已经确保编译使用了优化。切换自-O2 to -O3也没有什么区别。

为了隔离 pybind11 函数调用的开销,我用空函数体替换了 C++ 函数,即不执行任何操作。这将时间减少到 2-3μs,这意味着时间确实花在该特定函数体上。

有什么想法为什么我会观察到如此糟糕的性能吗?我可以在 C++ 方面做些什么来匹配 numba 实现的性能吗?

额外问题:GPU 版本是否会比 CPU 版本慢很多?


None

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 PyTorch C++ 扩展比其等效的 numba 版本慢得多? 的相关文章

  • 将 C++ 代码(本机客户端)移植到浏览器(Web 应用程序)

    我有一个使用 Qt creator SDK 编写的 C 模块 我想将此代码移植到任何网页上运行 而不会对最终用户损害源代码 用户应该能够在任何浏览器 Chrome Firefox Safari Explorer 上看到此模块的输出 而无需安
  • 如何使用带有进度条的 HttpClient 下载文件?

    我创建了一个名为SiteDownload并添加了一些下载图像的链接 using System Collections Generic using System Linq using System Net using System Threa
  • C++,多语言/本地化支持

    向 C 程序添加多语言支持的最佳方法是什么 如果可能 应该从包含键值对 WelcomeMessage Hello s 之类的纯文本文件中读取语言 我想到了添加一个 localizedString key 函数来返回加载的语言文件的字符串 有
  • fork 和 exec 之间的区别

    两者有什么区别fork and exec 指某东西的用途fork and exec它体现了 UNIX 的精神 它提供了一种非常简单的方法来启动新进程 The fork调用基本上复制了当前进程 在almost任何方式 并非所有内容都会被复制
  • C++ 标准是否允许未初始化的 bool 导致程序崩溃?

    我知道一个 未定义的行为 C 几乎可以让编译器做任何它想做的事情 然而 我遇到了一次令我惊讶的崩溃 因为我认为代码足够安全 在这种情况下 真正的问题仅发生在使用特定编译器的特定平台上 并且仅在启用优化的情况下发生 我尝试了几种方法来重现问题
  • 氧图。如何将轴旁边的值格式从 1000 更改为 1k

    我正在尝试更改轴旁边的值的格式 例如从 1000 更改为 1k 或 1000000 更改为 1M 这在 LinearAxis 中可能吗 这是我的代码 m Axes Add new LinearAxis Position AxisPositi
  • 委托和接口如何互换使用?

    我可以使用接口方法代替委托吗 如何 我发现搜索接口方法比使用委托更快 我希望有一个简单的代码片段 理论上 可以通过包含单个方法的接口 例如 Java 没有委托 来完成委托完成的所有工作 然而 它使代码变得更加冗长并且没有带来什么好处 话又说
  • lambda 表达式到函数指针的转换

    这是这个问题的后续问题 Lambda 如何作为参数传递 https stackoverflow com questions 3321283 c0x lambda how can i pass as a parameter 据推测 MSDN
  • Visual Studio 2013 Intellisense 不会将枚举类型放在方法参数的位置

    例如 我有以下代码 namespace VS2013 EnumTypes class Program enum SomeEnum One Two static void SomeMethod SomeEnum someEnum some c
  • WCF 客户端返回空数组 - XML 响应似乎正常

    我正在尝试为我们的 Intranet 上托管的 Web 服务创建一个简单的 WCF 客户端 C 使用 Fiddler 和 SoapUI 我可以看到请求和响应似乎正常 但是当我运行代码时返回一个空数组 我会尝试只粘贴相关的行 但会是很多东西
  • C++ fill() 与 uninitialized_fill()

    您好 我是初学者 我想知道容器的 fill 和 uninitialized fill 之间的区别 我在谷歌上进行了快速搜索 但没有得到很好的答案 有人可以帮助我吗 fill 将值 使用赋值运算符 分配给已构造的对象 uninitialize
  • Bool类型返回规则

    我使用 dapper ORM 所以我使用两个规则Query
  • 如何在 .NET 中自定义 JSON 枚举的反序列化?

    我有以下示例 C 代码 它是使用 svcutil exe 应用程序从 xsd 自动生成的 DataContract public enum Foo EnumMember Value bar Bar 1 EnumMember Value ba
  • 使用 unrar 库 - 将文件提取到文件流缓冲区中

    我需要的是能够将 rar 文件中的文件提取到流中 我正在创建一个测试用例来了解如何使用解压源文件 http www rarlab com rar unrarsrc 3 9 9 tar gz 我已经搜索和修补了一段时间 但我不知道如何使用该库
  • C# 的 xml 序列化中是否有一个属性可以跳过空数组?

    C 的 xml 序列化中是否有一个属性可以跳过空数组 这将提高 xml 输出的可读性 好吧 你也许可以添加一个ShouldSerializeFoo method using System using System ComponentMode
  • Lambda 按值捕获和“mutable”关键字

    关键词的必要性mutable在 lambda 中 是造成极大混乱的根源 考虑代码 int x 10 function
  • int 类型的构造函数

    考虑到成本 这些情况是否相同 case 1 int a 5 case 2 int a 5 case 3 int a a 5 这三种语法是不同的 请耐心等待 我使用用户定义类型而不是 int 稍后我将回到 int T a 5 Direct i
  • 在 Visual Studio C++ 资源编辑器中导入 png 文件

    我希望能够在 Visual Studio 资源编辑器中导入 png 文件 以便能够在不同的其他项目中使用嵌入的资源 有解决办法吗 我知道它适用于位图 但我对 png 感兴趣 因为即使在较低格式 16x16 或 32x32 上也可以使用 透明
  • 返回右值 - 这段代码有什么问题? [复制]

    这个问题在这里已经有答案了 我遇到了以下代码片段 std string test std string m Hello return std move m int main std string m test 我知道上面的代码是不正确且不安
  • 如何使用 __m128i 执行元素左移?

    我发现 SSE 移位指令只能在所有元素上移位相同的量 mm sll epi32 mm slli epi32 这些会移动所有元素 但移动量相同 http software intel com sites products documentat

随机推荐