एक सरल बाईट्रेस्टिंग लाइब्रेरी पर विचार करें। आपके पास एक बाइट स्ट्रिंग प्रकार हो सकता है जिसमें बाइट्स की लंबाई और आवंटित बफ़र होता है:
data BS = BS !Int !(ForeignPtr Word8)
बाइटस्ट्रिंग बनाने के लिए, आपको आमतौर पर IO क्रिया का उपयोग करना होगा:
create :: Int -> (Ptr Word8 -> IO ()) -> IO BS
{-# INLINE create #-}
create n f = do
p <- mallocForeignPtrBytes n
withForeignPtr p $ f
return $ BS n p
हालांकि, IO मोनड में काम करना सुविधाजनक नहीं है, इसलिए आपको थोड़ा असुरक्षित IO करने के लिए लुभाया जा सकता है:
unsafeCreate :: Int -> (Ptr Word8 -> IO ()) -> BS
{-# INLINE unsafeCreate #-}
unsafeCreate n f = myUnsafePerformIO $ create n f
अपनी लाइब्रेरी में व्यापक इनलाइनिंग को देखते हुए, असुरक्षित IO को सर्वश्रेष्ठ प्रदर्शन के लिए इनलाइन करना अच्छा होगा:
myUnsafePerformIO :: IO a -> a
{-# INLINE myUnsafePerformIO #-}
myUnsafePerformIO (IO m) = case m realWorld# of (# _, r #) -> r
लेकिन, जब आप सिंग्लटन बाइटस्ट्रेस बनाने के लिए एक सुविधा फ़ंक्शन जोड़ते हैं:
singleton :: Word8 -> BS
{-# INLINE singleton #-}
singleton x = unsafeCreate 1 (\p -> poke p x)
आपको यह जानकर आश्चर्य हो सकता है कि निम्नलिखित कार्यक्रम प्रिंट करता है True
:
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE UnboxedTuples #-}
import GHC.IO
import GHC.Prim
import Foreign
data BS = BS !Int !(ForeignPtr Word8)
create :: Int -> (Ptr Word8 -> IO ()) -> IO BS
{-# INLINE create #-}
create n f = do
p <- mallocForeignPtrBytes n
withForeignPtr p $ f
return $ BS n p
unsafeCreate :: Int -> (Ptr Word8 -> IO ()) -> BS
{-# INLINE unsafeCreate #-}
unsafeCreate n f = myUnsafePerformIO $ create n f
myUnsafePerformIO :: IO a -> a
{-# INLINE myUnsafePerformIO #-}
myUnsafePerformIO (IO m) = case m realWorld# of (# _, r #) -> r
singleton :: Word8 -> BS
{-# INLINE singleton #-}
singleton x = unsafeCreate 1 (\p -> poke p x)
main :: IO ()
main = do
let BS _ p = singleton 1
BS _ q = singleton 2
print $ p == q
जो एक समस्या है अगर आप दो अलग-अलग सिंगलेट्स से दो अलग-अलग बफ़र्स का उपयोग करने की अपेक्षा करते हैं।
यहाँ क्या गलत हो रहा है कि व्यापक इनलाइनिंग का मतलब है कि दो mallocForeignPtrBytes 1
कॉल इन singleton 1
और singleton 2
सिंगल आवंटन में फ़्लोट किए जा सकते हैं, पॉइंटर को दो बाइटस्ट्रेस के बीच साझा किया गया है।
यदि आप इनमें से किसी भी कार्य से इनलाइनिंग को हटाते हैं, तो फ्लोटिंग को रोका जा सकेगा, और प्रोग्राम False
उम्मीद के मुताबिक प्रिंट होगा । वैकल्पिक रूप से, आप निम्नलिखित परिवर्तन कर सकते हैं myUnsafePerformIO
:
myUnsafePerformIO :: IO a -> a
{-# INLINE myUnsafePerformIO #-}
myUnsafePerformIO (IO m) = case myRunRW# m of (# _, r #) -> r
myRunRW# :: forall (r :: RuntimeRep) (o :: TYPE r).
(State# RealWorld -> o) -> o
{-# NOINLINE myRunRW# #-}
myRunRW# m = m realWorld#
m realWorld#
गैर- इनलाइन फ़ंक्शन कॉल के साथ इनलाइन एप्लिकेशन को प्रतिस्थापित करना myRunRW# m = m realWorld#
। यह कोड का न्यूनतम हिस्सा है, जो यदि इनलेट नहीं है, तो आवंटन कॉल को उठाए जाने से रोक सकता है।
इस परिवर्तन के बाद, कार्यक्रम False
अपेक्षा के अनुरूप प्रिंट होगा ।
यह वह सब है जो inlinePerformIO
(AKA accursedUnutterablePerformIO
) से स्विच unsafeDupablePerformIO
करता है। यह उस फ़ंक्शन कॉल m realWorld#
को इनलाइन एक्सप्रेशन से समतुल्य गैर-विभिन्न में बदलता है runRW# m = m realWorld#
:
unsafeDupablePerformIO :: IO a -> a
unsafeDupablePerformIO (IO m) = case runRW# m of (# _, a #) -> a
runRW# :: forall (r :: RuntimeRep) (o :: TYPE r).
(State# RealWorld -> o) -> o
{-# NOINLINE runRW# #-}
runRW# m = m realWorld#
सिवाय, निर्मित में runRW#
जादू है। भले ही यह रूप में चिह्नित है NOINLINE
, यह है वास्तव में संकलक द्वारा inlined, लेकिन आवंटन कॉल के बाद संकलन के अंत के पास पहले से ही चल करने से रोका गया है।
तो, आपको उस unsafeDupablePerformIO
कॉल के पूरी तरह से बिना साइड इफ़ेक्ट के पूर्ण रूप से इनलाइन होने का परफॉरमेंस बेनिफिट मिलता है, जिससे अलग-अलग असुरक्षित कॉल्स में सामान्य एक्सप्रेशंस को एक सिंगल सिंगल कॉल में फ्लोट किया जा सकता है।
हालांकि, सच कहा जाए, तो एक लागत है। जब accursedUnutterablePerformIO
सही ढंग से काम करता है, तो यह संभावित रूप से थोड़ा बेहतर प्रदर्शन दे सकता है क्योंकि अनुकूलन के लिए अधिक अवसर हैं यदि m realWorld#
कॉल बाद में होने के बजाय पहले से इनलाइन हो सकती है। इसलिए, वास्तविक bytestring
पुस्तकालय अभी भी accursedUnutterablePerformIO
आंतरिक रूप से बहुत से स्थानों पर उपयोग करता है , विशेष रूप से जहां कोई आवंटन नहीं है (उदाहरण के लिए, head
इसका उपयोग बफर के पहले बाइट को झांकने के लिए करता है)।
unsafeDupablePerformIO
किसी कारण से अधिक सुरक्षित है। अगर मुझे लगता है कि यह शायद अंदर और बाहर से तैरने के साथ कुछ करना होगाrunRW#
। इस प्रश्न का समुचित उत्तर देने वाले किसी व्यक्ति की प्रतीक्षा है।