我正在编写一些'类似openvpn'的东西,并认为它将是一个很好的候选人来提高我的Haskell知识.但是,我遇到了相当严重的性能问题.
它的作用:它打开一个TUN设备; 它将自己绑定在UDP端口上,启动2个线程(forkIO,但是由于fdRead而使用-thaded编译).我没有使用过tuntap软件包,而是在Haskell中自己完成了.
线程1:从tun设备读取数据包(fdRead).使用UDP套接字发送它.
线程2:从UDP套接字读取数据包(recv); 将它发送到tun设备(fdWrite)
问题1:在这个配置中,fdRead返回String,我使用了接受String的Network.Socket函数.我在本地系统(一些iptables魔术)上进行了配置,我可以在localhost上运行15MB/s,程序基本上在100%CPU上运行.那很慢.我有什么办法可以改善表现吗?
问题2:我必须在我发送的数据包之前添加一些内容; 但是sendMany网络函数只接受ByteString; 从Fd读取返回String.转换速度很慢.使用TUN设备转换为Handle似乎效果不佳....
问题3:我想在Data.Heap(功能堆)中存储一些信息(我需要使用'takeMin',虽然对于3个项目来说太过分了,但很容易做到:)).所以我创建了一个MVar,并且在每个接收到的数据包上我从MVar中取出了Heap,用新信息更新了Heap并将它放回到init的MVar现在,它只是开始吃大量的内存.可能是因为旧的堆不会很快/经常收集垃圾......?
有没有办法解决这些问题,还是我必须回到C ......?我正在做的应该主要是zerocopy操作 - 我使用错误的库来实现它吗?
==================
我做了什么: - 当放到MVar时,做了:
a `seq` putMVar mvar a
这完全有助于内存泄漏.
改为ByteString; 现在我只使用"读/写"时没有进一步处理就得到42MB/s.C版本大约56MB/s,所以这是可以接受的.
Carl.. 22
字符串很慢.真的,非常非常慢.它是一个单独链接的cons单元列表,每个单元格包含一个unicode字符.将一个写入套接字需要将每个字符转换为字节,将这些字节复制到一个数组中,并将该数组交给系统调用.这听起来像你想做什么?:)
您想要独占使用ByteString.ByteString IO函数实际上尽可能使用零拷贝IO.特别是看看hackage 上的network- bytestring包.它包含所有网络库的版本,这些库经过优化,可以与ByteString一起高效工作.
字符串很慢.真的,非常非常慢.它是一个单独链接的cons单元列表,每个单元格包含一个unicode字符.将一个写入套接字需要将每个字符转换为字节,将这些字节复制到一个数组中,并将该数组交给系统调用.这听起来像你想做什么?:)
您想要独占使用ByteString.ByteString IO函数实际上尽可能使用零拷贝IO.特别是看看hackage 上的network- bytestring包.它包含所有网络库的版本,这些库经过优化,可以与ByteString一起高效工作.
卡尔对你的前两个问题是正确的.关于你的最后一个,考虑使用严格的并发包.
以下是两个示例程序:客户端和服务器.使用GHC 7.0.1和network-2.3,在我的全新双核笔记本电脑(总CPU使用率约为90%)上,我在环回时获得了超过7500 Mbps的速度.我不知道UDP引入了多少开销,但不过这是一个很大的数字.
-------------------- -- Client program -- -------------------- module Main where import qualified Data.ByteString as C import Network.Socket hiding (recv) import Network.Socket.ByteString (recv) import System.IO import Control.Monad main :: IO () main = withSocketsDo $ do devNull <- openFile "/dev/null" WriteMode addrinfos <- getAddrInfo Nothing (Just "localhost") (Just "3000") let serveraddr = head addrinfos sock <- socket (addrFamily serveraddr) Stream defaultProtocol connect sock (addrAddress serveraddr) forever $ do msg <- recv sock (256 * 1024) -- tuning recv size is important! C.hPutStr devNull msg sClose sock -------------------- -- Server program -- -------------------- module Main where -- import Control.Monad (unless) import Network.Socket hiding (recv) import qualified Data.ByteString.Lazy as S import Network.Socket.ByteString.Lazy ( --recv, sendAll) main :: IO () main = withSocketsDo $ do addrinfos <- getAddrInfo (Just (defaultHints {addrFlags = [AI_PASSIVE]})) Nothing (Just "3000") let serveraddr = head addrinfos sock <- socket (addrFamily serveraddr) Stream defaultProtocol bindSocket sock (addrAddress serveraddr) listen sock 1 (conn, _) <- accept sock talk conn sClose conn sClose sock where talk :: Socket -> IO () talk conn = sendAll conn $ S.repeat 7