论坛首页 编程语言技术论坛

多线程是个不靠谱的东西

浏览 32949 次
该帖已经被评为精华帖
作者 正文
   发表时间:2008-05-07  
Elminster 写道

我觉得更准确的说法是传统的企业应用性能瓶颈从来都不是 CPU 的计算能力,这和能不能用纯函数式来描述关系并不大。照 T1 的说法,我们过去做的都是“并发”(下面 qiezi 的回帖可以做个例证),高负载服务器研究的是如何能够同时处理更多的请求,琢磨的是如何尽可能地重叠 IO 同时尽可能减少上下文切换。这个方向也已经很成熟了,有效的解决方案一把一把。


qiezi 写道
仔细想了一下,发现用到多线程的地方都是因为IO阻塞。再想一下,发现目前还没做过对计算要求很高的东西


Yes. 我忘了 IO 这个最大的副作用了.
0 请登录后投票
   发表时间:2008-05-07  
buaawhl 写道
如 T1 所说, 主要就是并行粒度的问题.

目前的多线程模型,主要用于多任务,多用户的问题.(任务粒度).
比如,多线程下载.Web静态页面应答. 这些是简单的同类同质线程任务.问题本身就是顺序无关的.

个人看法.
LZ给出的一些例子,都是顺序相关的, 后面的任务要依赖于前面的任务的状态. (任务粒度).
这本身就带有了顺序限制, 这就是更复杂的多线程协作模型. 如消费者/生产者,读写者等等.这些线程任务的角色不同, 类型也不同.这种复杂的数序相关的情况, 什么模型,什么体系都没用.问题本身就是顺序相关的.

关于多核, 最关键的确实是并行粒度的问题.
如果能够并行到指令(语句)级别,那当然最好.
程序员根本就不需要考虑任何多线程模型,同步模型.随便写出一串代码, 就是天生多核并行的.
但那是不可能的(也许可能,但我想象不出来).指令所描绘的任务,很难避免时间性和顺序性.

并行粒度,不一定要达到指令级别. 先达到函数级别应该就够了.
如果,一系列顺序的函数调用都是无状态的,那么这些函数的执行顺序就无关了.
同一个调用层次上的函数就可以并行分配到多核上运行.
这样一层层下去, 调用树所有的无状态函数都可以分配为多核并行.
编程上,也简单了许多.只要写出无状态的函数, 这些函数,就是天生可以多核并行的.




我接触到的大规模计算基本上都有类似性质,大量相同的平行的运算。
为解决这一类计算,硬件上都是往增加特殊的运算单元和指令集这条路上走。RISC是能有利于分析调度,但他的性能终极还是不能和专门设计的运算器比。
再反过来看软件设计,通用设计的性能能不能跟专门设计的指令集和专用数据结构比?

我相信现在大多数的软件性能瓶颈只在存在于局部位置上,只要重点解决这些问题整个软件就能获得成功。

另外,我不认为并行的粒度越小越好,上下文的切换是要开销的,复杂度也上升得很厉害,合理的划分才是最重要的。例如,要利用多核的优势,可以简单地同时启动多个相同的工作,像 VS 在多核环境下就可以同时启动多个 cl.exe做编译,这可能比重新设计一个并行编译器会更有效。
0 请登录后投票
   发表时间:2008-05-07  
呵呵,我不把并行当做解决性能瓶颈的方案,我看种并行是因为到也许后年,一个台机就会有4-8个CPU+2个GPU在工作,让软件更快,并且具备增加CPU就能提高一些速度的能力,会取得某种技术上的优势。
0 请登录后投票
   发表时间:2008-05-07  
hyf 写道
buaawhl 写道
如 T1 所说, 主要就是并行粒度的问题.

目前的多线程模型,主要用于多任务,多用户的问题.(任务粒度).
比如,多线程下载.Web静态页面应答. 这些是简单的同类同质线程任务.问题本身就是顺序无关的.

个人看法.
LZ给出的一些例子,都是顺序相关的, 后面的任务要依赖于前面的任务的状态. (任务粒度).
这本身就带有了顺序限制, 这就是更复杂的多线程协作模型. 如消费者/生产者,读写者等等.这些线程任务的角色不同, 类型也不同.这种复杂的数序相关的情况, 什么模型,什么体系都没用.问题本身就是顺序相关的.

关于多核, 最关键的确实是并行粒度的问题.
如果能够并行到指令(语句)级别,那当然最好.
程序员根本就不需要考虑任何多线程模型,同步模型.随便写出一串代码, 就是天生多核并行的.
但那是不可能的(也许可能,但我想象不出来).指令所描绘的任务,很难避免时间性和顺序性.

并行粒度,不一定要达到指令级别. 先达到函数级别应该就够了.
如果,一系列顺序的函数调用都是无状态的,那么这些函数的执行顺序就无关了.
同一个调用层次上的函数就可以并行分配到多核上运行.
这样一层层下去, 调用树所有的无状态函数都可以分配为多核并行.
编程上,也简单了许多.只要写出无状态的函数, 这些函数,就是天生可以多核并行的.




我接触到的大规模计算基本上都有类似性质,大量相同的平行的运算。
为解决这一类计算,硬件上都是往增加特殊的运算单元和指令集这条路上走。RISC是能有利于分析调度,但他的性能终极还是不能和专门设计的运算器比。
再反过来看软件设计,通用设计的性能能不能跟专门设计的指令集和专用数据结构比?

我相信现在大多数的软件性能瓶颈只在存在于局部位置上,只要重点解决这些问题整个软件就能获得成功。

另外,我不认为并行的粒度越小越好,上下文的切换是要开销的,复杂度也上升得很厉害,合理的划分才是最重要的。例如,要利用多核的优势,可以简单地同时启动多个相同的工作,像 VS 在多核环境下就可以同时启动多个 cl.exe做编译,这可能比重新设计一个并行编译器会更有效。


我的想法是这样的.
类似性质,大量相同的平行的运算,应该属于纯函数范畴,没有任何副作用.
这种纯函数(或者没有副作用的任何指令节点),可以在多个CPU之间进行迁移调度.每次迁移的粒度都是恰好不需要上下文切换. 如果需要上下文切换的情况,会有某种标记告诉调度器不要进行并行多核迁移.

这个调度工作, 用软件做(编译器,VM)比较容易,软件(编译器,VM)可以在各种粒度上(任务,函数,语句)进行调度.因为软件能够接触到语义层次,语法层次.
用硬件做,我不是很了解.在我的想象中,硬件做大粒度的调度,比较困难.
比如,我就无法想象,硬件如何进行任务粒度的调度? 硬件接触到的已经是基本指令了,如何区分并行任务?
函数级别的调度可能也比较困难.硬件是否能够清晰地区分函数边界?
我不具备这方面的知识.



0 请登录后投票
   发表时间:2008-05-07  
buaawhl 写道
hyf 写道
buaawhl 写道
如 T1 所说, 主要就是并行粒度的问题.

目前的多线程模型,主要用于多任务,多用户的问题.(任务粒度).
比如,多线程下载.Web静态页面应答. 这些是简单的同类同质线程任务.问题本身就是顺序无关的.

个人看法.
LZ给出的一些例子,都是顺序相关的, 后面的任务要依赖于前面的任务的状态. (任务粒度).
这本身就带有了顺序限制, 这就是更复杂的多线程协作模型. 如消费者/生产者,读写者等等.这些线程任务的角色不同, 类型也不同.这种复杂的数序相关的情况, 什么模型,什么体系都没用.问题本身就是顺序相关的.

关于多核, 最关键的确实是并行粒度的问题.
如果能够并行到指令(语句)级别,那当然最好.
程序员根本就不需要考虑任何多线程模型,同步模型.随便写出一串代码, 就是天生多核并行的.
但那是不可能的(也许可能,但我想象不出来).指令所描绘的任务,很难避免时间性和顺序性.

并行粒度,不一定要达到指令级别. 先达到函数级别应该就够了.
如果,一系列顺序的函数调用都是无状态的,那么这些函数的执行顺序就无关了.
同一个调用层次上的函数就可以并行分配到多核上运行.
这样一层层下去, 调用树所有的无状态函数都可以分配为多核并行.
编程上,也简单了许多.只要写出无状态的函数, 这些函数,就是天生可以多核并行的.




我接触到的大规模计算基本上都有类似性质,大量相同的平行的运算。
为解决这一类计算,硬件上都是往增加特殊的运算单元和指令集这条路上走。RISC是能有利于分析调度,但他的性能终极还是不能和专门设计的运算器比。
再反过来看软件设计,通用设计的性能能不能跟专门设计的指令集和专用数据结构比?

我相信现在大多数的软件性能瓶颈只在存在于局部位置上,只要重点解决这些问题整个软件就能获得成功。

另外,我不认为并行的粒度越小越好,上下文的切换是要开销的,复杂度也上升得很厉害,合理的划分才是最重要的。例如,要利用多核的优势,可以简单地同时启动多个相同的工作,像 VS 在多核环境下就可以同时启动多个 cl.exe做编译,这可能比重新设计一个并行编译器会更有效。


我的想法是这样的.
类似性质,大量相同的平行的运算,应该属于纯函数范畴,没有任何副作用.
这种纯函数(或者没有副作用的任何指令节点),可以在多个CPU之间进行迁移调度.每次迁移的粒度都是恰好不需要上下文切换. 如果需要上下文切换的情况,会有某种标记告诉调度器不要进行并行多核迁移.

这个调度工作, 用软件做(编译器,VM)比较容易,软件(编译器,VM)可以在各种粒度上(任务,函数,语句)进行调度.因为软件能够接触到语义层次,语法层次.
用硬件做,我不是很了解.在我的想象中,硬件做大粒度的调度,比较困难.
比如,我就无法想象,硬件如何进行任务粒度的调度? 硬件接触到的已经是基本指令了,如何区分并行任务?
函数级别的调度可能也比较困难.硬件是否能够清晰地区分函数边界?
我不具备这方面的知识.




要落实到应用要看实际情况,
理论上你是能分析出那些是可以并行的,但是也要硬件支持啊。

a++;
b++;
c = a + b;
理论上 a++ 和 b++ 是可以并行执行,VM怎么抉择? 把他们分别调度到两个CPU执行?
如果是这样的话,c=a+b 这一句就得不到优化了,这句经过优化其实可以不用再去读内存。

还有同步的问题,假如 CPU1 在执行 c=a+b,CPU2却未完成b++,你打算怎么着?
划分的粒度如此小,以致同步问题激增。

况且,我看不出现在的硬件能支持这种调度。
操作系统的任务调度依靠的是硬件时钟中断,切换任务时要保护一大堆寄存器信息,然后装载另一个任务的状态段,再跳过去。粒度应该是属于过程级别的。
并行两三条指令获得好处比起这个开销就差得远了。
0 请登录后投票
   发表时间:2008-05-07  
hyf 写道

要落实到应用要看实际情况,
理论上你是能分析出那些是可以并行的,但是也要硬件支持啊。

a++;
b++;
c = a + b;
理论上 a++ 和 b++ 是可以并行执行,VM怎么抉择? 把他们分别调度到两个CPU执行?
如果是这样的话,c=a+b 这一句就得不到优化了,这句经过优化其实可以不用再去读内存。

还有同步的问题,假如 CPU1 在执行 c=a+b,CPU2却未完成b++,你打算怎么着?
划分的粒度如此小,以致同步问题激增。


在纯函数中,所有变量都是final的. a++, b++这种语句是不能允许的.
只能写
a1 = a + 1;
b1 = b + 1;
c = a + b;

最终还会被替换为一个公式,
c = a + 1 + b + 1

-------------------------

如果真的是那种 a++, b++ 等有状态,有变化,有一丁点副作用的指令,
这种指令就不应该进行多核并行调度.
指令前面可以加个标记 [single cpu] 之类的 ?

0 请登录后投票
   发表时间:2008-05-07  
buaawhl 写道
hyf 写道

要落实到应用要看实际情况,
理论上你是能分析出那些是可以并行的,但是也要硬件支持啊。

a++;
b++;
c = a + b;
理论上 a++ 和 b++ 是可以并行执行,VM怎么抉择? 把他们分别调度到两个CPU执行?
如果是这样的话,c=a+b 这一句就得不到优化了,这句经过优化其实可以不用再去读内存。

还有同步的问题,假如 CPU1 在执行 c=a+b,CPU2却未完成b++,你打算怎么着?
划分的粒度如此小,以致同步问题激增。


在纯函数中,所有变量都是final的. a++, b++这种语句是不能允许的.
只能写
a1 = a + 1;
b1 = b + 1;
c = a + b;

最终还会被替换为一个公式,
c = a + 1 + b + 1

-------------------------

如果真的是那种 a++, b++ 等有状态,有变化,有一丁点副作用的指令,
这种指令就不应该进行多核并行调度.
指令前面可以加个标记 [single cpu] 之类的 ?



最终分配到两个CPU执行的话还是一样的,因为他们之间的寄存器不共享,一些优化就没办法做了。

0 请登录后投票
   发表时间:2008-05-07  
引用
还有同步的问题,假如 CPU1 在执行 c=a+b,CPU2却未完成b++,你打算怎么着?



Elminster 写道

FP 用了 lazy eval 之后就可以把整个计算过程视为规约一个构造出来的树或者图。


0 请登录后投票
   发表时间:2008-05-07  
Trustno1 写道
引用
还有同步的问题,假如 CPU1 在执行 c=a+b,CPU2却未完成b++,你打算怎么着?



Elminster 写道

FP 用了 lazy eval 之后就可以把整个计算过程视为规约一个构造出来的树或者图。



一个函数内,上下的关联度是很高,如果 b 是lazy,那么c 也lazy,涉及到c 的表达式都lazy,这跟没做计算有什么区别?

理想是希望整个求值过程等于关键路径的耗时,不过前提是总有空闲的核可用,还不算任务创建切换等耗时。

我觉得在现有的技术条件下,任务级并行是合适的。
至于指令并行就让CPU自己做去好了。
0 请登录后投票
   发表时间:2008-05-07  
联系到最近的一些讨论,我越想越觉得,我当初设计的DJ,颇可以解决这个问题~~~

因为DJ,首先是将数据与操作分离了,然后在多个操作间,建立统一的消息触发机制。似乎,针对并发编程的场景,也能够很好的分解代码逻辑。
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics