# I/O 優化 ###### tags: `Competitive Programming Note` 本文已停止更新,新版請至 [WiwiHo 的競程筆記](https://cp.wiwiho.me/) 有的時候會在題目中看到這麼一句:「建議使用 `scanf`/`printf`,使用 `cin` 和 `cout` 可能造成 TLE。」在輸入、輸出量比較大的題目,應該都可以明顯感受到 `stdio` 和 `iostream` 兩個的差異,`scanf` 和 `printf` 的效率顯然高得多,但是事情並不是這樣。 `cin` 和 `cout` 的好處很明顯,像是它們保證類型安全,不會像 `printf` 和 `scanf` 有打錯類型的風險,且 `<<` 和 `>>` 這兩個運算子可以重載,換句話說,你可以在定義一個類型的物件 `a` 如何輸入輸出後,方便的使用 `cin >> a` 和 `cout << a`,就可以做到輸入輸出,既方便又直觀。 再加上 `cout` 像是補 0、控制位數等等都比 `printf` 好寫得多,所以事實上 `cin` 和 `cout` 是比較方便的,然而,速度似乎慢很多?先來看看為什麼: 在文字被輸出前,它會先到一個「緩衝區」,不是馬上就全部輸出,而是到一個特定的時間再一次輸出出去,因為分次輸出其實算是很耗資源的事。強制將緩衝區的資料釋放(輸出)的動作稱為 flush,在 `stdio`,把標準輸出流 flush 是 `fflush(stdout)`,而在 `iostream`,這個動作是 `cout << flush`。在一些互動題中,題目會要求在輸出後立刻 flush,就是因為文字不一定會馬上被輸出出去。如果完全沒被強制釋放,通常資料會在程式結束時輸出出來。 緩衝區的釋放是決定效率的關鍵因素,看是一次釋放、還是多次釋放,速度就會大有不同。`printf` 是不會自動釋放緩衝區的,`scanf` 也不會強制釋放,但 `cin` 就會了,因為在要求使用者輸入之前,應該先讓使用者看見先前輸出的文字,這是介面設計上的考量,但顯然不符合競程的需要,因此我們要取消強制釋放: ```cpp cin.tie(0); ``` `tie` 是 `ios` 的一個方法,也就是說,`cin`、`cerr`、`cout` 都有這個方法,`cin` 和 `cerr` 預設是有 tie `cout` 的,所以在使用 `cin`、`cerr` 時,`cout` 的緩衝區會先被釋放,`tie(0)` 就可以解除。所以如果你不想看到 `cerr` 和 `cout` 的訊息全部卡在一起,可以再加上 `cerr.tie(0)`。 接下來,還有一個地方會自動 flush,就是 `endl`,也就是說,`cout << endl` 其實是 `cout << flush << '\n'`,解決方法很簡單,就是不要用,都用 `cout << '\n'` 就可以了。 最後,還有一個跟緩衝區無關的問題,C++ 中有兩種輸入輸出系統:`stdio` 和 `iostream`,因為一些歷史因素,C++ 會讓 `iostream` 和 `stdio` 同步,來避免混用時發生無法預期的問題,所以這個額外的動作會造成效率降低,不過打競程時不會有混用的狀況,把同步解除就好: ```cpp ios_base::sync_with_stdio(false); ``` (記得這個用了之後,`iostream` 和 `stdio` 不能混用。)