免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 20790 | 回复: 89
打印 上一主题 下一主题

多线程OR多进程-访存密集型应用 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-12-21 11:58 |只看该作者 |倒序浏览
                   Linux下多线程和多进程的优劣我就不讲了,那些都是老生常谈的东西,最近项目做一个流数据统计引擎(有点类似数据库的统计count(*)等)。项目是用多线程实现的。
具体的场景是这样的, 一组线程从socket接受流数据,经过简单处理把数据送到处理线程,让处理线程对流数据进行处理(我们项目把每一类流数据具体为一类event),然后等到一定的时间把统计结果写文件。

                  处理的场景如下:所有的处理单元用无锁的环形缓冲区串在一起,形成一条流水线,每一个处理单元是一个线程,这样的好处是流数据是所有处理单元共享的,可以节约内存,然后流数据的指针在不同的处理单元串行流动(这样可以避免用到锁)。

                这是一个典型的场景,数据的指针在流动,然后每个线程查看数据(不会修改),然后更新统计结果,所有的程序没有用到锁,按照道理来说,这样的模型是比较好的。但是实际情况是一个处理单元的处理能力是3W个Event/s,10个处理单元形成流水线以后,处理能力只能达到3000个Event/s。这样的结果让我大跌眼镜。
               
               尝试过的解决方案:一开始以为操作系统的内存数据结构同步会影响流水线的并行性,后面尝试在每个线程上面加一个线程池,这样每个线程的内存分配和释放都是线程自己管理,甚至连STL也没有使用,内存完全是自己控制的,但是没有丝毫效果。
                                        后面尝试用多进程,每个处理单元都是一个进程,进程间用管道串起来,数据在管道间传输,这样的话,速度能够基本上保持线性增加(2U 4C 超线程的服务器)1个处理单元式3W 10个处理单元大概有2.5W/处理单元 也就是总共25W的吞吐。
                                 
                  这里我看出多线程还有些什么地方有问题,访存得问题还是缓存命中的问题,现在也不得而知,不知道这种模型能不能够改进,毕竟多线程能够共享数据,所以可以共享原始的流数据,可以节约很多内存,如果改进线程模型可以增加线性不知道又没有高手能够解答一下。
                 
              我在网上找了很多资料,版主 johnbull 曾今说过这个问题,他说多线程共享数据,会造成太多的缓冲会写(wirte-back),所以导致内存子系统的串行化访问,我感觉我的程序问题就在这里,不过我不懂的是,多线程共享数据,但是这个数据是只读的(没有线程回去修改),所以我的理解是数据如果不是dirty的 那么不用wirte-back吧。。。。。。。。
            
               不知道大家有没有关注访存密集型应用,特别是SMP的架构下,内存的访问是最大的瓶颈吧。期待大家的解答。。
                  

论坛徽章:
11
未羊
日期:2013-12-16 12:45:4615-16赛季CBA联赛之青岛
日期:2016-04-11 19:17:4715-16赛季CBA联赛之广夏
日期:2016-04-06 16:34:012015亚冠之卡尔希纳萨夫
日期:2015-11-10 10:04:522015亚冠之大阪钢巴
日期:2015-07-30 18:29:402015亚冠之城南
日期:2015-06-15 17:56:392015亚冠之卡尔希纳萨夫
日期:2015-05-15 15:19:272015亚冠之山东鲁能
日期:2015-05-14 12:38:13金牛座
日期:2014-12-04 15:34:06子鼠
日期:2014-10-16 13:40:4715-16赛季CBA联赛之八一
日期:2016-07-22 09:41:40
2 [报告]
发表于 2011-12-21 12:07 |只看该作者
没彻底搞清楚到底怎么个模型, 可否帖个大概代码出来?

论坛徽章:
0
3 [报告]
发表于 2011-12-21 12:25 |只看该作者
不能确定是我提到的原因。
我怀疑是你使用“无锁”算法造成的,无锁虽然避免了互斥开销,但你可能因此而不得不采用忙等方式操作,搞不好得不偿失。

论坛徽章:
0
4 [报告]
发表于 2011-12-21 12:35 |只看该作者
oprofile ,先找问题原因吧,

论坛徽章:
0
5 [报告]
发表于 2011-12-21 14:26 |只看该作者
代码太多,真帖不了

论坛徽章:
0
6 [报告]
发表于 2011-12-21 14:28 |只看该作者
确实是用了忙等,不过一个线程也是忙等。多个线程一起忙等,对程序有影响么,而且机器的线程数是32线程的,10个线程应该可以同时运行吧。

论坛徽章:
0
7 [报告]
发表于 2011-12-21 14:32 |只看该作者
确实正在用oprofile分析  不过那个工具网上介绍都比较浅,正在摸索

论坛徽章:
0
8 [报告]
发表于 2011-12-21 14:36 |只看该作者
hy036630 发表于 2011-12-21 14:28
确实是用了忙等,不过一个线程也是忙等。多个线程一起忙等,对程序有影响么,而且机器的线程数是32线程的, ...

忙等就是空转,空转就是 “誓把时间片吃尽,我不干事别人也别想干”...
性能还能好?老老实实加上锁再试试

google下“老板-雇员”算法。

论坛徽章:
0
9 [报告]
发表于 2011-12-21 14:58 |只看该作者
不好意思 可能我上面没有说清楚
我用的是下面这个环形队列  无锁的实现一个进程读,一个进程写,没有牵涉到其他线程。
这样的方式把10个线程串成一条流水线,主线程把指针push到第一个线程的环形队列,第一个线程从环形队列里面pop出来一个数据(指针),然后处理(不会修改这个指针指向的值),然后把这个指针插入到下一个线程的队列,下一个线程也从自己的队列里面pop一个数据,然后处理,再push到下下个线程的队列。
所以只有一个线程的队列满了,push不进去会循环的push,知道能够push进去,或者是队列空了,pop出来的数据null,会继续pop,这两种情况下会忙等,不然是不会忙等的,而这两种情况在测试的时候很少发生,对性能影响应该很小。
  1. template<class T>
  2. class RingQueue {
  3. public:

  4.     RingQueue() {
  5.         cout << "RingQueue::ctor" << endl;
  6.     };

  7.     virtual ~RingQueue() {
  8.         cout << "RingQueue::destory" << endl;
  9.     };

  10.     void InitQueue() {
  11.         Push_Count = 0;
  12.         Push_Index = 0;
  13.         Pop_Count = 0;
  14.         Pop_Index = 0;
  15.         memset(List, 0, sizeof (List));
  16.     }

  17.     bool Push(const T& AData) {
  18.         if (Push_Count - Pop_Count < Max_Count) {
  19.             List[Push_Index] = AData;
  20.             Push_Count++;
  21.             if (Push_Index == High_Index)
  22.                 Push_Index = 0;
  23.             else
  24.                 Push_Index++;
  25.             return true;
  26.         } else
  27.             return false;
  28.     }
  29.     T Pop() {
  30.         T result = NULL;
  31.         if (Push_Count != Pop_Count) {
  32.             result = List[Pop_Index];
  33.             Pop_Count++;
  34.             if (Pop_Index == High_Index)
  35.                 Pop_Index = 0;
  36.             else
  37.                 Pop_Index++;
  38.         }
  39.         return result;
  40.     }

  41.     long size() {
  42.         return this->Push_Count - this->Pop_Count;
  43.     }

  44. private:
  45.     RingQueue(const RingQueue& orig);
  46.     T List[524288 * 2]; //2^19  4M�ռ�
  47.     const static unsigned long Max_Count = 524288 * 2;
  48.     const static unsigned long High_Index = 524288 * 2 - 1;
  49.     unsigned long Push_Count;
  50.     unsigned long Push_Index;
  51.     unsigned long Pop_Count;
  52.     unsigned long Pop_Index;

  53. };
复制代码

论坛徽章:
0
10 [报告]
发表于 2011-12-21 15:02 |只看该作者
还有 google 老板-雇员 找不到{:3_199:}
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP