简体   繁体   English

为什么 '\\n' 优先于输出流的“\\n”?

[英]Why is '\n' preferred over "\n" for output streams?

In this answer we can read that:这个答案中,我们可以读到:

I suppose there's little difference between using '\\n' or using "\\n" , but the latter is an array of (two) characters, which has to be printed character by character, for which a loop has to be set up, which is more complex than outputting a single character .我想使用'\\n'或使用"\\n"之间几乎没有区别,但后者是一个(两个)字符的数组,必须逐个字符地打印,必须为其设置一个循环,其中比输出单个字符更复杂

emphasis mine强调我的

That makes sense to me.这对我来说很有意义。 I would think that outputting a const char* requires a loop which will test for null-terminator, which must introduce more operations than, let's say, a simple putchar (not implying that std::cout with char delegates to calling that - it's just a simplification to introduce an example).我认为输出一个const char*需要一个循环来测试空终止符,它必须引入比简单的putchar更多的操作(并不意味着带有char std::cout委托调用它 - 这只是介绍一个例子的简化)。

That convinced me to use这说服我使用

std::cout << '\n';
std::cout << ' ';

rather than而不是

std::cout << "\n";
std::cout << " ";

It's worth to mention here that I am aware of the performance difference being pretty much negligible.这里值得一提的是,我知道性能差异几乎可以忽略不计。 Nonetheless, some may argue that the former approach carries intent of actually passing a single character, rather than a string literal that just happened to be a one char long ( two char s long if you count the '\\0' ).尽管如此,有些人可能会争辩说,前一种方法的意图是实际传递单个字符,而不是恰好是一个char长的字符串文字(如果算上'\\0'则是两个char长)。

Lately I've done some little code reviews for someone who was using the latter approach.最近,我为使用后一种方法的人做了一些小的代码审查。 I made a small comment on the case and moved on.我对这个案子做了一个小小的评论,然后继续前进。 The developer then thanked me and said that he hadn't even thought of such difference (mainly focusing on the intent).开发者随后感谢我并说他根本没有想到这种差异(主要是在意图上)。 It was not impactful at all (unsurprisingly), but the change was adopted.它根本没有影响(不出所料),但更改被采用。

I then began wondering how exactly is that change significant, so I ran to godbolt.然后我开始想知道这个变化到底多大意义,所以我跑到了 Godbolt。 To my surprise, it showed the following results when tested on GCC (trunk) with -std=c++17 -O3 flags.令我惊讶的是,在带有-std=c++17 -O3标志的 GCC(主干)上测试时,它显示了以下结果 The generated assembly for the following code:为以下代码生成的程序集:

#include <iostream>

void str() {
    std::cout << "\n";
}

void chr() {
    std::cout << '\n';
}

int main() {
    str();
    chr();
}

surprised me, because it appears that chr() is actually generating exactly twice as many instructions as str() does:我很惊讶,因为看起来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

Why is that?这是为什么? Why both of them eventually call the same std::basic_ostream function with const char* argument?为什么他们最终都使用const char*参数调用相同的std::basic_ostream函数? Does it mean that the char literal approach is not only not better , but actually worse than string literal one?这是否意味着char文字方法不仅比字符串文字方法更好,而且实际上更糟

None of the other answers really explain why the compiler generates the code it does in your Godbolt link, so I thought I'd chip in.其他答案都没有真正解释为什么编译器会生成它在您的 Godbolt 链接中执行的代码,所以我想我会插手。

If you look at the generated code, you can see that:如果您查看生成的代码,您可以看到:

std::cout << '\n';

Compiles down to, in effect:编译为,实际上:

const char c = '\n';
std::cout.operator<< (&c, 1);

and to make this work, the compiler has to generate a stack frame for function chr() , which is where many of the extra instructions come from.为了使这项工作,编译器必须为函数chr()生成一个堆栈帧,这是许多额外指令的来源。

On the other hand, when compiling this:另一方面,在编译时:

std::cout << "\n";

the compiler can optimise str() to simply 'tail call' operator<< (const char *) , which means that no stack frame is needed.编译器可以将str()优化为简单的 'tail call' operator<< (const char *) ,这意味着不需要堆栈帧。

So your results are somewhat skewed by the fact that you put the calls to operator<< in separate functions.因此,由于您将调用operator<<放在单独的函数中,因此您的结果有些偏差。 It's more revealing to make these calls inline, see: https://godbolt.org/z/OO-8dS使这些调用内联更具启发性,请参阅: https : //godbolt.org/z/OO-8dS

Now you can see that, while outputting '\\n' is still a little more expensive (because there is no specific overload for ofstream::operator<< (char) ), the difference is less marked than in your example.现在你可以看到,虽然输出'\\n'仍然有点贵(因为ofstream::operator<< (char)没有特定的重载),但差异没有你的例子那么明显。

Keep in mind though that what you see in the assembly is only the creation of the callstack, not the execution of the actual function.请记住,您在程序集中看到的只是调用堆栈的创建,而不是实际函数的执行。

std::cout << '\\n'; is still much slightly faster than std::cout << "\\n";仍有很大略快于std::cout << "\\n";

I've created this little program to measure the performance and it's about 20 times slightly faster on my machine with g++ -O3.我创建了这个小程序来测量性能,它在我的机器上使用 g++ -O3 稍微快了 20 倍 Try it yourself!自己试试吧!

Edit: Sorry noticed typo in my program and it's not that much faster!编辑:抱歉注意到我的程序中有错字,而且速度并没有那么快! Can barely measure any difference anymore.几乎无法衡量任何差异了。 Sometimes one is faster.有时一个更快。 Other times the other.其他时间其他。

#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();
}

Edit: As geza suggested I tried 100000000 iterations for both and sent it to /dev/null and ran it four times.编辑:正如 geza 建议的那样,我尝试了 100000000 次迭代并将其发送到 /dev/null 并运行了四次。 '\\n' was once slower and 3 times faster but never by much, but it might be different on other machines: '\\n' 曾经更慢,快 3 倍,但从未如此之快,但在其他机器上可能会有所不同:

Nanoseconds: 8668263707
Nanoseconds: 7236055911

Nanoseconds: 10704225268
Nanoseconds: 10735594417

Nanoseconds: 10670389416
Nanoseconds: 10658991348

Nanoseconds: 7199981327
Nanoseconds: 6753044774

I guess overall I wouldn't care too much.我想总的来说我不会太在意。

Yes, for this particular implementation, for your example, char version is a little bit slower than the string version.是的,对于这个特定的实现,例如, char版本比 string 版本慢一点。

Both versions call a write(buffer, bufferSize) style function.两个版本都调用write(buffer, bufferSize)样式函数。 For the string version, bufferSize is known at compile time (1 byte), so there is no need to find the zero terminator run-time.对于字符串版本, bufferSize在编译时是已知的(1 字节),因此无需在运行时查找零终止符。 For the char version, the compiler creates a little 1-byte buffer on stack, puts the character into it, and passes this buffer to write out.对于char版本,编译器在堆栈上创建一个 1 字节的小缓冲区,将字符放入其中,并通过该缓冲区进行写出。 So, the char version is a little bit slower.所以, char版本会慢一点。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在 n 个流中拆分 ifstream? - Split ifstream in n streams? 迭代具有 n 和 n*n 元素的向量 - Iterate over vectors with n and n*n elements 使用&#39;\\ n&#39;代替endl会影响输出,为什么呢? - Using '\n' instead of endl affects output, why? 为什么这段代码没有显示大约10 ^ 5的n的输出 - Why this code is not showing output for n around 10^5 streams,stream_bufs,codecvt facets和\\ n到\\ r \\ n的翻译 - streams, stream_bufs, codecvt facets and \n to \r\n translation 输入,输出和\\ n - input, output and \n's 对于scanf(),为什么否定的扫描集“%[^ \\ n]”显示正确的输出“ \\ n”,而不显示扫描集“%[aeiou]”的“宙斯盾”? - For scanf(), why is negated scanset “%[^\n]” showing correct output for “\n”, but not the scanset “%[aeiou]” for “aegis”? 当“B c = a”时,为什么 output 不是:“构造函数 A\n 构造函数 B\n 复制构造函数 B”? - when "B c = a" why the output isnt : "cosntructor A\n constructor B\n copy constructor B"? 有什么方法可以从流中获取“ \\ n”吗? - Is there any way to get “\n” from streams? 为什么此代码输出的是文字“ \\ n”而不是换行符? - Why does this code output a literal “\n” instead of a newline?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM