অনিরাপদযোগ্যযোগ্য পারফর্মআইআইও এবং অভিশপ্ত অপ্রয়োজনীয় পারফর্মআইআই এর মধ্যে পার্থক্য কী?


13

আমি হাস্কেল লাইব্রেরির সীমাবদ্ধ বিভাগে ঘুরে বেড়াছিলাম এবং এই দু'টি দুষ্কর মন্ত্রটি পেয়েছি:

{- System.IO.Unsafe -}
unsafeDupablePerformIO  :: IO a -> a
unsafeDupablePerformIO (IO m) = case runRW# m of (# _, a #) -> a

{- Data.ByteString.Internal -}
accursedUnutterablePerformIO :: IO a -> a
accursedUnutterablePerformIO (IO m) = case m realWorld# of (# _, r #) -> r

আসল পার্থক্যটি কেবলমাত্র runRW#এবং এর মধ্যে বলে মনে হচ্ছে ($ realWorld#)। তারা কী করছে সে সম্পর্কে আমার কিছু প্রাথমিক ধারণা আছে তবে আমি একে অপরের ব্যবহারের প্রকৃত পরিণতি পাই না। কেউ আমাকে ব্যাখ্যা করতে পারে পার্থক্য কি?


3
unsafeDupablePerformIOকোনও কারণে নিরাপদ। যদি আমি অনুমান করতেই পারি তবে সম্ভবত ইনলাইনিং এবং ভাসমান অবস্থায় কিছু করতে হবে runRW#। এই প্রশ্নের যথাযথ উত্তর দেওয়ার জন্য কেউ অপেক্ষা করছেন।
23

উত্তর:


11

সরলিকৃত বাইস্টেটিং লাইব্রেরি বিবেচনা করুন। আপনার দৈর্ঘ্য এবং বাইটের একটি বরাদ্দযুক্ত বাফার সমন্বয়ে একটি বাইট স্ট্রিং টাইপ থাকতে পারে:

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এটি বাফারের প্রথম বাইটটি উঁকি দেওয়ার জন্য ব্যবহার করে)।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.