वहाँ सी पर शैली बिट हेरफेर करने के लिए कोई लाभ है :: बिटसेट?


17

मैं लगभग विशेष रूप से C ++ 11/14 में काम करता हूं, और आमतौर पर जब मैं इस तरह का कोड देखता हूं, तो मैं परेशान हो जाता हूं:

std::int64_t mArray;
mArray |= someMask << 1;

यह सिर्फ एक उदाहरण है; मैं सामान्य रूप से बिट-वार हेरफेर के बारे में बात कर रहा हूं। C ++ में, क्या वास्तव में कोई बिंदु है? std::bitsetआपको अनुमति देते समय उपरोक्त मन-वार और त्रुटि-प्रवण है:

  1. अधिक आसानी से std::bitsetएक टेम्पलेट पैरामीटर को समायोजित करके और कार्यान्वयन को बाकी की देखभाल करने की अनुमति देकर और के रूप में आवश्यकतानुसार आकार को संशोधित करें , और
  2. कम समय बिताएं जो पता चल रहा है (और संभवतः गलतियाँ कर रहा है) या अन्य डेटा कंटेनरों के std::bitsetसमान तरीके से लिखें std::array

मेरा सवाल यह है कि; क्या पिछड़े-अनुकूलता के अलावा, आदिम प्रकारों का उपयोग करने का कोई कारण है std::bitset?


एक std::bitsetका आकार संकलन-समय पर तय किया गया है। यही एकमात्र अपंगता है जिसके बारे में मैं सोच सकता हूं।
रवांग

1
@rwong मैं std::bitsetबनाम सी-स्टाइल बिट मैनिपुलेशन (जैसे int) के बारे में बात कर रहा हूं , जो संकलन-समय पर भी तय किया गया है।
बल्ली से ढकेलना

एक कारण विरासत कोड हो सकता है: कोड तब std::bitsetउपलब्ध था जब उपलब्ध नहीं था (या लेखक को ज्ञात था) और उपयोग करने के लिए कोड को फिर से लिखने का कारण नहीं है std::bitset
बार्ट वैन इनगेन शेनौ

मैं व्यक्तिगत रूप से सोचता हूं कि बाइनरी वेरिएबल्स के सेट / मैप / सरणी पर "ऑपरेशन कैसे करें" हर किसी को समझना आसान है, अभी भी काफी हद तक अनसुलझा है, क्योंकि प्रैक्टिस में कई ऑपरेशन ऐसे होते हैं, जिन्हें सरल ऑपरेशन के लिए कम नहीं किया जा सकता है। ऐसे सेटों का प्रतिनिधित्व करने के लिए भी कई तरीके हैं, जिनमें bitsetसे एक है, लेकिन एक छोटा वेक्टर या सेट ऑफ int(बिट इंडेक्स) भी वैध हो सकता है। C / C ++ का दर्शन प्रोग्रामर की इन पसंद की जटिलताओं को नहीं छिपाता है।
rwong

जवाबों:


12

तार्किक (गैर-तकनीकी) दृष्टिकोण से, कोई फायदा नहीं है।

किसी भी सादे C / C ++ कोड को उपयुक्त "लाइब्रेरी कंस्ट्रक्शन" के भीतर लपेटा जा सकता है। इस तरह के रैपिंग के बाद, "क्या यह उस से अधिक लाभप्रद है" का मामला एक मूक सवाल बन जाता है।

स्पीड-पॉइंट-ऑफ-व्यू से, C / C ++ को लाइब्रेरी कंस्ट्रक्शन को कोड उत्पन्न करने की अनुमति देनी चाहिए जो उस सादे कोड के रूप में कुशल है जो कि रैप करता है। हालांकि यह निम्न है:

  • फंक्शन इनलाइनिंग
  • कंपाइल-टाइम चेकिंग और अनावश्यक रनटाइम चेकिंग को खत्म करना
  • मृत कोड उन्मूलन
  • कई अन्य कोड अनुकूलन ...

इस तरह के गैर-तकनीकी तर्क का उपयोग करके, किसी भी "लापता कार्यों" को किसी के द्वारा जोड़ा जा सकता है, और इसलिए इसे नुकसान के रूप में नहीं गिना जाता है।

हालाँकि, अतिरिक्त कोड के साथ अंतर्निहित आवश्यकताओं और सीमाओं को दूर नहीं किया जा सकता है। नीचे, मैं तर्क देता हूं कि इसका आकार std::bitsetएक संकलन-समय स्थिर है, और इसलिए इसे नुकसान के रूप में नहीं गिना जाता है, फिर भी यह कुछ ऐसा है जो उपयोगकर्ता की पसंद को प्रभावित करता है।


सौंदर्य की दृष्टि से (पठनीयता, रखरखाव में आसानी आदि), एक अंतर है।

हालाँकि, यह स्पष्ट नहीं है कि std::bitsetकोड तुरंत सी कोड पर जीत जाता है। किसी को कोड के बड़े टुकड़ों (और कुछ खिलौना-नमूना) को देखने के लिए कहना होगा कि क्या std::bitsetस्रोत कोड के मानव गुणवत्ता में सुधार हुआ है।


बिट हेरफेर की गति कोडिंग शैली पर निर्भर करती है। कोडिंग शैली C / C ++ बिट हेरफेर दोनों को प्रभावित करती है, और समान std::bitsetरूप से अच्छी तरह से लागू होती है , जैसा कि समझाया गया है।


यदि कोई एक कोड लिखता है जो operator []एक बार में एक बिट पढ़ने और लिखने के लिए उपयोग करता है , तो किसी को एक से अधिक बिट्स में हेरफेर करने के लिए कई बार ऐसा करना होगा। वही सी-स्टाइल कोड के बारे में कहा जा सकता है।

हालांकि, bitsetइस तरह के रूप में अन्य ऑपरेटरों, है operator &=, operator <<=आदि, जो bitset की पूरी चौड़ाई पर चल रही है। क्योंकि अंतर्निहित मशीनरी अक्सर 32-बिट, 64-बिट और कभी-कभी 128-बिट (SIMD के साथ) एक समय में (सीपीयू चक्रों की समान संख्या में) काम कर सकती है, ऐसे कोड जो इस तरह के मल्टी-बिट ऑपरेशन का लाभ उठाने के लिए डिज़ाइन किए गए हैं "लोपी" बिट-हेरफेर कोड की तुलना में तेज़ हो सकता है।

सामान्य विचार को SWAR (एक रजिस्टर के भीतर SIMD) कहा जाता है , और बिट जोड़तोड़ के तहत एक उपपट्टी है।


कुछ C ++ विक्रेता bitset64-बिट और 128-बिट के बीच SIMD के साथ कार्यान्वित कर सकते हैं । कुछ विक्रेता (लेकिन अंततः ऐसा नहीं कर सकते हैं)। यदि यह जानने की आवश्यकता है कि C ++ विक्रेता की लाइब्रेरी क्या कर रही है, तो एक ही तरीका यह है कि डिससैसम को देखें।


जैसे कि std::bitsetसीमाएं हैं, मैं दो उदाहरण दे सकता हूं।

  1. std::bitsetसंकलन समय पर आकार ज्ञात होना चाहिए। गतिशील रूप से चुने गए आकार के साथ बिट्स की एक सरणी बनाने के लिए, एक का उपयोग करना होगा std::vector<bool>
  2. वर्तमान C ++ विनिर्देशन के लिए M बिट्स के std::bitsetएक बड़े bitsetभाग से N बिट्स के लगातार स्लाइस को निकालने का तरीका प्रदान नहीं करता है ।

पहला एक मौलिक है, जिसका अर्थ है कि जिन लोगों को गतिशील रूप से बिटसेट्स की आवश्यकता होती है, उन्हें अन्य विकल्पों को चुनना होगा।

दूसरे को दूर किया जा सकता है, क्योंकि कोई कार्य करने के लिए कुछ प्रकार के एडेप्टर लिख सकता है , भले ही मानक bitsetएक्स्टेंसिबल न हो।


कुछ विशेष प्रकार के उन्नत SWAR ऑपरेशन हैं, जिन्हें आउट-ऑफ-द-बॉक्स प्रदान नहीं किया जाता है std::bitset। एक बिट परमिट के बारे में इस वेबसाइट पर इन कार्यों के बारे में पढ़ सकता है । हमेशा की तरह, कोई भी अपने दम पर इन्हें लागू कर सकता है, शीर्ष पर परिचालन कर रहा है std::bitset


प्रदर्शन पर चर्चा के बारे में।

एक चेतावनी: बहुत से लोग मानक पुस्तकालय से क्यों (कुछ) के बारे में पूछते हैं , कुछ सरल सी-स्टाइल कोड की तुलना में बहुत धीमा है। मैं यहाँ माइक्रोबेनमार्किंग के पूर्व ज्ञान को नहीं दोहराऊंगा, लेकिन मेरे पास बस यही सलाह है: "रिलीज़ मोड" में बेंचमार्क सुनिश्चित करें (अनुकूलन सक्षम होने के साथ), और सुनिश्चित करें कि कोड को समाप्त नहीं किया जा रहा है (मृत कोड उन्मूलन) या जा रहा है लूप से बाहर फहराया गया (लूप-इनवेरेंट कोड मोशन)

चूँकि सामान्य तौर पर हम यह नहीं बता सकते हैं कि कोई व्यक्ति (इंटरनेट पर) सही तरीके से माइक्रोबैनचर्च कर रहा था या नहीं, एक ही तरीका है कि हम एक विश्वसनीय निष्कर्ष प्राप्त कर सकें, अपने स्वयं के माइक्रोबैनचर्च करने के लिए, और विवरणों का दस्तावेजीकरण करें, और सार्वजनिक समीक्षा और समालोचना प्रस्तुत करें। यह माइक्रोबेनचर्च को फिर से करने के लिए चोट नहीं करता है जो दूसरों ने पहले किया है।


समस्या # 2 का अर्थ यह भी है कि बिटसेट किसी भी समानांतर सेटअप में उपयोग नहीं किया जा सकता है जहां प्रत्येक थ्रेड बिटसेट के सबसेट पर काम करना चाहिए।
user239558

@ user239558 मुझे संदेह है कि कोई भी उसी पर समानांतर करना चाहेगा std::bitset। कोई स्मृति संगतता गारंटी (इन std::bitset) नहीं है, जिसका अर्थ है कि इसे कोर के बीच साझा नहीं किया जाना चाहिए। जिन लोगों को इसे कोर में साझा करने की आवश्यकता है, वे अपने स्वयं के कार्यान्वयन का निर्माण करेंगे। जब डेटा को अलग-अलग कोर के बीच साझा किया जाता है, तो उन्हें कैश लाइन सीमा के साथ संरेखित करने के लिए प्रथागत है। ऐसा नहीं करने से प्रदर्शन कम हो जाता है, और अधिक गैर-एटमॉसिटी कम हो जाती है। मेरे पास इतना ज्ञान नहीं है कि कैसे एक समानांतर कार्यान्वयन का निर्माण किया जा सके std::bitset
रवांग

डेटा समानांतर प्रोग्रामिंग आमतौर पर किसी भी स्मृति स्थिरता की आवश्यकता नहीं होती है। आप केवल चरणों के बीच सिंक्रनाइज़ करते हैं। मैं बिल्कुल समानांतर में एक बिटसेट को संसाधित करना चाहूंगा, मुझे लगता है कि कोई भी एक बड़ी bitsetइच्छाशक्ति के साथ।
user239558

@ user239558, जिसका अर्थ है नकल करना (प्रत्येक कोर द्वारा संसाधित की जाने वाली बिटसेट की प्रासंगिक प्रक्रिया को प्रसंस्करण शुरू होने से पहले कॉपी किया जाना है)। मैं इससे सहमत हूं, हालांकि मुझे लगता है कि किसी ने भी समानांतर के बारे में सोचकर पहले ही अपने कार्यान्वयन को लागू करने के बारे में सोच लिया होगा। सामान्य तौर पर, सी ++ मानक पुस्तकालय सुविधाओं का एक बहुत आधारभूत कार्यान्वयन के रूप में प्रदान किया जाता है; किसी को भी अधिक गंभीर आवश्यकताएं अपने स्वयं के कार्यान्वयन के लिए जा रही हैं।
रोंगोंग

नहीं, कोई नकल नहीं है। यह केवल एक स्थैतिक डेटा संरचना के विभिन्न भागों तक पहुँच रहा है। कोई तुल्यकालन की जरूरत है तो।
user239558

2

यह निश्चित रूप से सभी मामलों में लागू नहीं होता है, लेकिन कभी-कभी एक एल्गोरिथ्म महत्वपूर्ण प्रदर्शन लाभ प्रदान करने के लिए सी-स्टाइल बिट-ट्विडलिंग की दक्षता पर निर्भर हो सकता है। पहला उदाहरण जो मेरे दिमाग में आता है, वह है बिटबोर्ड का उपयोग , बोर्ड गेम पोजीशन के चतुर पूर्णांक एनकोडिंग, शतरंज इंजन और इस तरह की गति के लिए। यहाँ, पूर्णांक प्रकारों का निश्चित आकार कोई समस्या नहीं है, क्योंकि शतरंज की बिसात हमेशा 8 * 8 होती है।

एक सरल उदाहरण के लिए, निम्नलिखित फ़ंक्शन ( बेन जैक्सन द्वारा इस उत्तर से लिया गया ) पर विचार करें जो जीत के लिए कनेक्ट फोर स्थिति का परीक्षण करता है:

// return whether newboard includes a win
bool haswon2(uint64_t newboard)
{
    uint64_t y = newboard & (newboard >> 6);
    uint64_t z = newboard & (newboard >> 7);
    uint64_t w = newboard & (newboard >> 8);
    uint64_t x = newboard & (newboard >> 1);
    return (y & (y >> 2 * 6)) | // check \ diagonal
           (z & (z >> 2 * 7)) | // check horizontal -
           (w & (w >> 2 * 8)) | // check / diagonal
           (x & (x >> 2));      // check vertical |
}

2
क्या आपको लगता है कि कोई std::bitsetभी धीमा होगा?
बल्ली से ढकेलना

1
ठीक है, स्रोत पर एक त्वरित नज़र से, libc ++ बिटसेट एकल size_t या उनमें से एक सरणी पर आधारित है, इसलिए संभवतः कुछ अनिवार्य रूप से समतुल्य / समान को संकलित करेगा, विशेष रूप से एक सिस्टम पर जहां sizeof (size_t) = 8 -। तो नहीं, यह शायद कोई धीमा नहीं होगा।
रायन पावलिक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.