gpt4 book ai didi

haskell - 在haskell中,在不再引用外部指针后立即完成它们

转载 作者:行者123 更新时间:2023-12-03 15:06:25 25 4
gpt4 key购买 nike

有没有办法确保某些特定类型的对象(基本上是 ForeignPtr )被非常积极地进行垃圾收集?我有一些像这样的简单类型:

data SomePtr = SomePtr { ptr :: ForeignPtr CUChar, size :: CSize }
alloc :: CSize -> IO SomePtr
free :: SomePtr -> IO ()
free = finalizeForeignPtr . ptr

我认为标准理论是引用计数(这就是我自己在 C++ 中这样做的方式)比 ghc 使用的 GC 慢,这就是它不使用它的原因。但对我来说问题是,当使用外部分配的对象时,比如 GPU 内存,它最终会被释放的 promise 是不够的。内存非常稀缺,据我所知, ForeignPtr当我想要它时,实际上并没有调用终结器。我希望尽快释放内存,所以我最终调用 finalizeForeignPtr我。

有没有办法告诉 ghc 在销毁某些特定类型的对象时非常积极?

或者我正在以错误的方式解决这个问题?

这是示例代码来说明我的意思:

haskell

{-# LANGUAGE RecordWildCards #-}
import Foreign.ForeignPtr.Safe
import Foreign.Ptr
import Foreign.Marshal.Alloc
import Foreign.Storable
import Control.Monad
import Foreign.C.Types
import Text.Printf

data FPtr = FPtr { fptr :: ForeignPtr CUChar, size :: CSize }

foreign import ccall "falloc" falloc :: CSize -> Ptr (Ptr CUChar) -> IO CInt
foreign import ccall "&ffree" ffree :: FunPtr (Ptr CUChar -> IO ())

newFPtr :: CSize -> IO FPtr
newFPtr size =
do alloca $ \ptr -> do
result <- falloc size ptr
printf "Result: %d\n" (fromIntegral result :: Int)
fptr <- newForeignPtr ffree =<< peek ptr
return FPtr{..}

freeFPtr :: FPtr -> IO ()
freeFPtr = finalizeForeignPtr . fptr

main :: IO ()
main = forM_ [1 .. 5] $ const work
where
work = do x <- newFPtr 1024
-- freeFPtr x
return ()

C++

#include <cstdio>
using namespace std;

extern "C" {
int falloc(size_t size, unsigned char** ptr);
void ffree(unsigned char* ptr);
}

int some_counter = 0;

int falloc(size_t size, unsigned char** ptr) {
some_counter++;
printf("falloc(%lu, %#lx, %#lx); %d\n",
size, (unsigned long)ptr, (unsigned long)*ptr, some_counter);
*ptr = new unsigned char[size];
return 0;
}

void ffree(unsigned char* ptr) {
printf("ffree(%#lx)\n", (unsigned long)ptr);
delete[] ptr;
}

输出

falloc(1024, 0x100606010, 0); 1
Result: 0
falloc(1024, 0x100606028, 0); 2
Result: 0
falloc(1024, 0x100606040, 0); 3
Result: 0
falloc(1024, 0x100606058, 0); 4
Result: 0
falloc(1024, 0x100606070, 0); 5
Result: 0
ffree(0x101026400)
ffree(0x101027800)
ffree(0x101027c00)
ffree(0x101028000)
ffree(0x101028400)

期望的输出

falloc(1024, 0x100606010, 0); 1
Result: 0
ffree(0x101026400)
falloc(1024, 0x100606028, 0); 2
Result: 0
ffree(0x100802200)
falloc(1024, 0x100606040, 0); 3
Result: 0
ffree(0x100802200)
falloc(1024, 0x100606058, 0); 4
Result: 0
ffree(0x100802200)
falloc(1024, 0x100606070, 0); 5
Result: 0
ffree(0x100802200)

最佳答案

如果我们想独立于 GHC 的垃圾收集,我们需要引入某种确定性,因此需要显式释放。分配通常是 IO a 类型的东西,以及相应的 a -> IO () 类型的释放(就像你的例子)。

现在,如果我们有以下函数呢?

allocate  :: IO a -> (a -> IO ()) -> Alloc a
runAlloc :: Alloc a -> IO a
autoAllocate应该同时进行分配和解除分配,并在新的(表面) Alloc 中为您提供分配的结果单子(monad)和 runAlloc运行所有操作和释放。您的示例不会有太大变化,除了结尾:
allocateFPtr size = autoAllocate (newFPtr size) freeFPtr

main :: IO ()
main = forM_ [1 .. 5] $ runAlloc . const work
where
work = do x <- allocateFPtr 1024
return ()

现在, allocate , runAllocAlloc已存在于 resourcet allocate , runResourceT ResourceT ,实际代码如下所示:
allocateFPtr size = fmap snd $ allocate (newFPtr size) freeFPtr

main :: IO ()
main = forM_ [1 .. 5] $ runResourceT . const work
where
work = do x <- allocateFPtr 1024
return ()

结果:

法洛克(1024,0x1e04014,0); 1
结果:0
ffree(0x6abc60)
法洛克(1024,0x1e04020,0); 2
结果:0
ffree(0x6abc60)
法洛克(1024,0x1e0402c,0); 3
结果:0
ffree(0x6abc60)
法洛克(1024,0x1e04038,0); 4
结果:0
ffree(0x6abc60)
法洛克(1024,0x1e04044,0); 5
结果:0
ffree(0x6abc60)

但是你说你的一些指针实际上应该活得更久。这也不是问题,因为 allocate实际返回 m (ReleaseKey, a) , 和 ReleaseKey可用于在 runResourceT 之前释放内存(使用 release )或删除自动释放机制(使用 unprotect ,它返回释放操作)。

所以,总而言之,我想你的场景可以用 ResourceT 很好地处理。 .毕竟,它的概要是“确定性分配和释放稀缺资源”。

关于haskell - 在haskell中,在不再引用外部指针后立即完成它们,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24980766/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com