ios_base::sync_with_stdio(false);
和 cin.tie(0);
是C++标准库中用于优化输入/输出(I/O)性能的两个常用技巧。
ios_base::sync_with_stdio(false);
默认情况下,C++的标准输入输出库(即iostream,包括cin、cout等)与C的标准输入输出库(即stdio,包括scanf、printf、gets、puts等)是同步的。这意味着,无论何时你从stdio或iostream进行输入输出操作,它们都会保持彼此的数据是最新的,保证数据的一致性。但这种同步会导致性能开销。
通过调用 ios_base::sync_with_stdio(false);
,你可以禁用这种同步。这样做后,如果你只使用iostream(或只使用stdio),你的程序可能会获得显著的性能提升。但注意,一旦禁用同步,就不应该混合使用iostream和stdio的I/O操作,因为这会导致未定义的行为。
cin.tie(0);
默认情况下,cin和cout是绑定的。这意味着在每次使用cin进行输入之前,程序都会自动执行cout.flush(),确保在读取输入之前所有当前的输出都已经被刷新(即显示)到屏幕上。这通常是一个有用的特性,因为它避免了程序在等待用户输入时留下未显示的输出。
然而,如果你的程序进行大量输入输出操作,而且不需要每次输入之前都刷新输出缓冲区,这种自动刷新就可能成为性能瓶颈。通过调用 cin.tie(0);
,你可以解除cin和cout之间的这种绑定,禁止这种自动刷新行为,从而进一步提高I/O性能。
【注】:cout.tie(0);
本身并不直接影响性能或行为,因为cout默认不与输入流绑定。故不需要写这条语句。
使用这些优化的注意事项
-
如果你的程序只使用iostream(或只使用stdio)进行输入输出,而且对性能有较高要求,那么使用这些优化技巧是合适的。
-
如果你需要同时使用iostream和stdio,则不应禁用同步。
-
如果你的程序逻辑依赖于在读取输入之前自动刷新输出(例如,提示用户输入),则不应解除cin和cout的绑定。
-
使用这些优化时,需要确保它们不会干扰程序的正确性和预期行为。
下面是一个示例程序,它展示了如何在C++程序中应用 ios_base::sync_with_stdio(false);
和 cin.tie(0);
来优化输入输出性能。在这个示例中,我们将执行一个简单的任务:读取大量的整数,然后将它们输出。这个任务通常用于测试输入输出的速度。
#include <iostream>
using namespace std;
int main() {
// 禁用cin和cout的同步,从而加快cin和cout的速度
ios_base::sync_with_stdio(false);
// 解除cin与cout的绑定,进一步加快速度
cin.tie(0);
int n;
// 假设我们要处理的整数数量
cin >> n;
for(int i = 0; i < n; i++) {
int number;
cin >> number; // 读取每个整数
cout << number << "\n"; // 输出每个整数
}
return 0;
}
这个示例程序中,首先通过 ios_base::sync_with_stdio(false);
禁用了cin与cout之间的同步。这意味着,如果你的程序不需要同时使用C和C++风格的输入输出,你可以通过这种方式来提升性能。
紧接着,通过 cin.tie(0);
解除了cin和cout之间的自动刷新绑定。这样做的结果是,在进行输入操作之前,不会自动地刷新输出缓冲区。这可以在执行大量输入输出操作的程序中进一步提高性能。
需要注意的是,上述两个优化措施都有其特定的使用场景,它们可能并不总是适用。在决定使用这些优化前,你应该根据自己程序的具体需求来判断是否真的需要它们。
$猴子也能懂的解释$:
ios_base::sync_with_stdio(false);
想象一下,C++和C是两个使用不同语言的人,但他们需要通过同一位翻译(同步机制)来沟通。每次其中一个人说话,翻译都需要把这话翻译给另一个人听,以确保大家都在同一页面上。这个过程虽然能让大家都理解对方,但显然会拖慢对话速度。
ios_base::sync_with_stdio(false);
这条命令的作用,就好比是告诉他们,既然大家都在一个房间里,就不需要翻译了,每个人直接说自己的语言,独自完成自己的任务。这样做的好处是,沟通速度变快了,但坏处是C++和C就不能再混着用了,因为他们不再互相理解了。
cin.tie(0);
通常来说,C++在跟你聊天(也就是接收输入或输出信息)之前,总是喜欢确保之前说过的话(输出的信息)都已经告诉你了,没有遗漏。这就像是在你问问题之前,它总要确认之前的对话已经结束,确保你不会错过任何信息。
cin.tie(0);
这条命令的作用,就好像是告诉C:“嘿,不用每次都回顾之前说过的话了,直接跟我说现在的事情就行。”这样可以加快聊天的速度,因为它不用每次都检查之前的对话了。但这也意味着如果你真的需要知道之前的对话内容,你可能需要自己主动去确认,因为C不会再自动帮你复述了。
总的来说,这两个命令都是用来让C的聊天(输入输出)更高效的小技巧。它们让C少做一些不必要的工作,从而加快了交流速度。但使用这些技巧时,你也需要更加留心,确保自己不会因为缺少了某些自动的检查而错过重要的信息。
要记住这两句语句 ios_base::sync_with_stdio(false);
和 cin.tie(0);
,可以从英文单词的意思出发,关联到它们的功能上,这样可以帮助记忆:
ios_base::sync_with_stdio(false);
ios_base
:想象成输入输出系统的“基地”或“基础”,它是所有输入输出操作的根基。
sync_with_stdio
:sync
代表 $”synchronize”$,意思是“同步”或“协同工作”,stdio
是 C 语言中的标准输入输出,所以 sync_with_stdio
可以理解为“与C语言的标准输入输出同步”。
false
:表示“不”,这里就是说“不要同步”。
综合来看,ios_base::sync_with_stdio(false);
就像是在说:“输入输出系统的基础啊,请不要和C语言的标准输入输出保持同步。”
cin.tie(0);
cin
:cin 可以理解为“看进来”(C++的标准输入)。
tie
:在这里可以理解为“绑定”或“系在一起”,就像鞋带把两只鞋子绑在一起。
0
或 nullptr
:表示“没有”或“空”。在这里,将其设置为 0 就是让 cin 不再和任何东西绑定。
所以,cin.tie(0);
可以理解为:“输入啊,请不要再和其他的东西绑定在一起了。”
通过这种方式,可以把这些看似复杂的代码片段,转化成一些有意义的英文句子或故事,从而更容易地记住它们。
【问】可以在 $main$ 函数外使用这两个优化语句吗?
【答】可以这样写:
__attribute__((unused)) int io_ = []() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
return 0;
}();
其中
__attribute__((unused))
:这是一个编译器特定属性(特别是对于 $GCC$),用来指示一个变量或函数可能会故意未被使用,它不应该因此给出警告。这里用它是为了防止编译器对未使用的变量io_
发出警告。
int io_ = []() { ... }();
:这声明了一个整型变量io_
并用紧随其后的立即调用的 $lambda$ 表达式($IILE$)的结果初始化它。$lambda$ 本身定义在[](){...}
之间,而结尾的空括号()
则是在定义后立即调用这个 $lambda$。
在 $lambda$ 内部:
ios::sync_with_stdio(false);
cin.tie(nullptr);
的作用就不用多说了。
整个结构是一个巧妙的方式,在main()
开始执行之前执行这些 $I/O$ 优化代码,得益于全局变量初始化顺序。使用 $lambda$ 和立即调用模式是一个技巧,可以在初始化单个全局变量的过程中包含几个操作,确保在任何用户定义的代码之前运行这段代码,而无需在main()
的开始显式调用一个函数。
下面是使用这段代码和单独使用上面的两句优化代码的具体区别:
执行时机 :
使用__attribute__((unused)) int io_ = [](){...}();
形式,$I/O$ 优化代码被包装在一个立即调用的 $lambda$ 表达式($IILE$)中,并且这个表达式是在全局变量的初始化阶段执行的。这意味着,无论程序的入口点(通常是 $main$ 函数)中的代码如何,这些 $I/O$ 优化设置会在程序的 $main$ 函数开始执行之前就已经完成。这样做保证了这些优化设置在任何标准输入输出操作发生之前就已经生效。
直接在代码中(如在 $main$ 函数的开始处)使用这三行代码,则它们将在程序执行到那一点时执行。如果在这些命令执行之前已经进行了 $I/O$ 操作,那么这些设置将不会影响到那些操作。
上下文和可见性 :
将设置包装在全局变量的初始化中,意味着它们在全局作用域内执行。这种方式不需要修改 $main$ 函数或其他函数的代码,是一种“设置一次,全局有效”的策略。这对于希望将 $I/O$ 优化隐藏或封装起来,避免在业务逻辑代码中显式调用这些设置的场景特别有用。
将这些设置直接放在代码中(如在 $main$ 函数内)则更加直接和明显。这种方法让其他阅读代码的人立即就能看到 $I/O$ 优化设置的存在,但需要在每个需要这些设置的地方重复这些代码。
代码的“干净”程度和封装性 :
使用全局变量和 $lambda$ 表达式的方式可以看作是一种更“干净”的封装,因为它将 $I/O$ 设置的细节隐藏起来,不会干扰到主逻辑代码的结构。这对于保持代码的整洁和可维护性有好处。直接在 $main$ 或其他函数中使用这些设置,则使得这些优化措施更加明显和易于理解,但可能会增加代码的重复性,特别是在多个地方需要这些设置时。
【问】:cin.tie(nullptr)
和cin.tie(0)
有什么区别?
【答】:
-
cin.tie(nullptr);
使用nullptr
明确表示将cin
绑定到的流对象设置为“无”,这是 $C++ 11$ 引入的空指针常量表示方式,增强了代码的可读性和清晰度。 -
cin.tie(0);
是在 $C++ 11$ 之前的代码中更常见的表达方式,0
被隐式转换为空指针,用于同样的目的。虽然这种方式在技术上是正确的,但使用nullptr
替代0
作为空指针的表示更加明确和安全,因为nullptr
的类型是空指针类型,而0
可以有多重含义(比如整数或空指针)。
尽管两种方式在功能上是相同的,但是推荐使用 cin.tie(nullptr);
因为它更清晰地表达了程序员的意图,即将 cin
绑定到的对象设置为无,且遵循了 $C++ 11$ 及以后版本中对空指针的标准表示。这样的代码更易于理解和维护,特别是对于那些熟悉现代 $C++$ 特性的开发者。
【问】:要不要加cout.tie(nullptr);
?
【答】:cout
默认是不绑定的,加不加无所谓,但加了一方面是为了和cin
对称,一方面确实好像加了会稍快那么一丢丢,所以加呗。
这样一来追求极致的模板就变成了:
__attribute__((unused)) int io_ = []() {
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
return 0;
}();
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC target("avx")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("-fgcse")
#pragma GCC optimize("-fgcse-lm")
#pragma GCC optimize("-fipa-sra")
#pragma GCC optimize("-ftree-pre")
#pragma GCC optimize("-ftree-vrp")
#pragma GCC optimize("-fpeephole2")
#pragma GCC optimize("-ffast-math")
#pragma GCC optimize("-fsched-spec")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("-falign-jumps")
#pragma GCC optimize("-falign-loops")
#pragma GCC optimize("-falign-labels")
#pragma GCC optimize("-fdevirtualize")
#pragma GCC optimize("-fcaller-saves")
#pragma GCC optimize("-fcrossjumping")
#pragma GCC optimize("-fthread-jumps")
#pragma GCC optimize("-funroll-loops")
#pragma GCC optimize("-fwhole-program")
#pragma GCC optimize("-freorder-blocks")
#pragma GCC optimize("-fschedule-insns")
#pragma GCC optimize("inline-functions")
#pragma GCC optimize("-ftree-tail-merge")
#pragma GCC optimize("-fschedule-insns2")
#pragma GCC optimize("-fstrict-aliasing")
#pragma GCC optimize("-fstrict-overflow")
#pragma GCC optimize("-falign-functions")
#pragma GCC optimize("-fcse-skip-blocks")
#pragma GCC optimize("-fcse-follow-jumps")
#pragma GCC optimize("-fsched-interblock")
#pragma GCC optimize("-fpartial-inlining")
#pragma GCC optimize("no-stack-protector")
#pragma GCC optimize("-freorder-functions")
#pragma GCC optimize("-findirect-inlining")
#pragma GCC optimize("-fhoist-adjacent-loads")
#pragma GCC optimize("-frerun-cse-after-loop")
#pragma GCC optimize("inline-small-functions")
#pragma GCC optimize("-finline-small-functions")
#pragma GCC optimize("-ftree-switch-conversion")
#pragma GCC optimize("-foptimize-sibling-calls")
#pragma GCC optimize("-fexpensive-optimizations")
#pragma GCC optimize("-funsafe-loop-optimizations")
#pragma GCC optimize("inline-functions-called-once")
#pragma GCC optimize("-fdelete-null-pointer-checks")
大佬 牛逼