我正在编写一个类似于OpenVPN的程序,想用它来提高我对Haskell的了解。然而,我遇到了相当严重的性能问题。
它的功能是:打开TUN设备;绑定在一个UDP端口上,启动2个线程(使用forkIO,但由于fdRead,编译时使用了-threaded)。我没有使用tuntap包,在Haskell中完全自己实现了它。
线程1:从TUN设备读取数据包(fdRead)。使用UDP套接字将其发送出去。
线程2:从UDP套接字接收数据包(recv),将其发送到TUN设备(fdWrite)。
问题1:在这种配置中,fdRead返回String类型的数据,我使用了接受String类型的Network.Socket函数。我在本地系统上进行了一些iptables配置(一些魔法),并且可以在localhost上以15MB/s的速度运行该程序,但CPU占用率基本上达到了100%。 这太慢了。有什么方法可以改善性能吗?
问题2:我需要在发送的数据包前面添加一些内容;但是sendMany网络函数只接受ByteString类型的数据,而Fd读取的是String类型的数据。转换非常慢。将其转换为Handle似乎不能很好地与TUN设备配合使用....
问题3:我想在Data.Heap(函数式堆)中存储一些信息(我需要使用'takeMin'),虽然对于3个数据项来说有些过度了,但很容易实现。 因此,我创建了一个MVar,在每个接收到的数据包上,我从MVar中取出了Heap,在其中更新了新信息并将其放回到MVar中。现在程序开始占用大量内存。可能是因为旧堆没有被足够快/频繁地进行垃圾回收......?
是否有方法解决这些问题,或者我必须回到C语言...?我的操作应该主要是零复制操作-我是否使用了错误的库来实现它?
==================
我所做的事情: - 在将数据放入MVar时,执行以下操作:
a `seq` putMVar mvar a
那个完美地解决了内存泄漏问题。
- 改为ByteString后,现在使用“读/写”而没有进一步处理时,我获得了42MB/s的速度。C语言版本大约为56MB/s,因此这是可以接受的。