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

多线程是个不靠谱的东西

浏览 33022 次
该帖已经被评为精华帖
作者 正文
   发表时间:2008-05-08  
引用
一个函数内,上下的关联度是很高,如果 b 是lazy,那么c 也lazy,涉及到c 的表达式都lazy,这跟没做计算有什么区别?


这个问题叫做strictness analysis.这种分析在编译器的前端都已经做掉了,那些东西Lazy,那些东西strict.
真正的求值器从最下端节点开始往上规约的时候,总是规约strict节点.

像C中
a=6+6
b=a*2;
c=a+b;

FP中都是这么写
f(x)= let a=x+x;b=a*2
      in a+b

编译器分析的时候
就已经把let当中的两个表达式解析为子节点,把a=x+x解析为最左的子节点也就是strict节点。在这样一张图里,求值器没有机会spark其他的核心进行辅助求值。
但是你要是这么写
f(x)= let a=x+x;b=a*2;
            c=a+b
      in if g(c)>10000 then c    
        
         else if h(c)>9999 then c*2
         else
            f(c)
这里就会出现多个strict节点,因为g(c),h(c)要做comparison 必须是strict的。而f(c)则是non-strict的。
这个时候,当前的reducer就会把g(c),h(c)切割到一个叫working pool的东西里去。自己发现没有可求值的东西就转到下一个表达式。如果这个时候有并行跑着的reducer,就会从working pool里面去拿表达式,进行求值。

这套方法,就如Elminster所说已经是烂熟了。
引用
理想是希望整个求值过程等于关键路径的耗时,不过前提是总有空闲的核可用,还不算任务创建切换等耗时

至于这个问题,涉及到granularity analysis。这个方面还属于hot-open problem.有几种方向,一种是complexity analysis。这一路基本上很难,成果很少。目前好像只能对小规模的程序进行编译分析.
还有一种是用上面的strictness analysis的结果来进行time analysis.但是这仅对于strict time analysis阶段可以在编译阶段进行复杂性分析。但是对于lazy time analysis目前还是一个比较难的问题.

我觉得这个问题其实和c++的inline很有些相似。除了理论上的研究的static automatic Annotation以外。一般都是腿而求其次,一种方法就是通过Annotation,类似于Pfor这样的语言的标签通过人工干预。另一种方法就是通过统计,也就是类似JIT处理method inline的方式,程序第一次运行未必会帮你全面并行化,但是经过一定时间的运行以后可以通过函数调用统计,分析出那些粒度过细,那些粒度正好合适. 还有另外一种方法也是在runtime的时候分析。主要通过work stealing来实现.当一个函数f()求值的时候会产生一批图节点,这个时候当前的reducer如果忙于对一个node求值,那么其他idle的reducer,就把其他未求值的node steal过来求值.比如说fib(1000),第一个reducer产生fib(999),fib(998),第二个reducer就会过来steal fib(998).第二个reducer又会unwind fib(997),fib(996),第三个reducer又来steal fib(996),以此类推.

但是这一切的一切,都基于一个前提,必须没有副作用.

15 请登录后投票
   发表时间:2008-05-08  
Trustno1 写道

但是你要是这么写
f(x)= let a=x+x;b=a*2;
            c=a+b
      in if g(c)>10000 then c    
        
         else if h(c)>9999 then c*2
         else
            f(c)
这里就会出现多个strict节点,因为g(c),h(c)要做comparison 必须是strict的。而f(c)则是non-strict的。
这个时候,当前的reducer就会把g(c),h(c)切割到一个叫working pool的东西里去。自己发现没有可求值的东西就转到下一个表达式。如果这个时候有并行跑着的reducer,就会从working pool里面去拿表达式,进行求值。


收藏了.

h(c) 为什么是 strict 的 ?
如果  g(c)>10000 条件满足了,就可以不用执行 h(c) 了吧?
有了comparison, 就一定是strict的吗?

有没有这方面的资料 link ?

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

问一个基础的问题.

编译器/VM处理Lazy Node的时候,最终会产生一个 Thunk 结构 (Lazy Singleton).
在运行的时候,这个Thunk结构,应该也会遇到和Lazy Singleton类似的同步锁问题.

Thunk {
 value = null;

 getValue() {
   if value == null
     value = calculate()

   return value
 }

}


这种情况下,VM是如何处理的? 如何才能够达到无副作用,无状态, 无并发竞争, 能够被并行调度?

0 请登录后投票
   发表时间:2008-05-08  
引用
h(c) 为什么是 strict 的 ?
如果 g(c)>10000 条件满足了,就可以不用执行 h(c) 了吧?
有了comparison, 就一定是strict的吗?

这里的确有些问题
h(c)应该还是lazy的
不过你这么写
if g(c)>h(c) then
那么就应该是strict的

comparison,其实和+一样,都是语言中的atomic 算符,遇到这种节点都是强迫求值的.


引用
Thunk {  
value = null;  
 
getValue() {  
   if value == null 
     value = calculate()  
 
   return value  
}  
 

这个value如果是arugment,那么strict analysis的时候自动会把它算做lazy,只有当value求值完成以后才能unwind到下一个节点.
0 请登录后投票
   发表时间:2008-05-08  
Trustno1 写道

引用
Thunk {  
value = null;  
 
getValue() {  
   if value == null 
     value = calculate()  
 
   return value  
}  
 

这个value如果是arugment,那么strict analysis的时候自动会把它算做lazy,只有当value求值完成以后才能unwind到下一个节点.


e, 就是说, 只有strict node才可以被多核并行执行. Lazy node有可能涉及到状态,因此不能被多核并行执行.
0 请登录后投票
   发表时间:2008-05-08  
buaawhl 写道
Trustno1 写道

引用
Thunk {  
value = null;  
 
getValue() {  
   if value == null 
     value = calculate()  
 
   return value  
}  
 

这个value如果是arugment,那么strict analysis的时候自动会把它算做lazy,只有当value求值完成以后才能unwind到下一个节点.


e, 就是说, 只有strict node才可以被多核并行执行. Lazy node有可能涉及到状态,因此不能被多核并行执行.

不明白,什么叫做lazy node 涉及状态
0 请登录后投票
   发表时间:2008-05-08  
Trustno1 写道
buaawhl 写道
Trustno1 写道

引用
Thunk {  
value = null;  
 
getValue() {  
   if value == null 
     value = calculate()  
 
   return value  
}  
 

这个value如果是arugment,那么strict analysis的时候自动会把它算做lazy,只有当value求值完成以后才能unwind到下一个节点.


e, 就是说, 只有strict node才可以被多核并行执行. Lazy node有可能涉及到状态,因此不能被多核并行执行.

不明白,什么叫做lazy node 涉及状态


只有当value求值完成以后才能unwind到下一个节点.
这个意思就是说, Lazy 的执行结点,必须遵守一定的执行顺序.这就涉及到了状态 (反映到具体实现,就是Thunk里面的那个value成员变量).
Right ?

正是因为Thunk有状态, Lazy Node才不能并行执行.
Right ?

0 请登录后投票
   发表时间:2008-05-08  
引用
只有当value求值完成以后才能unwind到下一个节点.
这个意思就是说, Lazy 的执行结点,必须遵守一定的执行顺序.这就涉及到了状态 (反映到具体实现,就是Thunk里面的那个value成员变量).
Right ?

正是因为Thunk有状态, Lazy Node才不能并行执行.
Right ?


Lazy 节点在unwind之前根本不存在.跟状态有什么关系,一旦展开就说明value已经经过求值了.
0 请登录后投票
   发表时间:2008-05-08  
Trustno1 写道

Lazy 节点在unwind之前根本不存在.跟状态有什么关系,一旦展开就说明value已经经过求值了.


Unwind的具体含义是 展开运行?
Lazy节点是什么时候求值的呢?
是否说, 执行节点的执行方式有 unwind 和 wind 两种方式?
Lazy节点只能在 wind 方式下运行?




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

Lazy 节点在unwind之前根本不存在.跟状态有什么关系,一旦展开就说明value已经经过求值了.


Unwind的具体含义是 展开运行?
Lazy节点是什么时候求值的呢?
是否说, 执行节点的执行方式有 unwind 和 wind 两种方式?
Lazy节点只能在 wind 方式下运行?





不是,strict和non-strict的区别仅在于,你从什么方向对图遍历。
比如说square(2+3),sqaure x=x*x. unwind出来就是这一颗树。
如果你bottom up,从最左边的child往上遍历,那么就必须展开square,就是strict的
如果你做top-down,从最右边的root往下遍历,那么就无需展开square,就是non-strict的.


如果你是一个non-strict的语言,那么编译器会分析这张图,然后发现square是non-strict sub-expression,2+3就是一个strict sub-expression.然后再run time的时候根据一定的策略,把所有已经分析出来的sub-expression并行求值.




  • 大小: 4.1 KB
0 请登录后投票
   发表时间:2008-05-08  
Trustno1 写道

不是,strict和non-strict的区别仅在于,你从什么方向对图遍历。
比如说square(2+3),sqaure x=x*x. unwind出来就是这一颗树。
如果你bottom up,从最左边的child往上遍历,那么就必须展开square,就是strict的
如果你做top-down,从最右边的root往下遍历,那么就无需展开square,就是non-strict的.


如果你是一个non-strict的语言,那么编译器会分析这张图,然后发现square是non-strict sub-expression,2+3就是一个strict sub-expression.然后再run time的时候根据一定的策略,把所有已经分析出来的sub-expression并行求值.


前面你写的内容,好像是说,  Lazy语言执行是Top Down, Strict语言执行是Bottom Up.
Top Down可以合并共享Node,因此可以规约为图.

Trustno1 写道

(lambda x:x+3) (x+2)

Apply alpha[x/y]: lambda z . (lambda y . y+z)) (x + 2)
Apply beta: (lambda y . y + x + 2) 3
Apply beta: 3 + x + 2.

对于任何一个纯函数,都能表达为lambda式。对lambda 式可以使用树规约或者图规约.
前者是串行的而后者是天然并行的
比如说这是一个典型的bottom up树规约
((2 + 2) + (2 + 2)) + (3 + 3)
= ((2 + 2) + 4) + (3 + 3)
= (4 + 4) + (3 + 3)
= (4 + 4) + 6
= 8 + 6
= 14
或者构造一个的top down树规约
((2 + 2) + (2 + 2)) + (3 + 3)
= ((2 + 2) + (2 + 2)) + (6)
= ((2 + 2) + 4) + 6
= (4 + 4) + 6
= 8 + 6
= 14
这两种规约都是一颗树
           +
       +           +
    +        +    3 3
 two  two two  two

树规约可以转化为图规约,图规约可以共享子表达式

           +
       +           +
    ?     ?        3 3
       +
    two  two 


因此在图规约下,2+2和3+3互不依赖,可以并行执行.




执行的过程中,父节点需要依赖子节点的执行.
如果是 Bottom Up, 父节点就执行子节点进行求值.
如果是 Top Down, 父节点就是展开(Unwind)子节点.

我想知道的是,
在Top Down的过程中, 父节点展开(Unwind)子节点的时候,
如果遇到一个 Lazy Node, 那么就不会展开,而是直接进行求值.就是说Lazy Node的执行就是要遵守Bottom Up的方式了.
是这样吗?


0 请登录后投票
论坛首页 编程语言技术版

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