Pau*_*nes 1 memory performance haskell functional-programming haskell-stack
给定一个由空格分隔的单词输入文件,文件大小约为 64mb:
main :: IO ()
main = do
content <- getData "data_1.txt" -- opens file, reads contents, closes handle,returns
let tokens = words content
mappedData = map (\token -> (token, 1)) tokens
keySet = Set.fromList tokens
intermediateData = map (\key -> (key, map snd (filter (\kv -> fst kv == key) mappedData))) (Set.toList keySet)
final = map (\pair -> (fst pair, foldr (+) 0 (snd pair))) intermediateData
print final
Run Code Online (Sandbox Code Playgroud)
与
content = ""
with open("data_1.txt", "r") as file:
content = file.read()
tokens = content.split()
intermediate_data = []
for token in tokens:
intermediate_data.append((token, 1))
keys = set()
for pair in intermediate_data:
keys.add(pair[0])
grouped_values = []
for key in keys:
values = [y for x, y in intermediate_data if x == key]
grouped_values.append((key, values))
final = []
for elem in grouped_values:
reduced = sum(elem[1])
final.append((elem[0], reduced))
print(final)
Run Code Online (Sandbox Code Playgroud)
Haskell 程序使用 4.1 GB RAM,而 Python 程序使用 1.7 GB。它们都做几乎完全相同的事情,虽然这个例子是 100% 惰性评估,但大多数严格评估基本上根本不会提高使用率。有什么明显的事情表明我做错了吗?
我可以并行化 Haskell 程序或使用一些更高效的数据结构,但似乎存在一个潜在问题,因为 RAM 使用量比 Python 高约 2.5 倍。我想如果我使用更快的编译语言,RAM 使用量会更少。
lef*_*out 10
String是一个字符链接列表。就内存占用而言,这是可怕的(每个字符 \xe2\x89\x88128 位,而 8 位对于英文文本来说就足够了)。这种类型应该只用于需要延迟处理的字符串(具体来说,逐个字符延迟处理),或者字符串太少太短以至于效率不重要(并且您确信它不会)在不寻常的用例中,在运行时不会更多)。ByteString(用于精确的编码控制和二进制数据),或者Text将其用作文本,这似乎是这里的情况。foldr (+)计算总和。使用 simple sum,它内置了更好的严格性,可以提高效率。(这里实际上并不重要。)Set已经涉及将相同的单词合并在一起。如果您切换到相似的Map,您可以利用该合并来累积计数器,因此永远不需要存储所有这些冗余的1- 。尝试这个:
\nimport qualified Data.Text as T\nimport qualified Data.Map as M\n\nmain :: IO ()\nmain = do\n content <- T.readFile "data_1.txt"\n let tokens = T.words content\n mappedData = Map.fromListWith (+)\n $ map (\\token -> (token, 1)) tokens\n final = Map.toList mappedData\n print final\nRun Code Online (Sandbox Code Playgroud)\n...或更短
\n{-# LANGUAGE TupleSections #-}\n\nmain = do\n content <- T.readFile "data_1.txt"\n print . Map.toList . Map.fromListWith (+)\n $ (,1) <$> T.words content\nRun Code Online (Sandbox Code Playgroud)\n实际上使用readFile仍然使这远非理想,因为它Char在引擎盖下使用了旧的 s。在这种情况下,字符列表应该通过/增量垃圾收集来融合,这样它们就不会同时驻留在内存中,只有结果Text会同时驻留在内存中。
您甚至可以更进一步,甚至不阅读全文,而只是懒惰地阅读。这应该会使其内存更加精简。
\nimport qualified Data.ByteString.Lazy as BL\nimport qualified Data.Text.Lazy as TL\nimport qualified Data.Text.Lazy.Encoding as TL\n\nmain = do\n content <- TL.decodeUtf8 <$> BL.readFile "data_1.txt"\n print . Map.toList . Map.fromListWith (+)\n $ (,1) . TL.toStrict <$> TL.words content\nRun Code Online (Sandbox Code Playgroud)\n在此版本中,运行时将能够在大多数其他单词读入内存之前对第一个单词进行垃圾收集,并且只有重复减少的形式需要永久存储。
\n但请注意,惰性 IO 很繁琐,很容易意外地例如在真正完成文件之前关闭文件。如果在实际应用程序中使用它,您应该确保在移交控制权之前对结果列表进行完全评估/在内存中。
\n| 归档时间: |
|
| 查看次数: |
434 次 |
| 最近记录: |