FFI 中的可变数据和惰性
Mutable data across the FFI and laziness
简介
我正在用 inline-c
包装一个 C 数值库;一些函数可以将回调传递给步骤例程,考虑 ODE 的优化或时间积分。
特别是在本机 C 中使用回调可以对连续数组进行操作,通过指针修改它们,并且 return 将它们转换为一些不透明(分布式)数据结构。
所以这是一个可变数据问题,我想在 Haskell 方面表示它:在我的理解中,在回调中我们应该冻结数组,处理它,例如作为 Data.Vector.Storable.Vector
或 repa
,解冻结果,获取外部指针并将其传回。
内部结构:newtype Vec = Vec (Ptr Vec) deriving Storable
,以及 inline-c
上下文中的关联条目,表示指向不透明 C 数据结构和 vecGetArray
/[=19= 的指针的类型] produce/request 指向连续内存的相同指针和 request/produce 分别是 Vec
。
问:
我注意到,虽然 returned Vector
是正确的,但当我使用结果时,修改 Vec
("side effect"),在 return 从这个函数开始,它没有被修改。 GHC 不会重新计算它(懒惰?)。我如何让它重新计算它? Haskell 中有什么惯用的方法来处理 FFI 中的可变数据?
* 固定 *
查看答案
谢谢!
import qualified Data.Vector.Storable as V
import qualified Data.Vector.Storable.Mutable as VM
withVecGetVectorM ::
Vec ->
(V.Vector PetscScalar_ -> IO (V.Vector PetscScalar_)) ->
IO (V.Vector PetscScalar_)
withVecGetVectorM v f = do
p <- vecGetArrayPtr v
pf <- newForeignPtr_ p
vImm <- V.freeze (VM.unsafeFromForeignPtr0 pf len)
vImmOut <- f vImm
vMutOut <- V.thaw vImmOut
let (fpOut, _, _) = VM.unsafeToForeignPtr vMutOut
pOut = unsafeForeignPtrToPtr fpOut
vecRestoreArrayPtr v pOut
return vImmOut
where len = vecSize v
Vec.hs :
vecGetArrayPtr :: Vec -> IO (Ptr PetscScalar_)
vecGetArrayPtr v = chk1 (vecGetArrayPtr' v)
vecRestoreArrayPtr :: Vec -> Ptr PetscScalar_ -> IO ()
vecRestoreArrayPtr v ar = chk0 (vecRestoreArrayPtr' v ar)
InlineC.hs
-- PETSC_EXTERN PetscErrorCode VecGetArray(Vec,PetscScalar**);
vecGetArrayPtr' :: Vec -> IO (Ptr PetscScalar_, CInt)
vecGetArrayPtr' v = withPtr $ \p -> vga v p where
vga v p = [C.exp|int{VecGetArray($(Vec v), $(PetscScalar** p))}|]
-- PETSC_EXTERN PetscErrorCode VecRestoreArray(Vec,PetscScalar**);
vecRestoreArrayPtr' :: Vec -> Ptr PetscScalar_ -> IO CInt
vecRestoreArrayPtr' v c = with c $ \pc -> vra v pc
where
vra w pc = [C.exp|int{VecRestoreArray($(Vec w), $(PetscScalar** pc))}|]
此外,IIUC,代码制作了 2 个额外的矢量副本,一个在冻结时,一个在解冻时。 ,但我怀疑它效率低下。有人可以提出改进或简化建议吗?
固定:
我犯了一个微不足道的错误。要处理的指针;这是解决方法。这个函数与有问题的函数有相同的签名,这表明我必须有一种类型化的方式来表示我们正在修改哪个指针。如果有人对此有任何建议,请随时与我们联系。
vecRestoreVector :: Vec -> V.Vector PetscScalar_ -> IO ()
vecRestoreVector v w = do
p <- vecGetArrayPtr v
pf <- newForeignPtr_ p
V.copy (VM.unsafeFromForeignPtr0 pf len) w
vecRestoreArrayPtr v p
where
len = vecSize v
简介
我正在用 inline-c
包装一个 C 数值库;一些函数可以将回调传递给步骤例程,考虑 ODE 的优化或时间积分。
特别是在本机 C 中使用回调可以对连续数组进行操作,通过指针修改它们,并且 return 将它们转换为一些不透明(分布式)数据结构。
所以这是一个可变数据问题,我想在 Haskell 方面表示它:在我的理解中,在回调中我们应该冻结数组,处理它,例如作为 Data.Vector.Storable.Vector
或 repa
,解冻结果,获取外部指针并将其传回。
内部结构:newtype Vec = Vec (Ptr Vec) deriving Storable
,以及 inline-c
上下文中的关联条目,表示指向不透明 C 数据结构和 vecGetArray
/[=19= 的指针的类型] produce/request 指向连续内存的相同指针和 request/produce 分别是 Vec
。
问:
我注意到,虽然 returned Vector
是正确的,但当我使用结果时,修改 Vec
("side effect"),在 return 从这个函数开始,它没有被修改。 GHC 不会重新计算它(懒惰?)。我如何让它重新计算它? Haskell 中有什么惯用的方法来处理 FFI 中的可变数据?
* 固定 *
查看答案
谢谢!
import qualified Data.Vector.Storable as V
import qualified Data.Vector.Storable.Mutable as VM
withVecGetVectorM ::
Vec ->
(V.Vector PetscScalar_ -> IO (V.Vector PetscScalar_)) ->
IO (V.Vector PetscScalar_)
withVecGetVectorM v f = do
p <- vecGetArrayPtr v
pf <- newForeignPtr_ p
vImm <- V.freeze (VM.unsafeFromForeignPtr0 pf len)
vImmOut <- f vImm
vMutOut <- V.thaw vImmOut
let (fpOut, _, _) = VM.unsafeToForeignPtr vMutOut
pOut = unsafeForeignPtrToPtr fpOut
vecRestoreArrayPtr v pOut
return vImmOut
where len = vecSize v
Vec.hs :
vecGetArrayPtr :: Vec -> IO (Ptr PetscScalar_)
vecGetArrayPtr v = chk1 (vecGetArrayPtr' v)
vecRestoreArrayPtr :: Vec -> Ptr PetscScalar_ -> IO ()
vecRestoreArrayPtr v ar = chk0 (vecRestoreArrayPtr' v ar)
InlineC.hs
-- PETSC_EXTERN PetscErrorCode VecGetArray(Vec,PetscScalar**);
vecGetArrayPtr' :: Vec -> IO (Ptr PetscScalar_, CInt)
vecGetArrayPtr' v = withPtr $ \p -> vga v p where
vga v p = [C.exp|int{VecGetArray($(Vec v), $(PetscScalar** p))}|]
-- PETSC_EXTERN PetscErrorCode VecRestoreArray(Vec,PetscScalar**);
vecRestoreArrayPtr' :: Vec -> Ptr PetscScalar_ -> IO CInt
vecRestoreArrayPtr' v c = with c $ \pc -> vra v pc
where
vra w pc = [C.exp|int{VecRestoreArray($(Vec w), $(PetscScalar** pc))}|]
此外,IIUC,代码制作了 2 个额外的矢量副本,一个在冻结时,一个在解冻时。 ,但我怀疑它效率低下。有人可以提出改进或简化建议吗?
固定:
我犯了一个微不足道的错误。要处理的指针;这是解决方法。这个函数与有问题的函数有相同的签名,这表明我必须有一种类型化的方式来表示我们正在修改哪个指针。如果有人对此有任何建议,请随时与我们联系。
vecRestoreVector :: Vec -> V.Vector PetscScalar_ -> IO ()
vecRestoreVector v w = do
p <- vecGetArrayPtr v
pf <- newForeignPtr_ p
V.copy (VM.unsafeFromForeignPtr0 pf len) w
vecRestoreArrayPtr v p
where
len = vecSize v