【发布时间】:2010-11-19 19:00:33
【问题描述】:
我正在编写一些“类似 openvpn”的东西,并认为它是提高我的 Haskell 知识的好选择。但是,我遇到了相当严重的性能问题。
它的作用:它打开一个 TUN 设备;它将自身绑定在 UDP 端口上,启动 2 个线程(forkIO,但是由于 fdRead 使用 -thread 编译)。我没有使用过 tuntap 包,完全是在 Haskell 中自己完成的。
线程 1:从 tun 设备读取数据包 (fdRead)。使用 UDP 套接字发送。
线程 2:从 UDP 套接字读取数据包(recv);发送到 tun 设备 (fdWrite)
问题 1:在此配置中,fdRead 返回字符串,并且我使用了接受字符串的 Network.Socket 函数。我在本地系统上进行了配置(一些 iptables 魔术),我可以通过它在 localhost 上运行 15MB/s,程序基本上在 100% CPU 上运行。那很慢。我可以做些什么来提高性能吗?
问题 2:我必须在发送的数据包中添加一些内容;但是 sendMany 网络函数只需要 ByteString;从 Fd 读取返回字符串。转换很慢。使用 TUN 设备转换为 Handle 似乎效果不佳....
问题 3:我想在 Data.Heap(功能堆)中存储一些信息(我需要使用“takeMin”,虽然对于 3 个项目来说它是多余的,但很容易做到:))。所以我创建了一个 MVar 并且在每个接收到的数据包上我都从 MVar 中提取了堆,用新信息更新了堆并将其放回 MVar 现在事情开始消耗大量内存。可能是因为旧堆没有足够快/足够频繁地收集垃圾......?
有没有办法解决这些问题,还是我必须回到 C...?我所做的应该主要是零拷贝操作——我是否使用了错误的库来实现它?
===================
我做了什么: - 当放入 MVar 时,做了:
a `seq` putMVar mvar a
这对内存泄漏很有帮助。
- 更改为字节串;现在我只使用“读/写”而不进行进一步处理时得到 42MB/s。 C 版本的速度约为 56MB/s,因此这是可以接受的。
【问题讨论】:
-
介意我问你为什么不使用 tuntap 包吗? (我是维护者......所以我很好奇。)
-
我正在考虑使用“TUN”部分,并认为我会利用 Haskell 的“Handle”部分;事实并非如此。在我看来,我可能最终会使用它并使用 TAP 方式(没有太大区别)——因为它返回的 ByteString 可能会加快速度。
-
我将在 tuntap 包中添加注释;能够使用标准 SockAddr(而不是 Word32)设置 IP/网络掩码将非常受欢迎:)
-
耶!我刚刚看到并发严格将事物评估为正常形式(即 deepseqs),而不是文档声称的“头部正常形式”(即 seqs)。回到惰性 mvar 并自己强制使用 seq 进行评估可能是一个巨大的胜利。 Data.Heap 应该保持惰性以获得正确的摊销性能。
-
我也发现了这一点,尽管我首先是通过“错误地”输入 rnf deepseq 实例来做到这一点的。使用带有 seq 的惰性 mvar 肯定更干净。
标签: performance networking haskell