我正在编写一些“类似openvpn”的东西,并认为这将是提高我的Haskell知识的好人选。但是,我遇到了相当严重的性能问题。
它的作用:它打开一个 TUN 设备;它将自身绑定(bind)在 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
这完全有助于解决内存泄漏。
最佳答案
字符串很慢。真的,真的,真的很慢。它是一个包含一个 unicode 字符的 cons 单元的单链接列表。将一个写入套接字需要将每个字符转换为字节,将这些字节复制到一个数组中,然后将该数组交给系统调用。这听起来像是你想做的事情的哪一部分? :)
您想独占使用 ByteString。 ByteString IO 函数实际上尽可能使用零拷贝 IO。尤其是看network-bytestring hackage 包。它包含所有经过优化以有效使用 ByteString 的网络库的版本。
关于performance - 糟糕的haskell网络性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4228416/