Ray 在 4-CPU-cores 上工作时性能没有提高

Performance doesn't improve with Ray working on 4-CPU-cores

我试图在我的机器上重新运行 教程,但我无法重现教程中显示的性能改进。

可能是什么原因?我已经尝试寻找解决方案,但仍然无法理解。

多处理会产生时间开销 - 我认为这里的基本函数是如此之快,以至于开销占用了大部分时间。本教程真的使用简单整数作为输入吗?如果您使用大型数组作为输入,您应该会看到改进。

Q : What could be the reason for it?

...极低(几乎为零)[PARALLEL]代码执行部分
当将附加开销添加到修订后的 Amdahl 时,“负” 加速 << 1减速)变得明显
Amdahl's Law 定义了为什么,接下来是什么:

第一个:
永远不要在没有正确隔离 SuT - System-under-[ 的情况下开始“基准测试” =53=]Test,这里是分布式形式的计算.

这里,“前面的start = time.time()import ray 声明似乎是对读者对主题注意力​​的挑衅性测试,绝对不是正确设计的用例测试设计的标志 - 你有意地将所有 disk-I/O 延迟 + 从磁盘到 python 会话的数据传输,导入模块的语法检查的 TimeDOMAIN 成本,是 - 解释(在第二次测试中没有相同的条件)

下一个:
削减 import 的成本后,人们可能会开始比较 “同类”

...
#----------------------------------------- APPLES-TO-APPLES ( STILL AWFULLY NAIVE )
start   = time.time()
futures = [ f(i) for i in range(4) ]
print( time.time() - start )
print( 60*"_" + " pure-[SERIAL] python execution" )
#----------------------------------------- ORANGES
start   = time.time()
import ray                               # costs of initial import
ray.init( num_cpus = 4 )                 # costs of parametrisation
@ray.remote                              # costs of decorated def(s)
def f( x ):
    return x * x
print( time.time() - start )
print( 60*"_" + " ray add-on overheads" )
#----------------------------------------- APPLES-TO-APPLES ( STILL AWFULLY NAIVE )
start   = time.time()
futures = [ f.remote(i) for i in range(4) ]
print( time.time() - start )
print( 60*"_" + " ray.remote-decorated python execution" )

接下来是缩放:

对于微型使用规模,就像构建parallel/distributed代码执行的所有大炮只需要-4-调用,测量是可能的,但被许多与硬件相关和软件相关的技巧所扭曲(内存分配和缓存副作用通常是性能障碍,一旦 SuT 精心设计,不会掩盖这些下一个典型的 HPC 核心问题).

>>> def f( x ):
...     return x * x
...
>>> dis.dis( f )
  2           0 LOAD_FAST                0 (x)
              3 LOAD_FAST                0 (x)
              6 BINARY_MULTIPLY     
              7 RETURN_VALUE        

具有“低密度”计算(这里只用一个 MUL x, x 直线 RET ) 永远不会证明所有初始设置成本和所有每次调用的附加开销成本是合理的,这在小计算密度的情况下很重要,而不是在复杂和 CPU 密集型 HPC 计算任务中(为此Amdahl's Law 说,可实现的加速的主要限制在哪里)。

下一个片段将显示 f.remote() 调用的平均每次调用成本,分布在 4-CPU ray.remote- 处理路径上,与普通的相比,垄断的 GIL 步进处理模式(有关 [min, Avg, MAX, StDev] 的详细信息,请参阅其他基准测试帖子)

#----------------------------------------- APPLES-TO-APPLES scaled ( STILL NAIVE )
test_4N = 1E6                            # 1E6, 1E9, ... larger values may throw exception due to a poor ( not well fused ) range()-iterator construction, workarounds possible
start   = time.time()
futures = [ f.remote(i) for i in range( int( test_4N ) ) ]
print( ( time.time() - start ) /             test_4N )
print( 60*"_" + " ray.remote-decorated python execution per one call" )
#----------------------------------------- APPLES-TO-APPLES scaled ( STILL NAIVE )
start   = time.time()
futures = [ f(i) for i in range( int( test_4N ) ) ]
print( time.time() - start /          test_4N )
print( 60*"_" + " pure-[SERIAL] python execution" )

奖金部分

### Smoke-on : ###

如果确实感兴趣在燃烧更多燃料以了解密集计算如何受益于 just-[CONCURRENT],True-[PARALLEL] 甚至 尝试添加更多 CPU 密集型计算,一些显着的 RAM 内存分配,远远超出 CPU 内核的 L3 - 缓存大小,在参数和结果传输中的进程之间传递更大的 BLOB-s,如果不是略微超过 O/S 的高效进程切换和 RAM 交换,则更接近真实-life 计算问题,延迟和由此产生的性能确实很重要:

import numpy as np
@ray.remote
def bigSmoke( voidPar = -1 ):
    #                                +------------- this has to compute a lot
    #      +-------------------------|------------- this has to allocate quite some RAM ~ 130 MB for 100 x str( (2**18)! )
    #      |                         |                 + has to spend add-on overhead costs
    #      |                         |                          for process-to-process result(s) ~ 1.3 MB  for  (2**18)!
    #      |                         |                          SER/DES-transformations & transfer costs
    #      |    +--------------------|------------- this has to allocate quite some RAM ~ 1.3 MB for (2**18)!
    #      |    |                    |           +- this set with care, soon O/S swapping may occur above physical RAM sizes
    return [ str( np.math.factorial( i )     #   |              and immense degradation of the otherwise CPU-processing appears then
                                 for i in int( 1E2 ) * ( 2**18, )
                  )
             ][-1] # <----------------------------- this reduces the amount of SER/DES-transformations & process-2-process transfer costs

...
#----------------------------------------- APPLES-TO-APPLES scaled + computing
test_4N = 1E1                            # be cautious here, may start from 1E0, 1E1, 1E2, 1E3 ...
start   = time.time()
futures = [ bigSmoke.remote(i) for i in range( int( test_4N ) ) ]
print( ( time.time() - start ) /                    test_4N )
print( 60*"_" + " ray.remote-decorated set of numpy.math.factorial( 2**18 ) per one call" )
#----------------------------------------- APPLES-TO-APPLES scaled + computing
start   = time.time()
futures = [ bigSmoke(i) for i in range( int( test_4N ) ) ]
print( ( time.time() - start ) /             test_4N )
print( 60*"_" + " pure-[SERIAL] python execution of a set of numpy.math.factorial( 2**18 ) per one call" )

无论如何,请注意,过早的优化工作很容易误导人们的注意力,因此请随时阅读此处经常出现的性能调优故事,在 Stack Overflow 中。