2012-02-22 38 views
22

Các loại chuỗi Haskell thường được đề nghị có vẻ là ByteString hoặc Text. Tôi thường làm việc với một số lượng rất lớn các chuỗi ngắn (từ tiếng Anh), và thường cần lưu trữ chúng trong một bảng tra cứu như Data.Map. Trong nhiều trường hợp, tôi thấy rằng trong trường hợp này, một bảng các chuỗi có thể chiếm ít bộ nhớ hơn sau đó là một bảng ByteStrings. Data.Vector không được phân loại của Word8 cũng nhỏ hơn nhiều so với ByteStrings.Các chuỗi ký hiệu hiệu quả trong Haskell

Phương pháp hay nhất khi cần lưu trữ và so sánh số lượng lớn các chuỗi nhỏ trong Haskell là gì?

Dưới đây tôi đã cố gắng cô đọng một trường hợp có vấn đề cụ thể vào một ví dụ nhỏ:

import qualified Data.ByteString.Lazy.Char8 as S 
import qualified Data.ByteString as Strict 
import qualified Data.Map as Map 
import qualified Data.Vector.Unboxed as U 
import qualified Data.Serialize as Serialize 
import Control.Monad.State 

main = putStr 
    . unlines . map show . flip evalState (0,Map.empty) 
    . mapM toInt 
    . S.words 
    =<< 
    S.getContents 


toInt x = do 
    let x' = 
      U.fromList . Strict.unpack . -- Comment this line to increase memory usage 
      Serialize.encode $ x 
    (i,t) <- get 
    case Map.lookup x' t of 
    Just j -> return j 
    Nothing -> do 
     let i' = i + (1::Int) 
     put (i', Map.insert x' i t) 
     return i 

Khi tôi chạy trên một tập tin có chứa khoảng 400.000 lời của văn bản tiếng Anh, phiên bản với các phím bytestring nghiêm ngặt sử dụng khoảng 50MB bộ nhớ, một với các vectơ Word8 sử dụng 6MB.

+0

Bạn đang sử dụng những chuỗi đó để làm gì? Nó là một loại từ điển nào đó? – ARRG

+6

Bạn có thể đưa ra một số ví dụ mã trong đó ByteStrings chiếm nhiều bộ nhớ hơn Strings hoặc bộ nhớ "nhiều hơn" so với các vectơ Word8 không? Tôi không thể hiểu tại sao điều đó xảy ra trừ khi bạn đang làm điều gì đó kỳ lạ. – shang

+7

@shang: Tôi có thể tưởng tượng điều này xảy ra nếu bạn đang phạm sai lầm so sánh kích thước của một bản đồ đầy đủ các ByteStrings nghiêm ngặt với một bản đồ có chứa chuỗi khối. Mặc dù nhiều chi tiết hơn sẽ hữu ích. Một chương trình thử nghiệm ngắn cho thấy vấn đề sẽ đặc biệt tốt đẹp. – hammar

Trả lời

5

Trong trường hợp không có câu trả lời khác, tôi sẽ đi ra ngoài một chi ở đây.

Phương pháp hay nhất khi cần lưu trữ và so sánh số lượng lớn các chuỗi nhỏ trong Haskell là gì?

Nếu các chuỗi nhỏ có nghĩa là có thể đọc được bằng con người (ví dụ: từ tiếng Anh) thì hãy sử dụng Text. Nếu chúng chỉ được đọc bởi máy tính, hãy sử dụng ByteString. Quyết định sử dụng các biến thể nghiêm ngặt hoặc lười biếng trong số này phụ thuộc vào cách bạn xây dựng và sử dụng các chuỗi nhỏ này.

Bạn không cần phải sử dụng riêng của mình unboxed Vector s của Word8. Nếu bạn gặp một tình huống cụ thể, nơi thường xuyên String nhanh hơn Text hoặc ByteString, sau đó ném chi tiết lên trên StackOverflow và chúng tôi sẽ cố gắng tìm ra lý do. Nếu bạn thực hiện phân tích chi tiết và có thể chứng minh rằng Vector không hợp lệ của Word8 luôn hoạt động tốt hơn đáng kể so với Text hoặc ByteString, sau đó bắt đầu cuộc hội thoại trên danh sách gửi thư, irc, reddit, v.v. các thư viện chuẩn không được đặt trong đá và các cải tiến luôn được chào đón.

Nhưng tôi nghĩ rất có khả năng bạn đang làm điều gì đó kỳ lạ, như hammar và shang đề xuất.

P.S. đối với trường hợp sử dụng cụ thể của bạn, thay vì lưu trữ nhiều chuỗi nhỏ, bạn nên xem xét cấu trúc dữ liệu thích hợp hơn phù hợp với nhu cầu của mình, ví dụ: một Trie như danr gợi ý.

+4

Phân loại chuỗi _short_ là một nơi mà chuỗi 'Chuỗi' thường hoạt động tốt hơn' ByteString '(Tôi không biết về' Text', nhưng tôi sẽ không ngạc nhiên nếu 'String' đánh bại quá cho nhiệm vụ này). Tại sao điều đó là hiển nhiên: 'ByteString' sử dụng một loại đếm. –

3

A (nghiêm ngặt) ByteSting là một hàm tạo trên một không được mở rộng ForiegnPtr đến một Word8 và hai Ints không được hộp.

Một ForeignPtr là một nhà xây dựng trên một Addr# (một GHC prim) và một ForeignPtrContents:

data ForeignPtrContents 
    = PlainForeignPtr !(IORef (Finalizers, [IO()])) 
    | MallocPtr  (MutableByteArray# RealWorld) !(IORef (Finalizers, [IO()])) 
    | PlainPtr  (MutableByteArray# RealWorld) 

...

Đối với các chuỗi ngắn, ByteStrings chỉ đơn giản bao gồm quá nhiều chính quyền để làm lợi đại diện tiếp giáp của họ của dữ liệu "chuỗi" thực tế.

Đối với câu hỏi gốc - tôi muốn kiểm tra độ dài trung bình của kho văn bản, nhưng tôi không thể thấy ByteString hiệu quả hơn String aka [Char] sử dụng 12 byte trên mỗi Char (nguồn gốc ByteString giấy) .

Lời kêu gọi chung cho người bán hàng (không nhằm vào áp phích của câu hỏi gốc) - hãy dừng bashing String aka [Char] - có cả String và Text (và ByteString khi bạn thực sự cần byte). Hoặc sử dụng Clean nơi biểu diễn String tiếp giáp phù hợp hơn với các chuỗi ngắn.

Lưu ý - Tôi có thể đã xem xét phiên bản cũ của nội bộ ByteString liên quan đến loại dữ liệu nào sử dụng nội bộ.

2

Tôi biết đây là bài đăng cũ 6 năm, nhưng tôi đã tự hỏi tương tự gần đây và thấy bài đăng trên blog hữu ích này: https://markkarpov.com/post/short-bs-and-text.html. Dường như có, đây là một vấn đề được công nhận và Short (Text/ByteString) là giải pháp.

Các vấn đề liên quan