c++异网高效发送数据

原创
2022/08/22 19:08
阅读数 10

原文
strand,锁+发送队列和提交+发送队列,哪种方法更好呢?性能说话.答案是提交+发送队列>锁+发送队列>strand.
提交方法连续发送数据的另一个好处是,内部io线程需要连续发送数据时是无锁的,只有提交那里有锁,锁的范围很小,同时也影响io线程发数据的效率,它的效率无疑是最高的.
另一个场景,需要发送一段数据到服务端,数据包括包头和包体,包头中有表示包体长度的长度字段,服务端先读包头解析出包体长度后再读包体.客户端这样发送数据的:

空 发送(串 负载){
    向量<>缓冲;
    大小型 大小=负载.大小();
    缓冲.调整(大小+4);

    复制内存(缓冲.数据(),&大小,4);//包头
    复制内存(缓冲.数据()+4,负载.数据(),大小);
//包体.

    异步写(异网::缓冲(缓冲));//发送.
}

代码很简单,先构造完整长度的缓冲,然后拷贝包头,再拷贝包体,最后发送.这里有个问题就是有一内存分配(一次是负载,一次是缓冲)和两次内存拷贝,性能较低.如何优化性能呢?可用零拷贝来发送数据.

0拷贝

代码变成:

空 发送(串 负载){
    向量<异网::常缓冲>缓冲;

    大小型 大小=负载.大小();符 头[4];
    复制内存(,&大小,4);
    缓冲.压后(异网::缓冲(,4));
    缓冲.压后(异网::缓冲(负载.数据(),负载.大小()));
    异步写(缓冲);
}

这里代码没有分配内存,只有一次memcpy(head),效率比之前高很多,这里省掉了复制负载,称为零拷贝方式发送数据,或者称为分散-聚集方式,asio提供了发送std::vectorasio::常缓冲的接口,使用它可实现零拷贝方式发送数据.
零拷贝底层用writev来一次发送多个缓冲.

虽然零拷贝避免了额外分配和拷贝内存,但是一次发送多个缓冲的效率并没有一次发送大缓冲效率高,所以要看具体情况,如果是小数据,分配和拷贝内存代价较小反而比零拷贝方式更高效.

再改进

避免多次分配内存和发送多个缓冲:

空 发送(串 负载){
    向量<异网::常缓冲>缓冲;

    大小型 偏移=4,大小=负载.大小()-偏移;
    复制内存(负载.数据(),&大小,4);
    异步写(异网::缓冲(负载.数据(),负载.大小()));
}

这里在构造负载时候预留出包头部分长度,保证后面发送时不会再复制负载,只复制包头很少的数据,同时一次发送负载,效率更高.

本文同步分享在 博客“fqbqrr”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部