【问题标题】:When can I call a function using mutable variables in parallel?什么时候可以并行使用可变变量调用函数?
【发布时间】:2018-11-13 00:44:32
【问题描述】:

看了Phil Trelford的有趣讲座后

https://www.youtube.com/watch?v=hx2vOwbB-X0

我对通过将列表替换为数组以及更普遍地使用可变变量来加速我的代码的可能性很感兴趣。于是我做了一个简单的测试:

let xs = [0.0..1000000.00]
let axs = List.toArray xs

let f x = sin (x * x)

List.map f xs // Real: 00:00:00.170, CPU: 00:00:00.187, GC gen0: 5, gen1: 3, gen2: 1

Array.map f axs // Real: 00:00:00.046, CPU: 00:00:00.046, GC gen0: 0, gen1: 0, gen2: 0

通过数组映射比通过列表映射快三倍以上。此时我还没有测试当调用的函数计算量更大时的速度差异。差异可能只是因为在数组中的项目移动速度更快,并且在每次迭代计算密集型时可能变得微不足道。

不过,在某些情况下,使用数组或更一般的可变变量可能会产生重大影响。

在更改我的代码以使用数组而不是列表之前,我想更清楚地了解代码并行化时的后果。

一般来说,什么时候可以使用可变变量而不会有并行代码出现问题的风险?是否有一个简单的测试可以让我确定函数在并行调用时的稳健性?

【问题讨论】:

    标签: parallel-processing f# mutable


    【解决方案1】:

    与数组的速度差异与可变性无关;都是关于cache locality。数组在内存中是连续的,因此它们比列表更快地迭代:F# 列表是单链表,因此每个项目可以(并且通常是)位于不同的内存位置。这意味着您不会从 CPU 的缓存中受益,而对于数组,一旦您支付了从内存中检索第一个项目的成本,然后是第 2 到第 N 个项目(其中 N 的值取决于您正在检索的项目)已经在缓存中并准备好进行几乎即时的检索。如果 F# 有一个 ImmutableArray 类并且您使用了它,那么在通过该 ImmutableArray 进行映射时,您将获得与从您的可变数组进行映射时相同的速度优势。

    至于您的主要问题,关于何时可以安全地将可变变量与并行代码一起使用,简单的测试是询问“我是否实际上正在改变多个线程正在使用的数据?”如果你没有改变你的数据,那么让多个线程并行访问它是安全的。即使数据可以被改变(例如,一个数组),只要你实际上没有改变它,那么你的并行代码就不会遇到问题。如果你确实改变了数据,那么你必须处理锁定,以及所有与锁定相关的问题,例如资源匮乏、死锁等等。

    所以简单的经验法则是“变异数据 + 并行 = 痛苦”。如果你改变了你的数据但没有运行并行代码,你的痛苦就会少得多。如果你不改变你的数据,那么并行代码不会给你带来痛苦。但如果你同时做这两个,准备好头痛。

    【讨论】:

    • 我突然想到,由于 .NET 内存分配的工作方式,一次性初始化的列表实际上可能是内存本地的。
    【解决方案2】:

    虽然@rmunn 已经为实际问题提供了很好的答案,但我觉得我必须写这个附录,因为我认为它非常重要,而且太长了,无法放在评论中。

    这是为了回答隐含的问题,我将其解读为“既然可变数据更快,我不应该总是使用可变数据吗?

    确实,一般来说,可变数据结构,如果你做对了,表面上会更快,那么为什么我们不一直使用它们呢?如果你做对了,跳跃也确实比函数调用快,那么为什么don't we use goto all the time?手动内存管理也是真的,如果你做对了,用的内存更少,而且比垃圾回收快,那我们为什么要使用垃圾回收呢?确实(可以说)直接用汇编甚至二进制代码编写,如果你得到它真的,真的正确,比编译更快,所以我们为什么要拥有高级语言?

    以上所有问题的答案是,性能并不是软件开发中唯一关心的问题。这甚至不是最重要的问题。甚至可以说,它并不是最重要的关注点接近的任何地方。在现代,更重要的是可读性、稳定性、可维护性和整体弹性。

    在设计您的系统时,首先尝试猜测瓶颈可能在哪里,然后仔细设计这些地方并在它们周围放置一些日志记录和工具。在编写程序时,首先要使它们可读、可理解、可维护。然后测量性能——在生产环境中,或者在你负担得起的情况下在暂存环境中。 “衡量”我的意思不是“它是最快的吗?”,我的意思是“它对我们的目的来说足够快吗?”。如果是的话,很好。如果不是,请找出减速的​​确切位置,并优化该位置。随着时间的推移,随着经验的积累,您对潜在瓶颈的猜测会越来越好。

    不要尝试提前进行优化:您最终只会弄得一团糟,而且您必须尽快将其扔掉。 Premature optimization is the root of all evil.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-15
      • 2019-07-31
      • 1970-01-01
      • 2012-08-16
      • 2011-02-24
      • 1970-01-01
      • 2015-03-05
      相关资源
      最近更新 更多