< तेज़ <= C++ में? उदाहरणों के साथ मिथकों को तोड़ना

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

परिचय: वह मिथक जो दशकों से जीवित है

\n\n

यदि आपने कभी Stack Overflow पर C++ अनुभाग में देखा है, तो आपने शायद यह प्रश्न देखा होगा: «क्या <, <= से तेज़ है?»। इस प्रश्न ने हजारों व्यूज़ और चर्चा के दर्जनों पेज एकत्र किए हैं। और यह आश्चर्यजनक नहीं है — कोड ऑप्टिमाइज़ेशन का विषय सभी स्तरों के प्रोग्रामरों के दिमाग को उत्तेजित करता है।

\n\n

तर्क बहुत मजबूत लगता है: ऑपरेटर < दो संख्याओं की तुलना करता है और परिणाम लौटाता है, जबकि <= वही करता है, लेकिन अतिरिक्त रूप से समानता की जाँच करता है। इसका मतलब है कि < तेज़ होना चाहिए, है ना? यह विशेष रूप से लूप के लिए महत्वपूर्ण है, जहाँ तुलना ऑपरेशन लाखों बार किया जाता है।

\n\n

आज हम इस मिथक को हमेशा के लिए सुलझा लेंगे। आप जानेंगे:

\n
    \n
  • प्रोसेसर स्तर पर ऑपरेटर वास्तव में कैसे काम करते हैं।
  • \n
  • कंपाइलर आपका सबसे अच्छा दोस्त (या दुश्मन, यदि आप उस पर भरोसा नहीं करते) क्यों है।
  • \n
  • माइक्रो-ऑप्टिमाइज़ेशन कब समझ में आता है, और कब यह समय की बर्बादी है।
  • \n
\n\n

असेंबली स्तर पर < और <= ऑपरेटर कैसे काम करते हैं?

\n\n

यह समझने के लिए कि क्या कोई अंतर है, हमें 'हुड के नीचे' देखना होगा — यानी, कंपाइलर आपके C++ कोड को क्या रूपांतरित करता है। दो सरल फ़ंक्शनों पर विचार करें:

\n\n
bool 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) में हम यह देखेंगे:

\n\n
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 साइकिल।

\n\n

निष्कर्ष #1: मशीन निर्देश स्तर पर, < और <= के बीच प्रदर्शन में कोई अंतर नहीं है। पूर्ण विराम।

\n\n

लूप के बारे में क्या? यहाँ तो अंतर होना चाहिए!

\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\n

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) पर परिणाम:

ब्लॉग

पुस्तक अनुशंसाएँ