【问题标题】:C++ - vector version implement of argsort low effiency compared to the one in numpyC++ - argsort 的矢量版本实现与 numpy 中的相比效率低
【发布时间】:2018-08-23 09:20:07
【问题描述】:

这是我做的比较。 np.argsort 在一个包含 1,000,000 个元素的 float32 ndarray 上进行计时。

In [1]: import numpy as np

In [2]: a = np.random.randn(1000000)

In [3]: a = a.astype(np.float32)

In [4]: %timeit np.argsort(a)
86.1 ms ± 1.59 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

这里有一个 C++ 程序执行相同的过程,但在引用 this answer 的向量上。

#include <iostream>
#include <vector>
#include <cstddef>
#include <algorithm>
#include <opencv2/opencv.hpp>
#include <numeric>
#include <utility>
int main()
{
  std::vector<float> numbers;
  for (int i = 0; i != 1000000; ++i) {
    numbers.push_back((float)rand() / (RAND_MAX));
  }

  double e1 = (double)cv::getTickCount();

  std::vector<size_t> idx(numbers.size());
  std::iota(idx.begin(), idx.end(), 0);

  std::sort(idx.begin(), idx.end(), [&numbers](const size_t &a, const size_t &b)
                                               { return numbers[a] < numbers[b];});

  double e2 = (double)cv::getTickCount();
  std::cout << "Finished in " << 1000 * (e2 - e1) / cv::getTickFrequency() << " milliseconds." << std::endl;
  return 0;
}

它打印 Finished in 525.908 milliseconds. 并且比 numpy 版本慢得多。那么谁能解释一下是什么让np.argsort 这么快?谢谢。


Edit1:np.__version__ 返回 1.15.0,它在 Python 3.6.6 |Anaconda custom (64-bit)g++ --version 上运行,打印 8.2.0。操作系统是 Manjaro Linux。


Edit2:我尝试在g++ 中使用-O2-O3 标志进行编译,并在216.515 毫秒和205.017 毫秒内得到结果。这是一个改进,但仍然比 numpy 版本慢。 (Referring to this question) 这被删除了,因为我错误地在拔下笔记本电脑的直流适配器的情况下运行测试,这会导致它变慢。在公平的竞争中,C 数组和向量版本的性能相同(大约需要 100 毫秒)。


Edit3:另一种方法是将向量替换为 C 类数组:float numbers[1000000];。之后运行时间约为 100 毫秒(+/- 5 毫秒)。完整代码在这里:

#include <iostream>
#include <vector>
#include <cstddef>
#include <algorithm>
#include <opencv2/opencv.hpp>
#include <numeric>
#include <utility>
int main()
{
  //std::vector<float> numbers;
  float numbers[1000000];
  for (int i = 0; i != 1000000; ++i) {
    numbers[i] = ((float)rand() / (RAND_MAX));
  }

  double e1 = (double)cv::getTickCount();

  std::vector<size_t> idx(1000000);
  std::iota(idx.begin(), idx.end(), 0);

  std::sort(idx.begin(), idx.end(), [&numbers](const size_t &a, const size_t &b)
                                               { return numbers[a] < numbers[b];});

  double e2 = (double)cv::getTickCount();
  std::cout << "Finished in " << 1000 * (e2 - e1) / cv::getTickFrequency() << " milliseconds." << std::endl;
  return 0;
}

【问题讨论】:

  • 你不应该只是在c++ 版本中为sort 计时吗?
  • @EdChum 但是vector idx 应该被看作是排序结果的容器,所以我认为它的初始化进程应该加入计时
  • 平台、编译器、numpy 版本(以及它的构建选项)?您是否进行了优化编译?在本地尝试这个,我得到 122ms 的 Numpy,115ms 的 c++....(当然是发布版本,调试要慢一个数量级)
  • 现在我使用 clang++ 和 libc++ 获得了几乎相同的运行时间(~107-110 ms)。 g++ 大约慢了 10%。
  • 我发现自己在时间上犯了一个非常幼稚的错误。如果拔掉 DC 适配器,我的笔记本电脑可能会为了省电而变慢。很抱歉之前和现在在公平的环境中提供误导性信息,向量和 C-array 的性能与@ead 所说的一样。

标签: python c++ performance numpy


【解决方案1】:

我采用了您的实现并使用 10000000 项目对其进行了测量。大约需要 1.7 秒。

现在我介绍了一个类

class valuePair {
  public:
    valuePair(int idx, float value) : idx(idx), value(value){};
    int idx;
    float value;
};

with 被初始化为

std::vector<valuePair> pairs;
for (int i = 0; i != 10000000; ++i) {
    pairs.push_back(valuePair(i, (double)rand() / (RAND_MAX)));
}

和排序比用

std::sort(pairs.begin(), pairs.end(), [&](const valuePair &a, const valuePair &b) { return a.value < b.value; });

此代码将运行时间缩短至 1.1 秒。我认为这是由于更好的缓存一致性,但与 python 的结果相距甚远。

【讨论】:

  • 那是因为 python 运行 10^6 而不是 10^7 元素:)
  • 但这是一个非常好的观察结果。我猜 argsort 没有以这种方式实现,因为这种方法需要额外的内存(并且您缺少将索引复制到结果数组的步骤)。
  • 我删除了一个 0 并在本地测试了您的代码。在std::vector&lt;valuePair&gt; pairs; 之前开始计时。它在大约 85 到 100 毫秒内完成。
  • 对于 10^7 个元素,IPython 报告1.67 s ± 47.8 ms per loop,代码运行大约 910 - 925 ms。也许应该考虑元素的规模?
  • 而且,在这里我尝试将原始版本修改为 10^7,大约在 1800 - 1900 毫秒内完成。对于 C 数组版本,我为三个数字添加了一个 0,但不知何故它核心转储了,并且..我与此无关。所以,干得好@schorsch312。
【解决方案2】:

想法:

  • 不同的底层算法:。 np.argsort 默认使用快速排序,C++ 中的实现可能取决于您的编译器。

  • 函数调用开销:我不确定C++ 编译器是否内联您的比较函数。如果没有,调用此函数也可能会引入一些开销。 根据this post 并非如此

  • 编译器标志?

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-01
  • 2018-06-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多