代码之家  ›  专栏  ›  技术社区  ›  Soldalma

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

  •  1
  • Soldalma  · 技术社区  · 6 年前

    在看了一个有趣的演讲之后 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
    

    通过数组映射比通过列表映射快三倍多。在这一点上,我还没有测试当调用的函数计算量更大时的速度差。这种差异可能仅仅是由于在数组中移动项的速度更快,并且在每次迭代都需要大量计算时可能变得无关紧要。

    不过,在某些情况下,使用数组或更常见的可变变量可能会产生显著的影响。

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

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

    2 回复  |  直到 6 年前
        1
  •  9
  •   rmunn    6 年前

    与数组的速度差与易变性无关 cache locality . 数组在内存中是连续的,因此它们的迭代速度比列表快:f列表是单独链接的列表,因此每个项可以(通常是)位于不同的内存位置。这意味着您无法从CPU的缓存中获益,而对于数组,一旦您支付了从内存中检索第一个项的成本,那么第二个项到第n个项(其中n的值取决于您检索的项的大小)已经在缓存中,并且几乎可以在坦特检索。如果f有一个immutablearray类,并且您使用了它,那么当通过该immutablearray映射时,您将获得与从可变数组映射时相同的速度优势。

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

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

        2
  •  3
  •   Fyodor Soikin    6 年前

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

    这是回答 暗指的 问题,我读作“ 既然可变数据更快,我不应该总是使用可变数据吗?

    事实上,通常情况下,可变的数据结构,如果你把它们弄对了,在表面上会更快,那么我们为什么不一直使用它们呢?如果你做对了,跳转比函数调用要快,这也是事实,所以为什么 don't we use goto all the time ?当然,如果你做对了,手动内存管理会消耗更少的内存, 比垃圾收集快,那我们为什么要使用垃圾收集呢?而且(可以说)如果您得到它,直接在程序集中编写,甚至是二进制代码是正确的 真的,真的 是的,比编译快,那为什么我们有高级语言呢?

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

    在设计系统时,首先尝试 猜测 在可能存在瓶颈的地方,仔细设计这些地方,并在它们周围放置一些日志和工具。在编写程序时,首先要使它们可读、可理解、可维护。然后,在生产环境中,或者在您负担得起的临时环境中,测量性能。我说的“量度”并不是说“它是最快的吗?”,我的意思是“对我们的目的来说够快吗?”如果是的话,很好。如果不是,找出减速的确切位置,并优化那个位置。久而久之,凭经验,你的 猜测 因为潜在的瓶颈会越来越好。

    不要事先尝试优化:到头来你手上会一团糟,你很快就会把它扔掉。 Premature optimization is the root of all evil .