- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我编写了一个文件索引程序,该程序应读取数千个文本文件行作为记录,最后通过指纹将这些记录分组。它使用Data.List.Split.splitOn
在选项卡上拆分行并检索记录字段。该程序消耗10-20 GB的内存。
可能没有太多的方法可以减少如此大的内存占用,但是我无法解释为什么像splitOn
(breakDelim
)这样的函数会消耗那么多内存:
Mon Dec 9 21:07 2019 Time and Allocation Profiling Report (Final)
group +RTS -p -RTS file1 file2 -o 2 -h
total time = 7.40 secs (7399 ticks @ 1000 us, 1 processor)
total alloc = 14,324,828,696 bytes (excludes profiling overheads)
COST CENTRE MODULE SRC %time %alloc
fileToPairs.linesIncludingEmptyLines ImageFileRecordParser ImageFileRecordParser.hs:35:7-47 25.0 33.8
breakDelim Data.List.Split.Internals src/Data/List/Split/Internals.hs:(151,1)-(156,36) 24.9 39.3
sortAndGroup Aggregations Aggregations.hs:6:1-85 12.9 1.7
fileToPairs ImageFileRecordParser ImageFileRecordParser.hs:(33,1)-(42,14) 8.2 10.7
matchDelim Data.List.Split.Internals src/Data/List/Split/Internals.hs:(73,1)-(77,23) 7.4 0.4
onSublist Data.List.Split.Internals src/Data/List/Split/Internals.hs:278:1-72 3.6 0.0
toHashesView ImageFileRecordStatistics ImageFileRecordStatistics.hs:(48,1)-(51,24) 3.0 6.3
main Main group.hs:(47,1)-(89,54) 2.9 0.4
numberOfUnique ImageFileRecord ImageFileRecord.hs:37:1-40 1.6 0.1
toHashesView.sortedLines ImageFileRecordStatistics ImageFileRecordStatistics.hs:50:7-30 1.4 0.1
imageFileRecordFromFields ImageFileRecordParser ImageFileRecordParser.hs:(11,1)-(30,5) 1.1 0.3
toHashView ImageFileRecord ImageFileRecord.hs:(67,1)-(69,23) 0.7 1.7
[Char]
类型的内存效率太低(与
Text
相比),导致
splitOn
占用了那么多内存?
+RTS -s
建议)
23,446,268,504 bytes allocated in the heap
10,753,363,408 bytes copied during GC
1,456,588,656 bytes maximum residency (22 sample(s))
29,282,936 bytes maximum slop
3620 MB total memory in use (0 MB lost due to fragmentation)
Tot time (elapsed) Avg pause Max pause
Gen 0 45646 colls, 0 par 4.055s 4.059s 0.0001s 0.0013s
Gen 1 22 colls, 0 par 4.034s 4.035s 0.1834s 1.1491s
INIT time 0.000s ( 0.000s elapsed)
MUT time 7.477s ( 7.475s elapsed)
GC time 8.089s ( 8.094s elapsed)
RP time 0.000s ( 0.000s elapsed)
PROF time 0.000s ( 0.000s elapsed)
EXIT time 0.114s ( 0.114s elapsed)
Total time 15.687s ( 15.683s elapsed)
%GC time 51.6% (51.6% elapsed)
Alloc rate 3,135,625,407 bytes per MUT second
Productivity 48.4% of total user, 48.4% of total elapsed
fileToPairs
处理一个文本文件。它返回键值对的列表(键:记录的指纹,值:记录)。
sortAndGroup associations = Map.fromListWith (++) [(k, [v]) | (k, v) <- associations]
main = do
CommandLineArguments{..} <- cmdArgs $ CommandLineArguments {
ignored_paths_file = def &= typFile,
files = def &= typ "FILES" &= args,
number_of_occurrences = def &= name "o",
minimum_number_of_occurrences = def &= name "l",
maximum_number_of_occurrences = def &= name "u",
number_of_hashes = def &= name "n",
having_record_errors = def &= name "e",
hashes = def
}
&= summary "Group image/video files"
&= program "group"
let ignoredPathsFilenameMaybe = ignored_paths_file
let filenames = files
let hashesMaybe = hashes
ignoredPaths <- case ignoredPathsFilenameMaybe of
Just ignoredPathsFilename -> ioToLines (readFile ignoredPathsFilename)
_ -> return []
recordPairs <- mapM (fileToPairs ignoredPaths) filenames
let allRecordPairs = concat recordPairs
let groupMap = sortAndGroup allRecordPairs
let statisticsPairs = map toPair (Map.toList groupMap) where toPair item = (fst item, imageFileRecordStatisticsFromRecords . snd $ item)
let filterArguments = FilterArguments {
numberOfOccurrencesMaybe = number_of_occurrences,
minimumNumberOfOccurrencesMaybe = minimum_number_of_occurrences,
maximumNumberOfOccurrencesMaybe = maximum_number_of_occurrences,
numberOfHashesMaybe = number_of_hashes,
havingRecordErrorsMaybe = having_record_errors
}
let filteredPairs = filterImageRecords filterArguments statisticsPairs
let filteredMap = Map.fromList filteredPairs
case hashesMaybe of
Just True -> mapM_ putStrLn (map toHashesView (map snd filteredPairs))
_ -> Char8.putStrLn (encodePretty filteredMap)
最佳答案
如您所知,这里确实没有足够的信息可帮助我们提高程序效率。为此,可能值得在Code Review网站上发布一些(完整的,独立的)代码。
但是,我想我可以回答有关splitOn
为什么分配这么多内存的特定问题。实际上,关于splitOn
或其实现方式没有什么特别的。许多简单的Haskell函数将分配大量内存,但这本身并不表示它们编写得不好或运行效率低下。特别地,splitOn
的内存使用情况类似于其他基于定界符拆分字符串的简单方法。
首先要了解的是,GHC编译代码的工作方式与您可能看到的其他编译代码不同。如果您了解很多C知识并且了解堆栈框架和堆分配,或者您已经研究了一些JVM实现,则可以合理地期望其中的某些理解会转换为GHC可执行文件,但是您大都是错误的。
GHC程序或多或少是分配堆对象的引擎,并且-确实有一些例外(仅有少数例外)。几乎每个传递给函数或构造函数的参数(以及构造函数应用程序本身)都会分配至少16个字节(通常更多)的堆对象。采取一个简单的功能,如:
fact :: Int -> Int
fact 0 = 1
fact n = n * fact (n-1)
-O0 -ddump-stg
输出简化):
fact = \n -> case n of I# n' -> case n' of
0# -> I# 1#
_ -> let sat1 = let sat2 = let one = I#! 1# in n-one
in fact sat2;
in n*sat1
let
,这是堆分配(16个以上的字节),并且在
(-)
和
(*)
调用中可能还隐藏着更多内容。使用以下命令编译和运行该程序:
main = print $ fact 1000000
113,343,544 bytes allocated in the heap
44,309,000 bytes copied during GC
25,059,648 bytes maximum residency (5 sample(s))
29,152 bytes maximum slop
23 MB total memory in use (0 MB lost due to fragmentation)
+RTS -s
输出中最重要的真实效率指标可能是底部的“生产率”比率-它是程序花费在垃圾收集上的时间。而且,诚然,您程序的48%的生产率非常糟糕,从技术上来说,这可能意味着它分配了过多的内存,但可能仅分配了其应有数量的两倍或三倍,因此,也许应该“仅”为此工作负载分配大约7-8 Gig而不是23 Gig(因此,运行时间大约为5秒而不是15秒)。
breakDelim
实现:
breakDelim :: String -> [String]
breakDelim str = case break (=='\t') str of
(a,_:b) -> a : breakDelim b
(a,[]) -> [a]
main = interact (unlines . map (intercalate "," . breakDelim) . lines)
17,227,289,776 bytes allocated in the heap
2,807,297,584 bytes copied during GC
127,416 bytes maximum residency (2391 sample(s))
32,608 bytes maximum slop
0 MB total memory in use (0 MB lost due to fragmentation)
breakDelim
承担了很多责任:
COST CENTRE MODULE SRC %time %alloc
main Main Delim.hs:8:1-71 57.7 72.6
breakDelim Main Delim.hs:(4,1)-(6,16) 42.3 27.4
-O2
编译没有太大区别。关键效率指标生产率只有46%。所有这些结果似乎都与您在程序中看到的一致。
split
包有很多用处,但是仔细看一下代码,很明显,它几乎没有做出任何努力来使其变得特别有效或快速,因此
splitOn
的性能不比我的快速和肮脏的自定义更好就不足为奇了
breakDelim
函数。而且,正如我之前说过的,
splitOn
没什么特别的,它使它异常地占用了内存-我简单的
breakDelim
具有类似的行为。
String
类型的低效率,通常可能会出现问题。但是,它也可以以
Text
无法实现的方式参与优化(如列表融合)。上面的实用程序可以用以下更简单的形式重写:
main = interact $ map (\c -> if c == '\t' then ',' else c)
String
,但运行速度非常快(大约是天真的C
getchar
/
putchar
实现的四分之一),生产率为84%,同时在堆上分配了约5 Gig。
Text
”,很可能会发现它比原始程序更慢,而且占用更多内存!尽管
Text
可能比
String
效率更高,但它是一个复杂的程序包,而且
Text
对象在切片和切块后相对于分配的行为方式(例如,将大
Text
文件切成小段时)小小的
Text
字段)使正确设置变得更加困难。
split
包为所有形式的列表拆分操作提供了一个灵活的框架,但是考虑到它的设计速度,它可能不是处理制表符分隔文件的最佳方法。 String
数据类型可能会导致严重的低效率,但并不总是效率低下,而且Text
是一个复杂的程序包,不会作为插件替代来解决您的String
性能问题。 Text
相对于String
的理论优势在很大程度上是无关紧要的。 关于haskell - splitOn的内存占用量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59256312/
我的 DateTime 对象使用 DateTime.Now 分配了本地时间。我想知道一旦夏令时开始/结束,这个对象是否会给出正确的当前本地时间。或者我需要解决方法吗? 最佳答案 是的,DateTime
假设我需要“特定类别中可用的项目数量”与“所有项目的数量”的比率。请考虑这样的 MySQL 表: /* mysql> select * from Item; +----+------------+--
我有这张 table http://codepen.io/MetCastle/pen/lxceL我想使用 jQuery 根据 input type="number" 隐藏/显示列。表示整个列: Pro
想要制作一个看起来像这样的网格,其中 div/section 以百分比表示。 margin 在任何地方都是一样的。 http://www.ladda-upp.se/bilder/giefekcmgwm
这将返回 1(又名 TRUE) SELECT DATE_SUB(NOW(), INTERVAL 24*100 HOUR) = DATE_SUB(NOW(), INTERVAL 100 DAY); 10
我一直在尝试在 UIScrollView 中获取 UIView 的转换后的 CGRect。如果我不放大它就可以正常工作,但是一旦我放大,新的 CGRect 就会发生变化。这是让我接近的代码: CGFl
对于家庭作业,我需要在不使用内置模 (%) 运算符的情况下返回 num1 除以 num2 后的余数。我能够通过以下代码让大多数测试通过,但我仍然坚持如何解释给定数字的 -/+ 符号。我需要保留 num
我用 Javascript 创建了一个倒数计时器;它是成功的,期望未完成。事实上,从数学上讲,它是正确的,但是谷歌浏览器的浏览器设置“暂停”(因为没有更好的术语)SetInterval/Timeout
我有两个 的,每个都设置为其容器宽度的 45%。有没有办法使 居中?使得它们在容器的左右两侧有相同的空间,并且它们之间也有空间。 一开始我只是做了每个 50% 并且有 padding: 0px 2
我是一名优秀的程序员,十分优秀!