2013-08-07 22 views
5

Piszę aplikację, która współdziała z dużym (10-1000 GB) plikiem binarnym odwzorowanym w pamięci, trzymając w zasadzie kilka obiektów, które się do siebie odwołują. Wymyśliłem mechanizm do odczytu/zapisu tych danych, który jest skuteczny, ale brzydki i pełny (imo).Przechowywanie dużych strukturalnych danych binarnych za pomocą Haskella

P: Czy istnieje bardziej elegancki sposób na osiągnięcie tego, co zrobiłem?

Mam typeclass dla danych strukturalnych, z jednej metody, które odczytuje strukturę do typu danych Haskell (DataOp jest ReaderT wokół IO).

class DBStruct a where 
    structRead :: Addr a -> DataOp a 

Aby to bardziej czytelne, mam inny typeclass definiujący co członkowie struktury tam, gdzie:

class DBStruct st => StructMem structTy valTy name | structTy name -> valTy where 
    offset :: structTy -> valTy -> name -> Int64 

Mam kilka funkcji pomocniczych, które wykorzystują metody offset dla elementów konstrukcji odczytu/zapisu, do czytania struktur z zapisanych odniesień i leniwie odraczających odczytów struktury (w celu umożliwienia leniwego odczytu całego pliku).

Problem polega na tym, że wymaga użycia wielu powtórzeń. Na jednej strukturze, najpierw trzeba określić typ Haskell:

data RowBlock = RowBlock {rbNext :: Maybe RowBlock 
         ,rbPrev :: Maybe RowBlock 
         ,rbRows :: [RowTy] 
         } 

Następnie rodzaje name:

data Next = Next 
data Prev = Prev 
data Count = Count 
newtype Row = Row Int64 

Następnie wystąpień dla każdego elementu struktury:

instance StructMem RowBlock (Maybe (Addr RowBlock)) Next where offset _ _ _ = 0 
instance StructMem RowBlock (Maybe (Addr RowBlock)) Prev where offset _ _ _ = 8 
instance StructMem RowBlock Int64 Count where offset _ _ _ = 16 
instance StructMem RowBlock RowTy Row where offset _ _ (Row n) = 24 + n * 8 

Następnie strukturę przeczytaj metodę:

instance DBStruct RowBlock where 
    structRead a = do 
     n <- elemMaybePtr a Next 
     p <- elemMaybePtr a Prev 
     c <- elemRead a Count 
     rs <- mapM (elemRead a . Row) [0 .. c-1] 
     return $ RowBlock n p rs 

Więc wszystko, co naprawdę osiągnąłem, to ponowne wdrożenie struktur C w dużo bardziej gadatliwy (i powolny) sposób. Byłbym szczęśliwszy, gdyby to było bardziej zwięzłe, przy jednoczesnym zachowaniu bezpieczeństwa typu. Z pewnością jest to powszechnie spotykany problem.

kilka możliwych alternatyw mogę myśleć to:

  • Ditch pliki w pamięci i używać Data.Binary, pisanie ByteStrings na dysk normalny sposób.
  • Zastosowanie deriving Generic stworzyć ogólne funkcje odczytu i zapisu
  • Overload Functional References
  • Czy coś magicznego z monadycznych soczewek.

EDIT: SSCCE as requested

+0

Czy możesz podać uproszczony, samodzielny przykład, który można skompilować, od czego zacząć? –

+0

@ PetrPudlák Mogę zrobić to później, jeśli uważasz, że to pomoże ludziom znaleźć odpowiedź. Jednak miało to być bardziej architektoniczne pytanie niż pytanie "dlaczego nie jest to mój kod"; Kod, który napisałem, ma być bardziej ilustracją mojej obecnej architektury niż cokolwiek innego. – Dan

+1

Tak, [SSCCE] (http://www.sscce.org/) pomoże mi lepiej zrozumieć obecny projekt. –

Odpowiedz

1

Można spróbować użyć Data.Binary ze swoimi ptrs.

do zapisu:

Zastosowanie Data.Binary zbudować ByteString. ByteString to krotka (ForeignPtr Word8, Int, Int), która przechowuje adres, przesunięcie i długość, w której przechowywane są dane. Możesz użyć pakietu Data.ByteString.Internal, aby uzyskać plik ForeignPtr, który rozpakuje dla ciebie krotkę. Obcy.ForeignPtr dostarcza withForeignPtr, który przyjmuje funkcję, która wykonuje akcję IO za pośrednictwem wskaźnika. Tam możesz zapamiętywać (wiązanie do tego jest również zapewnione w Data.ByteString.Internal) pamięci bytestring do mmapped Ptr, które dostałeś od mmap.

do odczytu:

Można użyć Data.ByteString.Internal za fromForiegnPtr obrócić PTR w bytestring. Zasadniczo to robią biblioteki mmap, ale możesz zrobić to jednorazowo, zamiast z całym regionem. Gdy masz już widok ByteString w pamięci, możesz rozpakować go za pomocą Data.Binary.

Inną opcją jest skorzystanie z faktu, że ByteString ma alternatywną implementację w Data.Vector.Storable.ByteString, która pozwoliłaby ci użyć interfejsu Storable, którego teraz używasz do odczytu/zapisu do mmted Ptrs . Interfejs i podstawowy typ są izomorficzne dla elementu Data.ByteString, ale ma on instancje przechowywania.

+0

Potrzebuję również móc pisać do pliku. – Dan

Powiązane problemy