在this答案中,我们可以看到:
我想使用
'\n'
或使用"\n"
几乎没有什么区别,但是后者是一个(两个)字符的数组,必须逐个字符地打印,为此必须循环设置,比输出单个字符要复杂。
强调我的
这对我来说很有意义。我认为输出const char*
需要一个循环,该循环将测试空终止符,与简单的putchar
相比,必须引入更多的操作(这并不意味着std::cout
与char
的代表进行了调用-只是简单地介绍了一个示例。
说服我使用
std::cout << '\n';
std::cout << ' ';
而不是
std::cout << "\n";
std::cout << " ";
这里值得一提的是,我知道性能差异几乎可以忽略不计。但是,有些人可能会争辩说,前一种方法的意图是实际上传递单个字符,而不是恰好是一个char
长(两个 char
如果您算上'\0'
,就可以。)
最近,我为使用后一种方法的人做了一些代码修改。我对此案发表了一点评论,然后继续。然后,开发人员感谢我,并说他甚至都没有想到这种差异(主要集中在意图上)。完全没有影响(毫不奇怪),但是更改被采用。
然后我开始怀疑该变化到底有多精确,所以我狂奔。令我惊讶的是,它在带有-std=c++17 -O3
标志的GCC(干线)上进行测试时显示了following results。为以下代码生成的程序集:
#include <iostream>
void str() {
std::cout << "\n";
}
void chr() {
std::cout << '\n';
}
int main() {
str();
chr();
}
令我惊讶的是,看来chr()
实际上生成的指令数量实际上是str()
的两倍:
.LC0:
.string "\n"
str():
mov edx, 1
mov esi, OFFSET FLAT:.LC0
mov edi, OFFSET FLAT:_ZSt4cout
jmp std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
chr():
sub rsp, 24
mov edx, 1
mov edi, OFFSET FLAT:_ZSt4cout
lea rsi, [rsp+15]
mov BYTE PTR [rsp+15], 10
call std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long)
add rsp, 24
ret
那是为什么?为什么他们两个最终都使用std::basic_ostream
参数调用相同的const char*
函数?这是否意味着char
文字方法不仅比字符串文字方法更好,而且实际上更糟?
答案 0 :(得分:13)
没有其他答案能真正解释为什么编译器会生成您在Godbolt链接中执行的代码,所以我认为我会参与其中。
如果查看生成的代码,则可以看到:
std::cout << '\n';
有效地编译为:
char c = '\n';
std::cout.operator<< (&c, 1);
并且要使其工作,编译器必须为函数chr()
生成一个堆栈框架,这是许多额外指令的来源。
另一方面,在编译时:
std::cout << "\n";
编译器可以优化str()
来简单地“尾调用” operator<< (const char *)
,这意味着不需要堆栈帧。
因此,将调用operator<<
放在单独的函数中会导致您的结果有些偏差。内联进行这些调用更具启发性,请参见:https://godbolt.org/z/OO-8dS
现在您可以看到,尽管输出'\n'
仍然要贵一些,但差异没有您的示例那么明显。
答案 1 :(得分:3)
是的,对于这个特定的实现,例如,char
版本比字符串版本慢一点。
两个版本都调用write(buffer, bufferSize)
样式函数。对于字符串版本,bufferSize
在编译时(1字节)是已知的,因此无需查找零终止符运行时。对于char
版本,编译器会在堆栈上创建一个1字节的缓冲区,将字符放入其中,然后将该缓冲区传递出去以进行写出。因此,char
版本要慢一些。
答案 2 :(得分:2)
请记住,虽然您在程序集中看到的只是调用栈的创建,而不是实际函数的执行。
std::cout << '\n';
仍然比std::cout << "\n";
我创建了这个小程序来测量性能,在使用g ++ -O3的计算机上,它的运行速度大约快20倍。自己尝试!
编辑:对不起,我的程序中发现了错别字,而且速度没有那么快!几乎无法衡量任何差异。有时一个更快。其他时间其他。
#include <chrono>
#include <iostream>
class timer {
private:
decltype(std::chrono::high_resolution_clock::now()) begin, end;
public:
void
start() {
begin = std::chrono::high_resolution_clock::now();
}
void
stop() {
end = std::chrono::high_resolution_clock::now();
}
template<typename T>
auto
duration() const {
return std::chrono::duration_cast<T>(end - begin).count();
}
auto
nanoseconds() const {
return duration<std::chrono::nanoseconds>();
}
void
printNS() const {
std::cout << "Nanoseconds: " << nanoseconds() << std::endl;
}
};
int
main(int argc, char** argv) {
timer t1;
t1.start();
for (int i{0}; 10000 > i; ++i) {
std::cout << '\n';
}
t1.stop();
timer t2;
t2.start();
for (int i{0}; 10000 > i; ++i) {
std::cout << "\n";
}
t2.stop();
t1.printNS();
t2.printNS();
}
编辑:正如geza所建议的,我为两者都尝试了100000000次迭代,并将其发送到/ dev / null并运行了四次。 '\ n'曾经慢一点,快了3倍,但速度却不高,但是在其他机器上可能有所不同:
Nanoseconds: 8668263707
Nanoseconds: 7236055911
Nanoseconds: 10704225268
Nanoseconds: 10735594417
Nanoseconds: 10670389416
Nanoseconds: 10658991348
Nanoseconds: 7199981327
Nanoseconds: 6753044774
我想总体上我不会太在意。
答案 3 :(得分:-9)
建议不要使用std :: endl代替“ \ n”,以提高可读性。