我正在从 std::cin 读取值流,并从中构造一个 std::vector 。最初,我使用了带有临时 std::string 对象的 while 循环,并将 std::atof 与临时字符串中的 c_str() 一起使用。那里有一些电话,而且通常有很多事情发生。我用范围构造函数替换了它,使用 std::istream_iterator 和 std::cin ,认为它看起来更简单,而且更快。令我惊讶的是,虽然看起来确实更干净,但速度有点慢。
我的问题是这样的:为什么在下面的代码中,使用 std::istream_iterator 构造 std::vector 比使用函数调用混搭的替代方法慢?另外,是否有一种方法可以使用 std::istreambuf_iterator 来修改范围构造,以使两种方法的性能等效?我看到答案指出我应该将 std::ios_base::sync_with_stdio(false);
添加到代码中。虽然这提高了性能,但在两种情况下都是如此,并且两种方法之间仍然存在差异。
最小工作示例:
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
using namespace std;
int main()
{
/* Faster Method */
string temporary_line{};
vector<double> data{};
while(cin>> temporary_line)
data.push_back(atof(temporary_line.c_str()));
/* Slower Method */
//vector<double> data{ istream_iterator<double>{cin},
// stream_iterator<double>{} };
cout<< data.back() << '\n';
}
我通过 5 个不同的编译器(g++-{7,8} 和 clang++-{6,7,8})运行了代码。所有运行的代码均在 -O2 下编译,每次代表 5 次运行的平均值。时间很紧,增加更多的试验并不重要。结果显示所有编译器的行为相同,g++ 在这两种方法上仅比 clang++ 领先了一小部分。
要进行测试,请创建一个包含约 1,000,000 个随机整数的文件:
$ for i in {0..999999};执行 echo $RANDOM >> 数据文件;完成
编译:
$ g++ -o ds descriptive_statistics.cpp -O2
使用生成的示例数据运行:
$ time cat 数据文件 | ./ds
完整代码:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <memory>
#include <numeric>
#include <string>
#include <vector>
class DS {
public:
DS() = default;
DS(const DS& ) = default;
DS(DS&& ) = default;
DS(const double*, std::size_t length);
DS(const double*, const double*);
virtual ~DS() = default;
DS& operator=(const DS& ) = default;
DS& operator=(DS&& ) = default;
friend std::ostream& operator<<(std::ostream& , const DS& );
bool operator<(const DS& ) = delete;
bool operator>(const DS& ) = delete;
bool operator==(const DS& ) = delete;
private:
double min;
double first_quartile;
double mean;
double median;
double third_quartile;
double max;
double sum;
double variance;
double standard_deviation;
};
DS::DS(const double* begin, const double* end) {
const std::size_t size = std::distance(begin, end) ;
min = *begin;
first_quartile = begin[ size/4 ] ;
sum = std::accumulate(begin, end, double{});
mean = sum / size ;
const std::size_t idx{ size / 2 };
median = begin[ idx ] ;
if( ! (size & 1) ) {
median += begin[ idx - 1 ];
median *= 0.5;
}
third_quartile = begin[ 3*size/4 ] ;
variance = std::accumulate(begin, end, double{},
[&] (double a, double b) {
return a + std::pow(b - mean, 2.0);
}) / size ;
standard_deviation = std::sqrt(variance);
max = *std::prev(end);
}
DS::DS(const double* begin, std::size_t length) {
const double* end = begin + length;
*this = DS(begin,end);
}
std::ostream& operator<<(std::ostream& os, const DS& ds) {
os << ds.min << '\n'
<< ds.first_quartile << '\n'
<< ds.mean << '\n'
<< ds.median << '\n'
<< ds.third_quartile << '\n'
<< ds.max << '\n'
<< ds.sum << '\n'
<< ds.variance << '\n'
<< ds.standard_deviation << '\n';
return os;
}
int main(int argc, char** argv)
{
// This section is faster than the section below
std::string temporary_line{};
std::vector<double> data{};
while(std::cin>> temporary_line) {
data.push_back(std::atof(temporary_line.c_str()));
}
// This section is slower than the section above
// std::vector<double> data{
// std::istream_iterator<double>{std::cin},
// std::istream_iterator<double>{}
// };
if(! std::is_sorted(data.cbegin(), data.cend()) ) {
std::sort(data.begin(), data.end());
}
DS ds(&*data.cbegin(), &*data.cend());
std::cout<< ds << std::endl;
return(EXIT_SUCCESS);
}
最佳答案
看看 std::istream_iterator<double>
的实现你可以注意到这样做
std::vector<double> data{ std::istream_iterator<double>{file},
std::istream_iterator<double>{} };
实际上相当于做
double temporary_line;
std::vector<double> data{};
while (file>>temporary_line) {
data.push_back(temporary_line);
}
查看 godbolt 上汇编代码的差异
所以你的整个问题可以归结为为什么 std::atof
比 operator>>
快.
正如您在使用 gcc 的 O2 中注意到的那样,有一个对 strtod
的调用而不是call std::basic_istream<char, std::char_traits<char> >& std::basic_istream<char, std::char_traits<char> >::_M_extract<double>(double&)
https://www.godbolt.org/z/Od-FIk但代码结构基本相同。
我相信时差的原因是 locale
。 std::atof
部分忽略了 locale
(它看到 C 语言环境)另一方面 operator>>
使用指定 C++ locale
的约束进行解析工作最终使用 UNICODE 编码器。
进行更复杂的操作需要更多时间。但是考虑 UNICODE 和每个 locale
会产生 50% 的惩罚时间你不觉得那很糟糕吗?
关于c++ - 使用 istream_iterators 构造 vector 比使用 string/std::atof 的 while 循环慢,为什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57939299/