সরলিকৃত বাইস্টেটিং লাইব্রেরি বিবেচনা করুন। আপনার দৈর্ঘ্য এবং বাইটের একটি বরাদ্দযুক্ত বাফার সমন্বয়ে একটি বাইট স্ট্রিং টাইপ থাকতে পারে:
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)
নিম্নলিখিত প্রোগ্রামটি মুদ্রণ করে আপনি অবাক হয়ে যেতে পারেন 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
পয়েন্টার দুই bytestrings মধ্যে ভাগ সঙ্গে একটি একক বরাদ্দ ছড়িয়ে ভেসে করা যায়।
আপনি যদি এই ফাংশনগুলির কোনওর থেকে ইনলাইনিং সরিয়ে ফেলেন তবে ভাসমানটি প্রতিরোধ করা হবে এবং 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
(এ কেএ 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#
। এই প্রশ্নের যথাযথ উত্তর দেওয়ার জন্য কেউ অপেক্ষা করছেন।