编译器是否优化了并发性?

2021-02-01 21:28

假定我已经编写了一些顺序代码,可以将其分解为多个独立的任务,引入并发似乎很有效。例如print(priceous\u calc\u one()+priceous\u calc\u two())假设pr

解答动态

  • 假设昂贵的

    • 计算1和昂贵的

      • 计算2是纯函数
        ,判断一个函数是否纯就相当于解决一般情况下的中止问题。因此,你不能有一个在一般情况下可以决定函数是否纯的提前编译器。
        你必须帮助编译器,通过显式设计语言,使编译器有机会决定纯。例如,您必须强制程序员显式地对此类函数进行注释,或者执行Haskell或Clean之类的操作,在这些操作中,您可以使用system.
        类型清楚地隔离副作用,但计算成本也相当高
        不幸的是,在提前编译程序中确定函数是否为quot;sparkquot;thunkquot;thunk";又只是一段未经计算的代码),但这产生了太多的火花,以至于管理火花的开销淹没了运行时。
        当您这样做时,实际上,与命令式语言相比,您最终会遇到相反的问题:您很难将庞大的顺序代码分解为较小的并行位,而是很难将微小的并行位组合成合理大小的顺序位。虽然这在语义上比较容易,但因为不能通过序列化纯并行函数来中断代码,所以要获得正确的并行度和顺序位的大小仍然非常困难。

        • 编译器通常不够聪明,尤其是因为大多数语言都没有一个足够可靠的“纯函数”概念。线程之间可能存在相当微妙的交互。显然,它们不应该访问相同的内存区域。但是程序可能依赖于不变量,这些不变量可以通过并发修改来突破。引入线程也是对原始程序的一个相当剧烈的改变。这里讨论了“自动异步/等待”的一些其他方面。但是,如果给定一个并发程序,一些编译器/运行时可以使该程序并行运行,而无需大量额外工作。这些语言通常有一个负责完成挂起任务的执行器的概念,其中任务可以是I/O操作或正在等待的异步函数。通常可以有不同的执行器实现,即不同的事件循环,有时甚至在同一个程序中有多个正在运行的执行器。
          对于执行器,我所要说的就是我的函数是异步的,可以挂起它们。然后运行时负责决定如何调度它们,无论是在单线程事件循环中还是在多个并行线程上。每个CPU最多有一个线程,并且使用进程内调度器可以避免任务切换开销。通常,带有async/await的语言会将您的代码片段表示为这个:
          #启动昂贵的计算task_one=pricing_calc_one()task_two=pricing_calc_two()#等待两者完成并打印结果打印(await task_one+await task_two) 但是,执行异步函数的方式不同。当一个异步函数被调用时,有些函数直接执行到第一个挂起点,这不能导致并行性。其他人将创建任务而不执行函数的任何部分。应该在“后台”执行的CPU受限任务可能仍然需要额外的注释。遵循此一般策略的
          语言有C#、Python、Rust和许多其他语言。Python并不真正支持并行性,即使在使用多个线程时也是如此。在Rust中,类型系统确保可以将数据发送到不同的线程,或者在多个线程之间共享数据,如果没有,则防止创建此类任务。在Go中,可以用单元素通道模拟等待,而任务可以用Go关键字创建。JavaScript可以启动多个worker,但是事件循环是单线程的。Java和Swift也提供了执行器,尽管没有真正的wait语法。
          Haskell可能是唯一一种可以实现自动多线程的语言,因为每个函数(不涉及IO monad)都是纯定义的。但我不认为Haskell/GHC实际上是隐式地启动多个线程的

          • End

          免责声明:

          本页内容仅代表作者本人意见,若因此产生任何纠纷由作者本人负责,概与琴岛网公司无关。本页内容仅供参考,请您根据自身实际情况谨慎操作。尤其涉及您或第三方利益等事项,请咨询专业人士处理。