TL; DR:我的反应器吞吐量有限吗?我怎么说? io_service的实现是多么昂贵和可扩展(跨线程)?
我有一个远程大规模并行应用程序,运行在超线程双四核Xeon机器上,具有大量RAM和快速SSD RAID。这是使用boost :: asio开发的。
此应用程序接受来自大约1,000台其他计算机的连接,读取数据,解码简单协议,并将数据混洗到使用mmap()映射的文件中。该应用程序还使用madvise(WILLNEED)预取“未来”mmap页面,因此它不太可能阻止页面错误,但只是为了确定,我已经尝试产生多达300个线程。
这是在Linux内核2.6.32-27-generic(Ubuntu Server x64 LTS 10.04)上运行的。 Gcc版本是4.4.3,boost :: asio版本是1.40(两者都是Ubuntu LTS)。
运行vmstat,iostat和top,我看到磁盘吞吐量(在TPS和数据卷中)都是%的单个数字。同样,磁盘队列长度总是比线程数小很多,所以我不认为我是I / O绑定的。此外,RSS攀升,但然后稳定在几个演出(如预期)和vmstat显示没有分页,所以我想我不是内存限制。 CPU恒定为0-1%用户,6-7%系统,其余为空闲。线索!一个完整的“核心”(记住超线程)是CPU的6.25%。
我知道系统落后了,因为当超过64kB未完成时,客户端机器阻止TCP发送,并报告事实;他们都在不断报告这一事实,系统的吞吐量远低于预期,预期和理论上的可能性。
我的猜测是我正在争夺某种锁定。我使用应用程序级锁来保护可能会发生变异的查找表,因此我将其分为256个顶级锁/表来打破这种依赖。但是,这似乎没有任何帮助。
所有线程都通过一个全局io_service实例。在应用程序上运行strace显示它花费大部分时间来处理futex调用,我想这与io_service reactor的基于事件的实现有关。
我的反应器吞吐量是否有限?我怎么说? io_service的实现是多么昂贵和可扩展(跨线程)?
编辑:我最初没有找到这个其他线程,因为它使用了一组与我不重叠的标签: - /很可能我的问题是在执行boost :: asio reactor时使用了过多的锁定。见C++ Socket Server - Unable to saturate CPU 但问题仍然存在:我如何证明这一点?我该如何解决?答案 0 :(得分:2)
答案确实是即使最新的boost :: asio也只从一个线程调用epoll文件描述符,而不是一次从多个线程进入内核。我可以理解为什么,因为当您使用多个线程时,线程安全性和对象的生命周期非常不稳定,每个线程都可以获得相同文件描述符的通知。当我自己编写代码(使用pthreads)时,它可以工作,并且可以扩展到单个核心之外。此时不使用boost :: asio - 遗憾的是,设计良好且可移植的库应具有此限制。
答案 1 :(得分:2)
我相信如果你使用多个io_service对象(比如每个cpu核心),每个运行一个线程,你就不会有这个问题。请参阅boost ASIO页面上的http服务器示例2.
我已经针对服务器示例2和服务器示例3做了各种基准测试,并且发现我提到的实现效果最好。
答案 2 :(得分:0)
在我的单线程应用程序中,我从分析中发现,大部分处理器指令都花在了io_service :: poll()的锁定和解锁上。我使用BOOST_ASIO_DISABLE_THREADS宏禁用了锁定操作。根据您的穿线情况,它也可能对您有意义。