【问题标题】:pybind11 c++ unordered_map 10x slower than python dict?pybind11 c++ unordered_map 比 python dict 慢 10 倍?
【发布时间】:2022-01-01 21:03:48
【问题描述】:

我向 python 暴露了一个 c++ unordered_map<string, int>,结果发现这个映射比 python 的 dict 慢 10 倍。

参见下面的代码。

// map.cpp file
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <string>
#include <unordered_map>

namespace py = pybind11;

PYBIND11_MAKE_OPAQUE(std::unordered_map<std::string, int>);

PYBIND11_MODULE(map, m) {
    // map
    py::bind_map<std::unordered_map<std::string, int>>(m, "MapStr2Int");
}

在 MacOS 上,用这个 cmd 编译它:

c++ -O3 -std=c++14 -shared -fPIC -Wl,-undefined,dynamic_lookup $(python3 -m pybind11 --includes) map.cpp -o map$(python3-config --extension-suffix)

最后对比一下ipython中的pythondict

In [20]: import map

In [21]: c_dict = map.MapStr2Int()

In [22]: for i in range(100000):
    ...:     c_dict[str(i)] = i
    ...:

In [23]: py_dict = {w:i for w,i in c_dict.items()}

In [24]: arr = [str(i) for i in np.random.randint(0,100000, 100)]

In [25]: %timeit [c_dict[w] for w in arr]
59 µs ± 2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [26]: %timeit [py_dict[w] for w in arr]
6.58 µs ± 87.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

正如所见,c_dict 比 python 版本 py_dict 慢得多。

为什么以及如何改进?

【问题讨论】:

  • 不确定您的问题是什么?如果std::unordered_map 速度较慢,不要使用它?
  • 这似乎是 pybind11 的错,因为stackoverflow.com/questions/29268914/… 表明它们大致相等。在 pybind 的 github 上,您的问题已经提出了许多问题:github.com/pybind/pybind11/issues/1227github.com/pybind/pybind11/issues/2005 等。如果这有帮助,请见谅
  • 我希望 pybind11 需要分配新内存来构造 std::string 对象以传递给 unordered_map。 python版本不需要这样做。
  • @IainShelvington 不,我期待性能不会比 py 的 dict 慢,想知道我是否错过了任何技巧。
  • @Yolomep 感谢您的链接,看起来很相关。

标签: python c++ pybind11


【解决方案1】:

您正在比较一个本地字典实现(来自 Python 标准库的那个)和一个 pybind 包装的实现。我敢打赌,直接使用 std::unordered_map 的 C++ 程序肯定比用 Python 编写并使用 dict 的同等程序要快。

但这不是你在这里所做的。取而代之的是,您要求pybind 生成一个包装器,它将 Python 类型转换为 C++ 类型,调用 C++ 标准库类方法,然后将结果转换回 Python 类型。这些转换可能需要分配和解除分配,并且确实需要一些时间。此外,pybind 是一个非常聪明(因此很复杂)的工具。您不能期望它生成的代码与使用 Python API 直接调用一样优化。

除非您打算对散列函数使用特别优化的算法,否则您将无法编写比标准库更快的 C 或 C++ 代码,因为内置类型已经用 C 语言编码。顶多模仿一下,直接用Python/C API,应该能和标准库一样快。

【讨论】:

  • 我认为你应该冒险尝试一下。我的结果证实了 OP 所说的。 pydict 经过高度优化,在整数、小字符串和字符串方面优于 std:unordered_map。
  • @Jellyboy 在这里谢谢你们!坦白说,这真的让我对pybind11很困惑,我最初的动机只是用c++重写一些python函数,这样我就能得到更好的性能,但是这些函数需要一些dicts,所以我必须替换py dicts 与 c++ 映射,然后我得到了这个结果。不过很不高兴。
猜你喜欢
  • 2014-01-21
  • 2013-06-19
  • 2022-01-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-18
  • 1970-01-01
  • 2021-04-12
相关资源
最近更新 更多