【问题标题】:ostream_iterator vs for each loop efficiencyostream_iterator vs for each 循环效率
【发布时间】:2018-05-07 23:28:07
【问题描述】:

我看到了这个用户post yesterday。我认为这是输出向量的一种很酷的方式。因此,我输入了一个示例并问自己这与for each 循环相比如何?

template <typename T>
void printVectorO(std::vector<T> &v)
{
    std::cout << "Ostream_iterator contents: " << std::endl;
    auto start = std::chrono::high_resolution_clock::now();
    std::ostream_iterator<T> ost(std::cout, " ");
    std::copy(begin(v), end(v), ost);
    std::cout << std::endl;

    auto end = std::chrono::high_resolution_clock::now();
    auto time = end - start;
    auto nano = std::chrono::duration_cast<std::chrono::nanoseconds>(time);
    std::cout << "Ostream_iterator computation took: " << nano.count() << " nano seconds"<< std::endl;
    std::cout << std::endl;
}

template <typename T>
void printVectorC(std::vector<T> &v)
{
    std::cout << "For Each Loop contents: " << std::endl;
    auto start = std::chrono::high_resolution_clock::now();
    for (auto && e : v) std::cout << e << " ";
    std::cout << std::endl;

    auto end = std::chrono::high_resolution_clock::now();
    auto time = end - start;
    auto nano = std::chrono::duration_cast<std::chrono::nanoseconds>(time);
    std::cout << "For Each Loop took: " << nano.count() << " nano seconds" << std::endl;
    std::cout << std::endl;
}

我用了 3 个向量来测试这个:

std::vector<double> doubles = { 3.15, 2.17, 2.555, 2.014 };
std::vector<std::string> strings = { "Hi", "how", "are", "you" };
std::vector<int> ints = { 3, 2 , 2 , 2 };

我得到了各种结果。当我输出双打时,for each 循环总是优于ostream_iterator(例如 41856 与 11207 和 55198 与 10308 纳秒)。有时,字符串ostream_iterator 胜过for each 循环,而for each 循环和ostream_iterator 几乎与整数并驾齐驱。

这是为什么? ostream_iterator 的幕后发生了什么?在效率和速度方面,我什么时候会在 for each 循环上使用 ostream_iterator

【问题讨论】:

  • 建议添加编译器和优化级别,以帮助建立参考框架。
  • 也许在您对copy 的调用周围添加一个循环,并在您的for 周围添加一个循环,以重复感兴趣的代码一百万次,以排除可能影响您的时间安排的其他事情。
  • 除了 jxh 的注释之外,也许添加一个循环来生成几十万个值放入向量中,这样差异会更加明显(即检查差异是否总是几纳秒 vs . 如果它们按比例增长)。
  • 我同意,只有三个值,任何指标都是没有意义的。尝试几千个值。也尝试输出到std::ostringstream 而不是std::cout。输出到 std::cout 可能会反映有关输出缓冲区刷新的一些伪影。
  • 在控制台上显示字符(和滚动控制台)的成本将使两种迭代策略的可忽略的性能差异相形见绌。基准测试有意义的唯一方法是流式传输到 std::ostream ,其基础字符串已经具有足够的容量来防止重新分配。另一种选择是创建自己的 streambuf 派生流缓冲区并将其分配给 ostream。

标签: c++ performance foreach iterator


【解决方案1】:

小心微基准测试。

我有几个关于此代码的通用 cmet:

  1. 将只读变量作为 const 引用而不是常规引用传递。不过,这不会影响性能
  2. 不要使用 std::endl ,因为它调用了 flush() ,最终在这样的微基准测试中占用了您的大部分运行时间。例如,使用 std::endl 打印双精度数需要 37010 ns,而使用 '\n' 仅需要 4456 ns
  3. 单次测量不准确。为了消除任何测量噪声,您必须在循环中多次运行它。这仍然不完美,因为最好的办法是交替运行测试(产生随机事件,这会减慢代码速度,以相同的方式影响两种实现)
  4. 最好将其重定向到文件,否则终端速度将决定结果。

这是修正后的基准:

constexpr unsigned ITERATIONS = 1000000;
template <typename T>
void printVectorO(const std::vector<T> &v)
{
    std::cout << "Ostream_iterator contents\n";
    auto start = std::chrono::high_resolution_clock::now();
    for (unsigned i=0 ; i < ITERATIONS; ++i) {
        std::ostream_iterator<T> ost(std::cout, " ");
        std::copy(begin(v), end(v), ost);
        std::cout << '\n';
    }

    auto end = std::chrono::high_resolution_clock::now();
    auto time = end - start;
    auto nano = std::chrono::duration_cast<std::chrono::nanoseconds>(time);
    std::cout << "Ostream_iterator computation took: "
              << nano.count() / ITERATIONS << " nano seconds\n\n";
}

template <typename T>
void printVectorC(const std::vector<T> &v)
{
    std::cout << "For Each Loop contents\n";
    auto start = std::chrono::high_resolution_clock::now();
    for (unsigned i=0 ; i < ITERATIONS ; ++i) {
        for (auto && e : v) std::cout << e << " ";
        std::cout << '\n';
    }

    auto end = std::chrono::high_resolution_clock::now();
    auto time = end - start;
    auto nano = std::chrono::duration_cast<std::chrono::nanoseconds>(time);
    std::cout << "For Each Loop took: "
              << nano.count() / ITERATIONS << " nano seconds\n\n";
}

并通过以下方式调用它:

template <class Container>
void test(const Container & ctr)
{
    printVectorC2(ctr);
    printVectorO2(ctr);
}


int main()
{
    std::vector<double> doubles = { 3.15, 2.17, 2.555, 2.014 };
    test(doubles);
    std::vector<std::string> strings = { "Hi", "how", "are", "you" };
    test(strings);
    std::vector<int> ints = { 3, 2 , 2 , 2 };
    test(ints);
}

现在,在搜索 nano 之后,我们有:

For Each Loop took: 2045 nano seconds
Ostream_iterator computation took: 2033 nano seconds
For Each Loop took: 487 nano seconds
Ostream_iterator computation took: 485 nano seconds
For Each Loop took: 503 nano seconds
Ostream_iterator computation took: 499 nano seconds

几乎没有任何区别。实际上,通过这种特定的运行,似乎 ostream 版本更快。但是再次运行它会得到稍微不同的结果。

【讨论】:

    猜你喜欢
    • 2014-10-19
    • 2013-01-26
    • 1970-01-01
    • 1970-01-01
    • 2015-11-12
    • 1970-01-01
    • 2015-11-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多