哈斯克尔交换和C之间的结构化数据 [英] Interchange structured data between Haskell and C

查看:132
本文介绍了哈斯克尔交换和C之间的结构化数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,我是一个Haskell初学者。

First, I'm a Haskell beginner.

我正打算整合哈斯克尔成C的实时游戏。
哈斯克尔做逻辑,C不渲染。要做到这一点,我必须通过从/给对方每个蜱(每秒至少30次)庞大复杂地结构化数据(游戏状态)。因此,传球数据应该是轻量级的。这种状态的数据可以放在内存时序空间。双方的哈斯克尔和C部分应该进入自由状态的每一个领域。

I'm planning integrating Haskell into C for realtime game. Haskell does logic, C does rendering. To do this, I have to pass huge complexly structured data (game state) from/to each other for each tick (at least 30 times per second). So the passing data should be lightweight. This state data may laid on sequential space on memory. Both of Haskell and C parts should access every area of the states freely.

在最好的情况下,通过数据的成本可以是复制的指针的存储器。在最坏的情况下,复制整个数据与转换。

In best case, the cost of passing data can be copying a pointer to a memory. In worst case, copying whole data with conversion.

我在读Haskell的FFI(http://www.haskell.org/haskellwiki/FFICookBook#Working_with_structs)
哈斯克尔code看起来指定内存布局明确。

I'm reading Haskell's FFI(http://www.haskell.org/haskellwiki/FFICookBook#Working_with_structs) The Haskell code look specifying memory layout explicitly.

我有几个问题。


  1. 哈斯克尔可以指定内存布局明确? (与C结构完全匹配)

  2. 这是真正的内存布局?或任何形式的转换所需要的? (性能下降)

  3. 如果Q#2是真实的,在明确指定的任何性能损失的内存布局?

  4. 什么是语法#{对齐富} ?我在哪里可以找到这个文件样?

  5. 如果我想通过与最佳性能庞大的数据,我应该怎么做呢?

  1. Can Haskell specify memory layout explicitly? (to be matched exactly with C struct)
  2. Is this real memory layout? Or any kind of conversion required? (performance penalty)
  3. If Q#2 is true, Any performance penalty when the memory layout specified explicitly?
  4. What's the syntax #{alignment foo}? Where can I find the document about this?
  5. If I want to pass huge data with best performance, how should I do that?

* PS
外显记忆布局功能,我说的只是C#的[StructLayout]属性。这是指定的内存位置和大小明确。
<一href=\"http://www.developerfusion.com/article/84519/mastering-structs-in-c/\">http://www.developerfusion.com/article/84519/mastering-structs-in-c/

我不知道哈斯克尔已经匹配的语言结构与C结构的配套领域。

I'm not sure Haskell has matching linguistic construct matching with fields of C struct.

推荐答案

我会强烈建议使用preprocessor。我喜欢c2hs,而是因为它包含GHC hsc2hs是很常见的。绿卡似乎被抛弃。

I would strongly recommend using a preprocessor. I like c2hs, but hsc2hs is very common because it's included with ghc. Greencard appears to be abandoned.

要回答你的问题:

1)是,通过可存储实例的定义。使用可存储是唯一的安全机制,通过FFI传递数据。可存储实例定义如何在Haskell的类型和原始内存之间的数据编组(无论是一个Haskell PTR,ForeignPtr或StablePtr,或C指针)。这里有一个例子:

1) Yes, through the definition of the Storable instance. Using Storable is the only safe mechanism to pass data through the FFI. The Storable instance defines how to marshal data between a Haskell type and raw memory (either a Haskell Ptr, ForeignPtr, or StablePtr, or a C pointer). Here's an example:

data PlateC = PlateC {
  numX :: Int,
  numY :: Int,
  v1   :: Double,
  v2   :: Double } deriving (Eq, Show)

instance Storable PlateC where
  alignment _ = alignment (undefined :: CDouble)
  sizeOf _ = {#sizeof PlateC#}
  peek p =
    PlateC <$> fmap fI ({#get PlateC.numX #} p)
           <*> fmap fI ({#get PlateC.numY #} p)
           <*> fmap realToFrac ({#get PlateC.v1 #} p)
           <*> fmap realToFrac ({#get PlateC.v2 #} p)
  poke p (PlateC xv yv v1v v2v) = do
    {#set PlateC.numX #} p (fI xv)
    {#set PlateC.numY #} p (fI yv)
    {#set PlateC.v1 #}   p (realToFrac v1v)
    {#set PlateC.v2 #}   p (realToFrac v2v)

{#...#} 片段c2hs code。 网​​络 fromIntegral 。在获取和设置片段值指的是下面的结构从包含的头,而不是Haskell的类型相同的名称:

The {# ... #} fragments are c2hs code. fI is fromIntegral. The values in the get and set fragments refer to the following struct from an included header, not the Haskell type of the same name:

struct PlateCTag ;

typedef struct PlateCTag {
  int numX;
  int numY;
  double v1;
  double v2;
} PlateC ;

c2hs转换成以下普通的Haskell:

c2hs converts this to the following plain Haskell:

instance Storable PlateC where
  alignment _ = alignment (undefined :: CDouble)
  sizeOf _ = 24
  peek p =
    PlateC <$> fmap fI ((\ptr -> do {peekByteOff ptr 0 ::IO CInt}) p)
           <*> fmap fI ((\ptr -> do {peekByteOff ptr 4 ::IO CInt}) p)
           <*> fmap realToFrac ((\ptr -> do {peekByteOff ptr 8 ::IO CDouble}) p)
           <*> fmap realToFrac ((\ptr -> do {peekByteOff ptr 16 ::IO CDouble}) p)
  poke p (PlateC xv yv v1v v2v) = do
    (\ptr val -> do {pokeByteOff ptr 0 (val::CInt)}) p (fI xv)
    (\ptr val -> do {pokeByteOff ptr 4 (val::CInt)}) p (fI yv)
    (\ptr val -> do {pokeByteOff ptr 8 (val::CDouble)})   p (realToFrac v1v)
    (\ptr val -> do {pokeByteOff ptr 16 (val::CDouble)})   p (realToFrac v2v)

偏移量当然依赖于体系结构的,因此使用pre-processer允许你写便携式code。

The offsets are of course architecture-dependent, so using a pre-processer allows you to write portable code.

您通过为数据类型分配空间(的malloc 等),使用这个和 ING数据到PTR(或ForeignPtr)。

You use this by allocating space for your data type (new,malloc, etc.) and pokeing the data into the Ptr (or ForeignPtr).

2)这是真正的内存布局。

2) This is the real memory layout.

3)/有一个点球阅读与写偷看 / 。如果你有大量的数据,最好只有你所需要的转换,例如从C数组读一个元素,而不是整个数组编组为一个Haskell名单。

3) There is a penalty for reading/writing with peek/poke. If you have a lot of data, it's better to convert only what you need, e.g. reading just one element from a C array instead of marshalling the entire array to a Haskell list.

4)语法取决于您选择的preprocessor。 c2hs文档 hsc2hs文档。令人困惑的是,hsc2​​hs使用语法 #stuff #{东西} ,而c2hs使用 { #stuff#}

4) Syntax depends upon the preprocessor you choose. c2hs docs. hsc2hs docs. Confusingly, hsc2hs uses the syntax #stuff or #{stuff}, while c2hs uses {#stuff #}.

5)@ sclv的建议是什么,我会做的一样好。写一个可存储的实例,并保持一个指向数据的指针。你可以编写C函数来完成所有的工作,并利用窥视称他们通过FFI,或(不太好的)编写低级Haskell和捅到你需要的数据只是部分运作上。编组整个事情前后(即调用偷看对整个数据结构)将是昂贵的,但如果你只通过各地的价格指针将是最小的。

5) @sclv's suggestion is what I would do as well. Write a Storable instance and keep a pointer to the data. You can either write C functions to do all the work and call them through the FFI, or (less good) write low-level Haskell using peek and poke to operate on just the parts of the data you need. Marshalling the whole thing back and forth (i.e. calling peek or poke on the entire data structure) will be expensive, but if you only pass pointers around the cost will be minimal.

通过FFI调用导入函数有显著处罚,除非他们标记为不安全。声明进口不安全意味着该函数不应该回调到Haskell的或不确定的行为。如果您在使用并发或并行,这也意味着,在相同的能力(即CPU)的所有哈斯克尔线程将阻塞,直到调用返回,所以应该很快回来。如果这些条件是可以接受的不安全的呼叫是比较快的。

Calling imported functions through the FFI has a significant penalty unless they're marked "unsafe". Declaring an import "unsafe" means that the function should not call back into Haskell or undefined behavior results. If you're using concurrency or parallelism, it also means that all Haskell threads on the same capability (i.e. CPU) will block until the call returns, so it should return fairly quickly. If those conditions are acceptable an "unsafe" call is relatively fast.

有很多,与这样的事情处理上Hackage包。我可以推荐 hsndfile ​​ hCsound作为表现出良好的实践与c2hs。如果你看一个绑定到一个小的C库你熟悉的,虽然它可能更容易。

There are a lot of packages on Hackage that deal with this sort of thing. I can recommend hsndfile and hCsound as exhibiting good practice with c2hs. It's probably easier if you look at a binding to a small C library you're familiar with though.

这篇关于哈斯克尔交换和C之间的结构化数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆