Структурированные данные обмена между Haskell и C

StackOverflow https://stackoverflow.com/questions/4502115

  •  12-10-2019
  •  | 
  •  

Вопрос

Во -первых, я новичок в Хаскелле.

Я планирую интегрировать Haskell в C для игры в реальном времени. Haskell делает логику, C делает рендеринг. Чтобы сделать это, я должен передавать огромные сложные структурированные данные (состояние игры) друг от друга для каждого тика (не менее 30 раз в секунду). Таким образом, передача данных должно быть легким. Данные этого состояния могут заложены в последовательном пространстве на памяти. Оба частей Хаскелла и С должны свободно получать доступ к каждой области штатов.

В лучшем случае стоимость передачи данных может быть копирование указателя на память. В худшем случае копирование целых данных с конверсией.

Я читаю FFI Haskell (http://www.haskell.org/haskellwiki/fficookbook#working_with_structs) Код Haskell явно определяет макет памяти.

У меня есть несколько вопросов.

  1. Может ли Haskell указать макет памяти явно? (чтобы точно соответствовать C -структуре)
  2. Это настоящая макет памяти? Или требуется какая -либо преобразование? (штраф за производительность)
  3. Если Q#2 верно, какой -нибудь штраф за производительность, когда макет памяти явно указан?
  4. Что такое синтаксис #{alignment foo}? Где я могу найти документ об этом?
  5. Если я хочу передавать огромные данные с лучшей производительностью, как мне это сделать?

*PS явная функция макета памяти, которая, как я сказал, - это просто атрибут C#[structlayout]. Который явно определяет положение и размер в памяти.http://www.developerfusion.com/article/84519/mastering structs-in-c/

Я не уверен, что Haskell имеет соответствующую лингвистическую конструкцию с полями C struct.

Это было полезно?

Решение

Я настоятельно рекомендую использовать препроцессор. Мне нравится C2HS, но HSC2HS очень распространен, потому что он включен в GHC. Греенсард, кажется, оставлена.

Чтобы ответить на ваши вопросы:

1) Да, через определение хранения. Использование Flabube - единственный безопасный механизм для передачи данных через FFI. Хлан -экземпляр определяет, как создавать данные между типом Haskell и необработанной памятью (либо Haskell PTR, ForeignPTR или StablePTR, либо указатель C). Вот пример:

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. fI является fromIntegral. Анкет Значения в фрагментах GET и SET относятся к следующему структуру из включенного заголовка, а не с тем же названием Haskell Type:

struct PlateCTag ;

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

C2HS преобразует это в следующую простой 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)

Смещения, конечно, зависят от архитектуры, поэтому использование предварительного процесса позволяет писать портативный код.

Вы используете это, выделяя пространство для вашего типа данных (new,malloc, и т. д.) и pokeДанные в PTR (или ForeignPtr).

2) Это настоящая макет памяти.

3) Существует штраф за чтение/написание с peek/poke. Анкет Если у вас много данных, лучше преобразовать только то, что вам нужно, например, прочитать только один элемент из массива C вместо того, чтобы составить весь массив в список Haskell.

4) Синтаксис зависит от выбранного вами препроцессора. C2HS DOCS. HSC2HS DOCS. Анкет В замешательстве HSC2HS использует синтаксис #stuff или же #{stuff}, в то время как C2HS использует {#stuff #}.

5) @SCLV предложение - это то, что я бы сделал. Напишите художественный экземпляр и сохраните указатель на данные. Вы можете либо написать функции C, чтобы выполнять всю работу, и позвонить им через FFI, либо (менее хорошо) написать низкоуровневый Haskell, используя Peek и Poke, чтобы работать только на частях необходимых данных. Разыгрывая все это взад и вперед (т.е. peek или же poke Во всей структуре данных) будет дорого, но если вы проходите только указатели по стоимости, будут минимальными.

Вызов импортируемых функций через FFI имеет значительный штраф, если они помечены «небезопасно». Объявление импорта «небезопасно» означает, что функция не должна возвращаться к результатам Haskell или неопределенного поведения. Если вы используете параллелизму или параллелизм, это также означает, что все потоки Haskell с той же возможности (IE CPU) будут блокироваться до возврата вызова, поэтому он должен вернуться довольно быстро. Если эти условия являются приемлемыми, «небезопасной» вызов относительно быстрый.

Есть много пакетов на хаке, которые имеют дело с такими вещами. Я могу порекомендовать hsndfile а также HCSound как выставляя хорошую практику с C2HS. Вероятно, это проще, если вы посмотрите на привязку к небольшой библиотеке C, с которой вы знакомы.

Другие советы

Несмотря на то, что вы можете получить детерминированную макет памяти для строгих распадных структур Haskell, нет никаких гарантий, и это действительно плохая идея.

Если вы готовы жить с конверсией, есть годный: http://www.haskell.org/ghc/docs/6.12.3/html/libraries/base-4.2.0.2/foreign-stable.html

Что я делаю, так это построить структуры C, а затем построить функции Haskell, которые работают непосредственно на них, используя FFI, а не пытаются создать «эквиваленты Haskell» для них.

С другой стороны, вы можете решить, что вам нужно только передать информацию о избранной информации в C - не все состояние игры, а лишь несколько фрагментов информации о том, какие объекты находятся в мире, с вашей реальной информацией о том, как это Нарисуйте их, живя исключительно на стороне C уравнения. Затем вы выполняете всю логику в Хаскелле, работая на собственных структурах Хаскелла и только проецируете в мир C, что крошечное подмножество данных, с которыми C, с которым C, фактически необходимо сделать.

РЕДАКТИРОВАТЬ: Я должен добавить, что матрицы и другие общие структуры С уже имеют отличные библиотеки/привязки, которые поддерживают тяжелую работу на стороне C.

HSC2HS, C → HS, а также Зеленая карта Все обеспечивают автоматизированную структуру Haskell⇆c Peek/Poke или Marshalling. Я бы порекомендовал их использование вручную, определяя размеры и смещения и использование манипуляций с указателями в Хаскелле, хотя это тоже возможно.

  1. Насколько я знаю, не знаю, если я правильно вас понимаю. У Хаскелла нет встроенной обработки иностранных совокупных структур данных.
  2.  
  3.  
  4. Как описывает эта страница, это hsc2hs С некоторой магией C.
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top