我何时可以并行使用可变变量来调用函数?

Sol*_*lma 1 parallel-processing f# mutable

看完一个有趣的讲座后 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
Run Code Online (Sandbox Code Playgroud)

通过数组映射比通过列表映射快三倍以上.此时,当调用的函数计算量更大时,我还没有测试速度差异.差异可能仅仅是因为更快地遍历数组中的项目,并且当每次迭代都是计算密集时可能变得无关紧要.

但是,必须存在使用数组或更普遍的可变变量才能产生显着差异的情况.

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

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

rmu*_*unn 9

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

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

所以简单的经验法则是"Mutating data + Parallelism = Pain".如果你改变你的数据但没有运行并行代码,那么你的痛苦就会少得多.如果您没有改变数据,那么并行代码不会让您感到痛苦.但如果你两个都做,那就准备好头痛了.