Chinaunix

标题: 线程性能到达瓶颈,并发还是并行? [打印本页]

作者: heguangwu    时间: 2015-12-04 14:50
标题: 线程性能到达瓶颈,并发还是并行?
获奖详情:http://bbs.chinaunix.net/thread-4241378-1-1.html

首先,跟大家分享个陈年趣事:
几年前,我去某软公司应聘,电话面试中与面试官有如下一番对话。 面试官:你了解多线程并发吗?
我:不了解,我之前做业务系统,多线程很大程度上都是委托给容器的……
面试官:我理解了。你不太熟悉并发是吗?
我:是的。
面试官:那我们还是来聊一聊并发吧。
祝大家线程安全。

案列介绍:
      最初是一个单线程程序性能到达瓶颈后,通过将整个作业切成一个个小任务,每一个任务执行一个线程,运行结果发现居然和之前差不多,通过调试发现其中一个任务执行时间过长,再动态调节该任务的线程数,当发现队列过多时启动新线程来同时进行,结果性能上升不大,再定位发现多个线程竞争需要同步访问同一个资源,修改为静态线程池,再由上游哈希将数据放置不同的线程队列中。
      进一步的性能提升发现怎么增大线程数性能都是这样了,再检查发现之前下游任务需要等待上游线程全部完成才能进一步操作,通过查阅资料找到一个算法只要上游有数据就可以开始进行计算,这样到上游全部陆续完成时该步骤也只需要计算少量的数据就可以了。针对此问题您有什么更好的解决方法?


讨论话题:(可任选一个或几个)
1. 阐述一下你设计过的最满意的并发/并行软件架构。
2. 详细描述一下在多线程/进程/协程方式下遇到过的最难解决的问题以及如何解决的
3. 详细讲述一下曾经使用过的最好的并发/并行组件
4. 对并行/并发的某一个理论进行详细的说明


讨论时间:2015年12月15日—2016年1月15日


奖励设置:
活动结束后,我们将选取5位讨论精彩的同学,各送技术图书《七周七并发模型》一本。



作者: (美)Paul Butcher   
译者: 黄炎
丛书名: 图灵程序设计丛书
出版社:人民邮电出版社
ISBN:9787115386069
上架时间:2015-3-13
出版日期:2015 年4月
开本:16开
页码:234
版次:1-1

内容简介:并发编程近年逐渐热起来,Go等并发语言也对并发编程提供了良好的支持,使得并发这个话题受到越来越多人的关注。本书延续了《七周七语言》的写作风格,通过以下七个精选的模型帮助读者了解并发领域的轮廓:线程与锁,函数式编程,Clojure,actor,通信顺序进程,数据级并行,Lambda架构。书中每一章都设计成三天的阅读量。每天阅读结束都会有相关练习,巩固并扩展当天的知识。每一章均有复习,用于概括本章模型的优点和缺陷。


样章试读: 第1章 概述.pdf (1.11 MB, 下载次数: 49)

---------------------------------------------------------分割线---------------------------------------------------------------------
其实案例我想描述的更清楚一些,因为当时没有时间,现在补充如下:
软件功能描述,通过libpcap抓取Mysql的包,并按照Mysql协议分解后将数据发送到Kafka供后续的分析实现,软件架构如下:
pcapThread---->TcpProcThread----->MysqlProcThread----->KafkaSendThread
各线程作用比较清晰,pcap线程调用libpcap的接口获取抓包,并根据配置排除掉不属于本机或黑名单中的抓包,并将TCP/IP头域将解开有用的放到一个数据结构中
TcpProc线程负责TCP的排序,包括乱序重传等处理,将有效的消息发送到Mysql线程处理,MySQL线程根据MySQL协议提取SQL语句和结果元数据信息组成json格式并调用rdkafka库发送
之前开发为简便实现,各线程之间通信采用出入队列方式,同步采用互斥锁,在QPS比较小的机器上测试正常,但上线到QPS比较大机器上,pcapThread出现丢包且比较严重
同时CPU飚的很高,直接占据单个核的100%,当前通过一些手段,如锁改为条件变量、使用无锁队列等,CPU下降到30%,丢包从30%以上下降到10%以下,但优化还未完成,如果大家有兴趣也可以直接根据这个现实的案例来思考发散。



作者: chenxing2    时间: 2015-12-17 09:23
这个好像很难,没人回了......
作者: demilich    时间: 2015-12-17 11:38
才看到,居然没有人回答 ... 支持一下,期待大牛回答
作者: sjf0115    时间: 2015-12-17 11:58

主要是没用到过
作者: heguangwu    时间: 2015-12-17 13:52
其实案例我想描述的更清楚一些,因为当时没有时间,现在补充如下:
软件功能描述,通过libpcap抓取Mysql的包,并按照Mysql协议分解后将数据发送到Kafka供后续的分析实现,软件架构如下:
pcapThread---->TcpProcThread----->MysqlProcThread----->KafkaSendThread
各线程作用比较清晰,pcap线程调用libpcap的接口获取抓包,并根据配置排除掉不属于本机或黑名单中的抓包,并将TCP/IP头域将解开有用的放到一个数据结构中
TcpProc线程负责TCP的排序,包括乱序重传等处理,将有效的消息发送到Mysql线程处理,MySQL线程根据MySQL协议提取SQL语句和结果元数据信息组成json格式并调用rdkafka库发送
之前开发为简便实现,各线程之间通信采用出入队列方式,同步采用互斥锁,在QPS比较小的机器上测试正常,但上线到QPS比较大机器上,pcapThread出现丢包且比较严重
同时CPU飚的很高,直接占据单个核的100%,当前通过一些手段,如锁改为条件变量、使用无锁队列等,CPU下降到30%,丢包从30%以上下降到10%以下,但优化还未完成
后续做完后有时间总结一下发到这里更合适一些
作者: 王楠w_n    时间: 2015-12-17 14:20
大牛在下面回复了,可以给大家普及写各位不懂的领域,希望大家能得到更多回复 4# sjf0115


   
作者: sjf0115    时间: 2015-12-17 20:46
回复 6# 王楠w_n


    恩  就是希望在这个平台学到自己感兴趣的知识
作者: laputa73    时间: 2015-12-17 21:04
本帖最后由 laputa73 于 2015-12-17 21:08 编辑

一直觉得线程狠复杂,性能也狠差。
基于线程模型的java服务器,跑到500并发已经狠了不起了。
基于事件的就可以轻松上万并发。但是回调写起来让头大。
最早接触协程是通过erlang.这个对后续的影响很大。
但是erlang的学习门槛确实比较高。
后来又陆续看了lua的协程和perl的coro,python的stackless和gevent.
感觉协程确实是个好东西,高大上,但是组件级别的支持还是比较别扭。
golang一出,语言级支持协程并发,而且还同时支持多核并行。
没有什么可犹豫的,直接拥抱go吧。
java的同学,估计转scala比较容易。
作者: chenxing2    时间: 2015-12-18 12:56
回复 5# heguangwu


看描述,pcapThread用来抓包,然后还处理。当QPS高的时候,处理慢了,丢包正常。

建议 抓包和处理数据分开。

pcapThread抓包后,直接发到kafka,然后多个消费者取包,然后处理,再继续后面的处理,根据量还可增加消费者

如:

宝突然增加的时候,kafka也可以积压存储,保证丢包率比较低
pcapThread(抓包)  --> kafka   --> 增加一个程序(根据配置排除掉不属于本机或黑名单中的抓包,并将TCP/IP头域将解开有用的放到一个数据结构中) --> kafka --> TcpProcThread --> kafka -->MysqlProcThread -->KafkaSendThread
作者: heguangwu    时间: 2015-12-18 13:32

pcapThread并没有做业务处理,只是做了一些及其简单的过滤,如去掉了长度为0的包,这个不是丢包的原因,因为哪怕我直接将数据放入下一个线程的队列同样会丢包
回复 9# chenxing2


   
作者: chenxing2    时间: 2015-12-19 14:52
回复 10# heguangwu

这样看就是 pcapThread 抓包的速度跟不上了。

LibPcap这玩意记得在资源不足以再容纳下一段数据时,会丢弃数据。

建个线程池,在loop的时候,拿到数据直接扔到线程池然后再后面的处理,每次也多收些包


   
作者: heguangwu    时间: 2015-12-19 19:27
我们的问题其实已经解决了,通过调整pcap的参数和进程优先级再加上之前的由互斥锁修改为条件变量、锁的同步范围等方法,现在已经丢包很少了

回复 11# chenxing2


   
作者: 文峰聊书斋    时间: 2015-12-22 09:16
e_zpoll的方式呢?
作者: 流氓无产者    时间: 2015-12-22 09:28
这个有点儿困难吧,需要实际操作先找到瓶颈,再对症下药
作者: 奇怪的蜀黍    时间: 2015-12-22 14:19
“ 最初是一个单线程程序性能到达瓶颈后,通过将整个作业切成一个个小任务,每一个任务执行一个线程,运行结果发现居然和之前差不多”
采用多线程,很多时候是为了防止在某个部分响应时间过长,影响其它的操作。
如果你的程序性能达到了瓶颈,仅仅想依靠多个线程分担来提升性能。这样的想法是不可行的。
作者: yulihua49    时间: 2015-12-22 14:19
本帖最后由 yulihua49 于 2015-12-22 14:45 编辑
heguangwu 发表于 2015-12-04 14:50
首先,跟大家分享个陈年趣事:
几年前,我去某软公司应聘,电话面试中与面试官有如下一番对话。 面试官:你 ...


我不知道你说的并发还是并行。从另一个角度说:

你这是想搞并发算法。每种业务不同,设计专门的算法,一是很费力,二是达不到充分运用核,三是不通用,没有普适价值。
现在更多的做法,算法是串行的。把数据分成多组,分发到多台服务器,由多个线程并行处理。
系统由任务分发器(数据分组,负载均衡)------>  多台应用服务器(每台多线程)<------> 数据库(ORACLE+RAC)
这是一个可伸缩结构。如果计算密集型,把应用服务器多配些。如果IO密集型,把数据库配强大些。
如果是网络密集型(数据传输量特大),我们在分发--->服务环节采用压缩/解压缩,把网络瓶颈转化为计算瓶颈,多核多线程并行处理化解之。
对于服务器里的线程来说,所有的线程做所有的事,就是对称多处理,这样负载很容易均衡的。

总结下:
算法串行,处理分段并行,系统可伸缩(每个段的资源可伸缩)。

这本来是很成熟的技术,使用交易中间件进行任务分发,完成map-reduce功能。
作者: demilich    时间: 2015-12-22 14:20
回复 5# heguangwu


    一般来讲使用多线程要依赖于做的操作室计算密集型还是I/O密集型。根据描述,pcapThread似乎是有读写操作,而TcpProcThread/MysqlProcThread其实都是编解码,不牵涉阻塞操作,如果是这样为什么不把他们两个线程合并到一起呢?还是说,每个线程已经绑定到某个cpu核上了?
作者: demilich    时间: 2015-12-22 14:39
本帖最后由 demilich 于 2015-12-22 14:40 编辑

抛砖引玉一下,尤其如何使用多线程,什么时候用,怎么用,还希望高手指教。

1. 阐述一下你设计过的最满意的并发/并行软件架构。
线程池+多个FIFO队列,这种类型应用范围最广

2. 详细描述一下在多线程/进程/协程方式下遇到过的最难解决的问题以及如何解决的
看似很莫名其妙的问题,最后解决的时候,会发现一般不外乎函数线程不安全/死锁/锁的太大导致性能差

3. 详细讲述一下曾经使用过的最好的并发/并行组件
尝试过TBB和openMP,谈不上更多的经验

4. 对并行/并发的某一个理论进行详细的说明
我的理解:是否要用并发,最简单还是要看应用类型,如果仅仅是单核环境的无阻塞计算密集型的任务,无需并发多线程,光是计算就可以把CPU全部占满;如果CPU是多核,此时是要用多线程的,这样可以调动更多的CPU参与运算。再有就是有阻塞的任务,此时才是并发的用武之地,无论单核多核,多线程都可以提高程序的效率,把CPU充分利用上,因此如何识别阻塞就比较重要了:常见的就是I/O和UI操作 ...
作者: hellioncu    时间: 2015-12-22 16:56
就你那案例,我觉得线程分太多了,TcpProc和MysqlProc都是些内存运算,应该合到一起。我不知道“rdkafka库”,如果其内部有队列机制,可以异步,那么也不需要这个单独的发送线程。
作者: heguangwu    时间: 2015-12-23 10:32
回复 20# hellioncu
你说的很对,rdkafka本身并不需要一个线程,只是当时考虑是为了控制发送流量避免影响在线应用而做的


   
作者: heguangwu    时间: 2015-12-23 10:34
能详细说说线程池+多个FIFO队列的要点吗,另外TBB是一个很大的并行库,能挑出一到两个来具体说明吗
回复 19# demilich


   
作者: 等一等    时间: 2015-12-24 22:59
新人正学习中的
作者: 等一等    时间: 2015-12-24 23:00
新人正学习中的

作者: hjiayz    时间: 2015-12-26 04:04
并发和并行有各自的适用环境.
并发基于系统事件,开销比较小,回调甚至不需要进入事件循环.所以处理读写密集型任务非常出色.
并行有多种方案.
多进程,由于系统内部的隔离机制,稳定性更好,但是进程间通信效率不高.
多线程,由于拥有共享内存,线程间通信的效能不错,但是由此带来的同步问题也很复杂.
由于多线程的开销不小,线程池是必要的.并行的主要目的还是处理计算密集型任务.

至于楼主的案例,建一个线程池,主线程接受消息并往线程池丢数据,池中的工作线程处理任务是很常见的方案.
作者: linux_c_py_php    时间: 2015-12-29 13:50
这个就是C/C++服务很正常设计。

程序内部按模块划分,通过接口交换数据。

有的模块内部是线程池并发,通过对数据哈希或者轮转的方式分发到多个线程里计算。 而且为了降低锁带来的性能瓶颈,一般尽量避免多线程争夺相同的资源,尽量通过大拆小的思路提高并发计算能力。
作者: 王楠w_n    时间: 2015-12-29 17:55

作者: mordorwww    时间: 2016-06-23 09:11
heguangwu 发表于 2015-12-17 13:52
其实案例我想描述的更清楚一些,因为当时没有时间,现在补充如下:
软件功能描述,通过libpcap抓取Mysql的 ...


你为什么要搞这么多线程,是什么目的?有什么好处?
如果是多核多vcpu的话,一个vcpu一到两个线程就够了,最好是一个线程,如果不需要并发的话
作者: karma303    时间: 2016-08-10 10:59
不懂帮顶~~




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2