免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: hy036630
打印 上一主题 下一主题

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

论坛徽章:
0
41 [报告]
发表于 2011-12-23 11:02 |只看该作者
xhl 发表于 2011-12-23 09:00
仔细看了一下那个队列, 貌似没问题。主要是利用数组跟最大长度机制保证了非原子不安全性。

我没 ...

我觉得你讲的蛮好的 ,基本上就是我的思路

1.的确要均衡各节点的负载,不过测试情况是各节点都做同样的事情,所以负载应该是很平衡的。

2.我觉得我那样空等,最多浪费CPU时间,但是不至于性能降低如此多,你说得一批一批得处理,是基于队列空转的考虑么?

3.程序是访存密集型的,所以CPU并没有很大的计算量,大部分情况是读内存,写内存,所以我怀疑是访存的影响,但是多进程(把数据都复制一份给

各个进程)并没有如此明显的性能下降(基本上有0.8以上的加速比)。所以想不通,处理的过程中,所有的线程需要访问同一份配置数据,不会有影响吧。

论坛徽章:
0
42 [报告]
发表于 2011-12-23 11:27 |只看该作者
你的问题不晓得我理解清楚没。 所有的流水线线程。比如 1, 2, 3, 4,...n,   2是需要1的处理结果,3需要2的结果?。。。n需要n-1的即如果才能得到最终结果。是不是?如果是基于这个假设。我想你的瓶颈在线程切换上面。。

论坛徽章:
0
43 [报告]
发表于 2011-12-23 12:40 |只看该作者
xiboboy123 发表于 2011-12-23 11:27
你的问题不晓得我理解清楚没。 所有的流水线线程。比如 1, 2, 3, 4,...n,   2是需要1的处理结果,3需要2 ...

不是这样的,每个线程只关心流数据,不关心其他线程的结果

论坛徽章:
0
44 [报告]
发表于 2011-12-23 12:48 |只看该作者
本帖最后由 xhl 于 2011-12-23 13:08 编辑
2.我觉得我那样空等,最多浪费CPU时间,但是不至于性能降低如此多,你说得一批一批得处理,是基于队列空转的考虑么?


这个我觉得你的理解有问题, 你一个其他线程浪费CPU, 就直接导致你的真的需要工作的线程获得CPU的机会小。

假如一段程序如下:

int i = 0;
while (1)
{
    i++;
    if (i = 100)
    {
        DoTask();
        i = 0;
    }
}

你觉得这样的程序性能能理想?我认为你的流水线里很可能有这样类似的情况发生。

我建议你的流水线线程模型应该如下:

while (1)
{
    Lock(inputQueue.mutex)
   
    快速得到一批数据从inputQueue里

    Unlock(inputQueue.mutx)

    if (输入队列里没有数据了)  休息个100毫秒, continue, 或者用条件变量等待
   
    遍历得到这批数据, DoTask();

    Lock(outputQueue.mutex)
   
    快速装入一批数据进入outputQueue

    Unlock(outputQueue.mutex)
}

这个模型不适合你做的那个循环BUFF队列, 因为你那个有最大值,当一个快一个慢满了的时候不好处理, 就直接用普通的std::list上锁就可以了。
只有多个生产者多个消费者, 上锁对效率影响比较大, 一个生产一个消费, 如果锁用好了, 效率一点都不差。
         
3.程序是访存密集型的,所以CPU并没有很大的计算量,大部分情况是读内存,写内存,所以我怀疑是访存的影响,但是多进程(把数据都复制一份给

各个进程)并没有如此明显的性能下降(基本上有0.8以上的加速比)。所以想不通,处理的过程中,所有的线程需要访问同一份配置数据,不会有影响吧。



多进程的模式下, 我想你是通过网络或者进程通信的手段来实现的吧。 这个我想区别就在于你没把CPU浪费在无用功上, 因为网络或者进程通信不会像你那样轮训处理。

至于说多个线程访问进程内同一内存, 我觉得只要是只读,不上锁, 这个不会导致严重的性能下降。


Linux下的线程跟进程差别不大。 本质不在这些, 你所说的吞吐就是你相同时间内,你的CPU执行指令周期是总量是固定的, 你到底有多少指令周期做了你影响吞吐的事情。

论坛徽章:
0
45 [报告]
发表于 2011-12-23 13:07 |只看该作者
xhl 发表于 2011-12-23 12:48
这个我觉得你的理解有问题, 假如一段程序如下:

int i = 0;

int i = 0;
while (1)
{
    i++;
    if (i = 100)
    {
        DoTask();
        i = 0;
    }
}

你觉得这样的程序性能能理想?

虽然我想不通上面的程序性能为什么有影响,不过我还是决定去试试你的方案。

不过我测试的结果 队列真不是瓶颈。

论坛徽章:
0
46 [报告]
发表于 2011-12-23 13:14 |只看该作者
hy036630 发表于 2011-12-23 13:07
int i = 0;
while (1)
{



顺便告诉你一下如何实现快速取一批。

你要用普通的链表实现队列, 装的时候, 先把一堆节点都挂接到, 然后上锁, 把你准备好的这个队列接到线程之间的队列里。

取得时候也, 直接从线程队列里拿下来一个节点跟他后面的一批节点, 这样其实上锁基本没啥开销, 速度很快。

如果当前某个线程没事情做了, 就让他休息, 把CPU让给更需要的线程使用。

论坛徽章:
0
47 [报告]
发表于 2011-12-23 13:23 |只看该作者
本帖最后由 sonicling 于 2011-12-23 13:24 编辑

回头看了一下你的设计和RingQueue。RingQueue本身并没有什么问题,但是我注意到你是把处理单元放在RingQueue里面,而不是数据?这样能叫流水线吗?

那个RingQueue就相当于传送带,流水线是要把处理对象放在传送带上,然后给处理单元按顺序处理,标准的就是按照xiboboy123在42楼说的那样。我以前做的也是这样。

我详细说一下我之前做的吧。之前在一个比较蛋疼的公司做,他们要求进行全盘加密,而加密是靠他们提供的一个加解密卡来实现,让我写驱动。那个所谓的高速加解密卡俺就不多批评了,设置密钥和加/解密模式使用的慢速的LPC接口,数据传输用DMA。他们内部测试能达到40MB/s,按看了一下测试代码,我艹,只是开始把密钥和加解密设置好,并准备好数据,然后for循环DMA……这不是在测试加解密速度,这是在测试DMA速度……如果随机设置密钥和加解密模式,实际速度得降几个数量级,因为LPC和DMA就隔了好几个数量级。

为了让他们那蛋疼的加密卡满负荷运行,我必须保证所有的数据在流水线上高速流动,从而发挥DMA的最大效率。总共的流程有5个:1取数据-》2DMA到卡-》3等待加解密完成-》4DMA到内存-》5返回结果数据,其中1,234,5可以分三组并行,因此我准备了一个分4段的Ring,插入段(1)、准备段(234)、完成段(234)、返回段(5),然后开3个内核进程处理。

每个段由一个下标表示,index_1 ~ index4,插入段的空余空间是index_2-index_1,准备段index3-index2以此循环类推。每次插入的时候,先读下一段的下标并判定是否可以增加,如果是,就更新本段下标。所有下标只由一个线程循环递增,其他线程读,因此无需加锁,这就是lockfree的分段ring结构。最后的速度是23MB/s,因为DMA之前还得准备DMA用的物理内存(不像那个艹蛋的测试程序,连内存准备都免了),而DMA内存是稀缺的竞争资源,所以这得花上一点时间。这个速度也就是每个数据流过整条Ring的平均速度。



论坛徽章:
0
48 [报告]
发表于 2011-12-23 13:28 |只看该作者
另外循环等待会影响进程的优先级,因为进程总是满满当当用完了自己的时间片,却并没做多少实际事情,而Linux调度可不管你是否做了实事。也许你测试时没开其他程序,所以没什么影响,如果有其他的交互程序运行来抢夺CPU,你的进程是绝对抢不赢的,优先级低一些。

论坛徽章:
0
49 [报告]
发表于 2011-12-23 13:47 |只看该作者
呵呵 RingQueue里放的是数据呢  不是处理单元
处理单元一直读RingQueue里的数据,然后push到下一级的RingQueue里面
{:3_201:}

我觉得大家都把关注点放到了流水线和lockfree上面了 其实这个模型如果不处理数据, 吞吐量是很大的,100W/s

但是开的处理单元多了(线程),速度就成倍降低啊,实际处理1个处理单元3W,10个处理单元3000  测试的时候,处理单元都是做同样的处理,就

像我之前贴出来的代码。

还有,希望不要有单核多进程的思路考虑这个问题啊,机器是多核的,足够每个线程跑在一个核上面。


实际的业务场景是做内存统计
举个例子   从接口收到了今天的上网记录  里面有  {id,name,url,agent},那么第一个处理单元希望根据URL这个维度统计上网人数,第二个处理单元希望根据agent这个维度统计上网人数。那么处理单元1拿到上网记录指针,如果这个url在统计结果里,那么更新这个统计结果,如果没有,那么插入统计结果,处理完以后把指针放到下一个处理单元的队列里。第二个处理单元然后根据agent再做统计。

更通俗的过程就是:
把上网记录存到一张数据库表里面 ,假如这张表是net_cdr  ,那么处理单元1完成的功能用sql来表示就是select count(*) from net_cdr group by   
url.处理单元2完成的功能就是select count(*) from net_cdr group by agent;
我的程序只是把这个过程放到内存里面做了,因为数据库不能承受如此大数据量的快速查询。

论坛徽章:
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
50 [报告]
发表于 2011-12-23 14:09 |只看该作者
hy036630 发表于 2011-12-23 13:47
呵呵 RingQueue里放的是数据呢  不是处理单元
处理单元一直读RingQueue里的数据,然后push到下一级的RingQ ...

就是, 一帮人似乎不认真看帖, 什么他妈的空转, 如果系统中除此之外不运行其他任务, 然后每个核分配一个线程, 我空转又怎么了, 非要等的流水线前先堆起一座山, 然后再放开开足马力处理? 你堆山的时间就不是时间了???
只要满足流水线越靠前的的线程处理时间越短就没有什么问题, 自然最理想的是各个处理所用时间都相同
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP