- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
所以我正在编写一个数据包嗅探应用程序。基本上我希望它嗅探 TCP session ,然后解析它们以查看它们是否是 http,如果是,以及它们是否具有正确的内容类型等,将它们保存为我的硬盘上的文件。
因此,为此,我希望它高效。由于当前的 http 库是基于字符串的,并且我将处理大文件,并且我只需要解析 http 响应,因此我决定在 attoparsec 中推出自己的库。
当我完成我的程序时,我发现当我解析一个包含 wav 文件的 9 meg http 响应时,当我分析它时,它在尝试解析出正文时分配了一大块内存。 http 响应。当我查看 HTTP.prof 时,我看到一些行:
httpBody Main 362 1 0.0 0.0 93.8 99.3 take Data.Attoparsec.Internal 366 1201 0.0 0.0 93.8 99.3 takeWith Data.Attoparsec.Internal 367 3603 0.0 0.0 93.8 99.3 demandInput Data.Attoparsec.Internal 375 293 0.0 0.0 93.8 99.2 prompt Data.Attoparsec.Internal 378 293 0.0 0.0 93.8 99.2 +++ Data.Attoparsec.Internal 380 586 93.8 99.2 93.8 99.2
So as you can see, somewhere within httpbody, take is called 1201 times, causing 500+ (+++) concatenations of bytestrings, which causes an absurd amount of memory allocation.
Here's the code. N is just the content length of the http response, if there is one. If there isn't one it just tries to take everything.
I wanted it to return a lazy bytestring of 1000 or so character bytestrings, but even if I change it to just take n and return a strict bytestring, it still has those allocations in it (and it uses 14 gig of memory).
httpBody n = do
x <- if n > 0
then AC.take n
else AC.takeWhile (\_ -> True)
if B.length x == 0
then return Nothing
else return (Just x)
我正在读一个做combinatorrent的人写的博客,他也遇到了同样的问题,但我从未听说过解决方案。有没有人曾经遇到过这个问题或找到解决方案?
编辑:好吧,我一整天都把这个放在一边,什么也没得到。在研究了这个问题之后,我认为没有办法在不向 attoparsec 添加惰性字节串访问器的情况下做到这一点。我还查看了所有其他库,它们要么缺少字节串,要么缺少其他东西。
所以我找到了一个解决方法。如果你考虑一个 http 请求,它会包含标题、换行符、换行符、正文。由于正文是最后一个,并且解析返回一个元组,其中包含您解析的内容和字节串的剩余内容,因此我可以跳过解析 attoparsec 内的正文,而是直接从剩下的字节串中提取正文。
parseHTTPs bs = if P.length results == 0
then Nothing
else Just results
where results = foldParse(bs, [])
foldParse (bs,rs) = case ACL.parse httpResponse bs of
ACL.Done rest r -> addBody (rest,rs) r
otherwise -> rs
addBody (rest,rs) http = foldParse (rest', rs')
where
contentlength = ((read . BU.toString) (maybe "0" id (hdrContentLength (rspHeaders http))))
rest' = BL.drop contentlength rest
rs' = rs ++ [http { rspBody = body' }]
body'
| contentlength == 0 = Just rest
| BL.length rest == 0 = Nothing
| otherwise = Just (BL.take contentlength rest)
httpResponse = do
(code, desc) <- statusLine
hdrs <- many header
endOfLine
-- body <- httpBody ((read . BU.toString) (maybe "0" id (hdrContentLength parsedHeaders)))
return Response { rspCode = code, rspReason = desc, rspHeaders = parseHeaders hdrs, rspBody = undefined }
它有点困惑,但最终它运行速度很快并且分配的内容不会超出我的需要。所以基本上你折叠了收集http数据结构的字节串,然后在集合之间,我检查刚刚获得的结构的内容长度,从剩余的字节串中提取适当的量,然后如果还有剩余的字节串则继续。
编辑:我实际上完成了这个项目。奇迹般有效。我没有正确地进行阴谋,但如果有人想查看整个源代码,你可以在 https://github.com/onmach/Audio-Sniffer 找到它。 .
最佳答案
这里有combinatorrent 人:)
如果没记错的话,attoparsec 的问题是需要一次输入一点点,构建一个最终连接的惰性字节串。我的“解决方案”是自己滚动输入功能。也就是说,我从网络套接字获取 attoparsec 的输入流,并且我知道消息中需要多少字节。基本上,我分为两种情况:
消息很小:从套接字读取最多 4k 数据,然后一次一点地吃掉该字节串(字节串切片速度很快,我们在耗尽 4k 后将其丢弃)。
消息是“大”的(这里的“大”在 BitTorrent 中意味着大约 16 KB):我们计算我们拥有的 4k block 可以满足多少,然后我们只需请求底层网络套接字来填充内容。我们现在有两个字节串,即 4k block 的剩余部分和大块。他们拥有所有数据,因此我们要做的就是连接这些数据并解析它们。
您也许可以优化串联步骤。
TL;DR 版本:我在 attoparsec 之外处理它并手动滚动循环以避免问题。
相关的combinatorrent提交是fc131fe24,参见
https://github.com/jlouis/combinatorrent/commit/fc131fe24207909dd980c674aae6aaba27b966d4
了解详情。
关于parsing - Attoparsec 在大型 'take' 调用上分配大量内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4151265/
关闭。这个问题需要更多 focused .它目前不接受答案。 想改进这个问题?更新问题,使其仅关注一个问题 editing this post . 3年前关闭。 Improve this questi
我想,只是为了了解一些关于 Iteratees 的知识,使用 Data.Iteratee 和 Data.Attoparsec.Iteratee 重新实现我制作的一个简单的解析器。不过,我很难过。下面我
我被 attoparsec 困住了,我无法返回关于它是“嵌入式类型”的值。 我试图解析一个文件: type value type value ... 例如: 0 -- code for a strin
我正在使用 Attoparsec,并且我想在整个解析任务中跟踪用户状态值。 我熟悉 Parsec 的一元函数 getState、putState 和修改状态,但我似乎无法在 Attoparsec 中找
我正在尝试在第 5 列中使用 JSON 制作适合导入到 mongoDB 的大型 TSV 文件。特别是我想将顶级和仅顶级关键字段更改为 _id。这是我目前所拥有的,它似乎有效但速度很慢: {-# LAN
假设有一个数据结构表示一个文本,里面有评论。 data TWC = T Text TWC -- text | C Text TWC -- comment | E -- end deri
我正在使用据说默认回溯的 Attoparsec。但是,以下行: parseOnly (string "foo" *> many1 anyChar manyTill1 p e = (:) p m
我正在尝试用一个函数来扩充 Haskell 的 Attoparsec 解析器库 takeRegex :: Regex -> Parser ByteString 使用其中一种正则表达式实现。 (动机:好
是否有一些“简单”的方法(例如我在 Attoparsec 或其他一些库中缺少的东西)将定义的 Attoparsec 解析器从 ByteString 解析为从 Text 解析的解析器? 例如我有: im
Attoparsec提供了至少消耗一个字符的函数 takeWhile1。 但是,skipWhile 没有类似的东西。如何实现这个函数skipWhile1? 注意:这个问题故意表现出没有研究工作,因为它
我正在将一些使用 Parsec 的功能 Haskell 代码转换为使用 Attoparsec,希望获得更好的性能。我已经进行了更改并且所有内容都已编译,但我的解析器无法正常工作。 我正在解析一个由各种
我的类型: data Test = Test { a :: Int, b :: Int } deriving (Show) 我的解析器: testParser :: Parser Test tes
背景 我使用 attoparsec 编写了一个日志文件解析器。我所有较小的解析器都成功了,组成的最终解析器也是如此。我已通过 tests 确认了这一点。但我在使用解析的流执行操作时遇到了困难。 我尝试
我一直在编写 attoparsec 解析器,并且一直在寻找一种模式,我想将解析器转换为递归解析器(将它们与 monad 绑定(bind) >>= 运算符递归地组合)。 所以我创建了一个函数将解析器转换
我一直在编写一个 attoparsec 解析器来解析 Uniform Code for Units of Measure 的内容。调用 。它被定义为某个类(该类包括所有数字 0-9)中不以数字结尾的最
Attoparsec提供了至少消耗一个字符的函数 takeWhile1。 但是,skipWhile 没有类似的东西。如何实现这个函数skipWhile1? 注意:这个问题故意表现出没有研究工作,因为它
我正在使用 attoparsec 的内置解析器“double”和“number”来解析浮点值,我从不同的解析器得到不同的结果。 >parse number "8.918605790440055e-2"
attoparsec 0.72 有这个功能,但在后来的版本中似乎消失了: stringTransform :: (ByteString -> ByteString) -> ByteString ->
我有一个简单的基于 attoparsec 的 pdf parser .它工作正常,直到与 iteratee 一起使用。 当输入的大小超过缓冲区大小时。 import qualified Data.By
我使用 attoparsec 编写了以下解析代码: data Test = Test { a :: Int, b :: Int } deriving (Show) testParser :
我是一名优秀的程序员,十分优秀!