परिचय: वह मिथक जो दशकों से जीवित है
\n\nयदि आपने कभी Stack Overflow पर C++ अनुभाग में देखा है, तो आपने शायद यह प्रश्न देखा होगा: «क्या <, <= से तेज़ है?»। इस प्रश्न ने हजारों व्यूज़ और चर्चा के दर्जनों पेज एकत्र किए हैं। और यह आश्चर्यजनक नहीं है — कोड ऑप्टिमाइज़ेशन का विषय सभी स्तरों के प्रोग्रामरों के दिमाग को उत्तेजित करता है।
\n\nतर्क बहुत मजबूत लगता है: ऑपरेटर < दो संख्याओं की तुलना करता है और परिणाम लौटाता है, जबकि <= वही करता है, लेकिन अतिरिक्त रूप से समानता की जाँच करता है। इसका मतलब है कि < तेज़ होना चाहिए, है ना? यह विशेष रूप से लूप के लिए महत्वपूर्ण है, जहाँ तुलना ऑपरेशन लाखों बार किया जाता है।
आज हम इस मिथक को हमेशा के लिए सुलझा लेंगे। आप जानेंगे:
\n- \n
- प्रोसेसर स्तर पर ऑपरेटर वास्तव में कैसे काम करते हैं। \n
- कंपाइलर आपका सबसे अच्छा दोस्त (या दुश्मन, यदि आप उस पर भरोसा नहीं करते) क्यों है। \n
- माइक्रो-ऑप्टिमाइज़ेशन कब समझ में आता है, और कब यह समय की बर्बादी है। \n
असेंबली स्तर पर < और <= ऑपरेटर कैसे काम करते हैं?
\n\nयह समझने के लिए कि क्या कोई अंतर है, हमें 'हुड के नीचे' देखना होगा — यानी, कंपाइलर आपके C++ कोड को क्या रूपांतरित करता है। दो सरल फ़ंक्शनों पर विचार करें:
\n\nbool less_than(int a, int b) {\n return a < b;\n}\n\nbool less_or_equal(int a, int b) {\n return a <= b;\n}\n\nआइए उन्हें g++ (बिना ऑप्टिमाइज़ेशन, फ़्लैग -O0) के साथ कंपाइल करें। असेंबली लिस्टिंग (x86-64) में हम यह देखेंगे:
less_than(int, int):\n cmp edi, esi ; a और b की तुलना करें\n setl al ; al=1 सेट करें यदि a < b (कम है)\n ret\n\nless_or_equal(int, int):\n cmp edi, esi ; a और b की तुलना करें\n setle al ; al=1 सेट करें यदि a <= b (कम या बराबर है)\n ret\n\nदेखा? एकमात्र अंतर निर्देश setl (set if less) और setle (set if less or equal) का है। दोनों निर्देश एक प्रोसेसर क्लॉक साइकिल में निष्पादित होते हैं। आधुनिक CPU (Intel Core i7, AMD Ryzen) पर, उनकी समान विलंबता है — 1 साइकिल।
निष्कर्ष #1: मशीन निर्देश स्तर पर, < और <= के बीच प्रदर्शन में कोई अंतर नहीं है। पूर्ण विराम।
लूप के बारे में क्या? यहाँ तो अंतर होना चाहिए!
\n\nअक्सर, बहस लूप के आसपास होती है। दो दृष्टिकोणों की तुलना करें:
\n\n// विकल्प 1: < के साथ\nfor (int i = 0; i < n; ++i) {\n // कुछ करें\n}\n\n// विकल्प 2: <= के साथ\nfor (int i = 0; i <= n - 1; ++i) {\n // वही करें\n}\n\nअंतर्ज्ञान कहता है: दूसरा विकल्प एक अतिरिक्त गणना n - 1 करता है। लेकिन आइए देखें कि कंपाइलर ऑप्टिमाइज़ेशन के बाद क्या होता है।
n = 1 000 000 000 के लिए g++ -O2 के साथ बेंचमार्क चलाएँ:
\n\n#include <iostream>\n#include <chrono>\n\nint main() {\n const long long n = 1'000'000'000;\n volatile long long sink = 0; // ताकि कंपाइलर लूप को हटा न दे\n\n auto start = std::chrono::high_resolution_clock::now();\n \n for (long long i = 0; i < n; ++i) {\n sink += i;\n }\n \n auto end = std::chrono::high_resolution_clock::now();\n std::cout << "समय (<): " \n << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()\n << " ms\\n";\n\n start = std::chrono::high_resolution_clock::now();\n \n for (long long i = 0; i <= n - 1; ++i) {\n sink += i;\n }\n \n end = std::chrono::high_resolution_clock::now();\n std::cout << "समय (<=): " \n << std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()\n << " ms\\n";\n\n return 0;\n}\n\nमेरी मशीन (i7-107) पर परिणाम: