परिचय: दो वर्णों का युद्ध
\nयदि आपने कभी C++ में प्रोग्राम लिखा है और कंसोल में टेक्स्ट आउटपुट किया है, तो आपने निश्चित रूप से इस दुविधा का सामना किया होगा: std::endl का उपयोग करें या "\\n" का? पहली नज़र में अंतर न्यूनतम है - दोनों लाइन को नई लाइन पर ले जाते हैं। लेकिन इस सरल विकल्प के पीछे प्रदर्शन, बफरिंग और आपके प्रोग्राम में अप्रत्याशित मंदी की एक पूरी कहानी छिपी हुई है।
Stack Overflow पर इस प्रश्न ने हजारों व्यूज प्राप्त किए हैं और गरमागरम बहस छेड़ दी है। आज हम इस विषय को एक बार और हमेशा के लिए समझेंगे: क्या तेज़ है, क्यों, और प्रत्येक विकल्प कब उचित है। यदि आप C++ में लिखते हैं और चाहते हैं कि आपका कोड अधिकतम दक्षता के साथ काम करे - तो यह लेख आपके लिए है।
\n\nसंक्षिप्त उत्तर (स्पॉयलर)
\n"\\n" हमेशा std::endl से तेज़ होता है। लेकिन इसलिए नहीं कि std::endl अपने आप में धीमा है, बल्कि इसलिए कि यह अतिरिक्त काम करता है। std::endl केवल एक नई लाइन का वर्ण जोड़ता ही नहीं है - यह आउटपुट बफर को भी फ्लश (flush) करता है। और यह एक ऐसा ऑपरेशन है जो आपको कीमती मिलीसेकंड खर्च कर सकता है।
आइए समझते हैं कि व्यवहार में इसका क्या अर्थ है, और कोड पर एक नज़र डालते हैं।
\n\nC++ में आउटपुट कैसे काम करता है
\nजब आप std::cout << "Hello"; लिखते हैं, तो डेटा तुरंत स्क्रीन पर नहीं भेजा जाता है। यह बफर में जमा होता है - मेमोरी में एक अस्थायी भंडारण। यह दक्षता के लिए किया जाता है: डेटा को एक-एक वर्ण करके स्क्रीन पर भेजना बहुत धीमा होता है, इसलिए सिस्टम तब तक प्रतीक्षा करता है जब तक पर्याप्त डेटा जमा न हो जाए, और फिर उसे एक पैकेट में भेजता है।
बफर फ्लश (flush) - यह सभी संचित डेटा को जबरन भेजना है। std::endl ठीक यही करता है।
अंदर क्या होता है?
\n// std::endl का उपयोग\nstd::cout << "लाइन 1" << std::endl;\n// इसके बराबर है:\nstd::cout << "लाइन 1" << '\\n' << std::flush;\n\n// "\\n" का उपयोग\nstd::cout << "लाइन 1\\n";\n// बस बफर में नई लाइन का वर्ण जोड़ता है\n\nअंतर देखा? std::endl दो काम करता है: '\\n' जोड़ता है और std::flush को कॉल करता है। "\\n" केवल एक काम करता है।
प्रयोग: प्रदर्शन मापना
\nआइए इसे एक वास्तविक उदाहरण पर जांचते हैं। एक छोटा प्रोग्राम लिखते हैं जो दो तरीकों से 100,000 लाइनें आउटपुट करता है और समय मापता है।
\n\n#include <iostream>\n#include <chrono>\n\nint main() {\n const int N = 100000;\n \n // std::endl के साथ परीक्षण\n auto start = std::chrono::high_resolution_clock::now();\n for (int i = 0; i < N; ++i) {\n std::cout << "लाइन नंबर " << i << std::endl;\n }\n auto end = std::chrono::high_resolution_clock::now();\n auto duration_endl = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);\n std::cout << "endl के साथ समय: " << duration_endl.count() << " मि.से.\\n";\n \n // "\\n" के साथ परीक्षण\n start = std::chrono::high_resolution_clock::now();\n for (int i = 0; i < N; ++i) {\n std::cout << "लाइन नंबर " << i << "\\n";\n }\n end = std::chrono::high_resolution_clock::now();\n auto duration_n = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);\n std::cout << "\\\\n के साथ समय: " << duration_n.count() << " मि.से.\\n";\n \n return 0;\n}\n\nपरिणाम (सामान्य):
\n- \n
std::endl: ~350-400 मि.से. \n "\\n": ~5-10 मि.से. \n
अंतर 30-70 गुना है! और यह कोई मज़ाक नहीं है। std::endl का प्रत्येक कॉल सिस्टम को बफर फ्लश करने के लिए ऑपरेटिंग सिस्टम से संपर्क करने पर मजबूर करता है। और यह एक सिस्टम कॉल है, जो अपेक्षाकृत महंगा है।
flush कब आवश्यक है?
\nलेकिन std::endl को पूरी तरह से छोड़ने में जल्दबाजी न करें। कभी-कभी बफर फ्लश करना महत्वपूर्ण होता है।