Производительность не улучшается при работе Ray на 4-х процессорных ядрах - PullRequest
1 голос
/ 06 января 2020

Я пытаюсь повторно запустить учебник на моей машине, но мне не удается воспроизвести улучшения производительности, как показано в учебнике.

В чем может быть причина этого? Я пытался найти решения, но все еще не мог понять.

performance comparison with & without ray

Ответы [ 2 ]

1 голос
/ 06 января 2020

Q : Что может быть причиной этого?

... an чрезвычайно низкий (почти ноль) [PARALLEL] выполнение кода часть enter image description here
Когда дополнительные дополнения добавляются к пересмотренному Amdahl, «отрицательный» ускорение << 1 </strong> ( замедление ) становятся очевидными

Амдал Закон определяет обоснование ПОЧЕМУ, затем следует ЧТО:

Первое:
никогда не начинать "тестирование" без правильной изоляции SuT - S ystem- u nder- T est, здесь это распределенная -форма вычисление .

Здесь start = time.time() находится "перед" , оператор import ray выглядит следующим образом: скорее провокационный тест концентрации читателей на предмете, определенно не признак должным образом спроектированного тестового дизайна - вы сознательно берете в измеренное время также все задержки дискового ввода-вывода + передачи данных с диска в сеанс 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" )

Далее идет масштабирование:

Для миниатюрных масштабов использования , таких как создание всей артиллерии параллельного / распределенного выполнения кода только для вызовов измерения возможны, но искажены многими хитростями, связанными с аппаратным обеспечением и программным обеспечением (распределение памяти и побочные эффекты кэша чаще всего являются блокирующими производительность, после того как SuT был хорошо обработан, чтобы затмить эти очередные типичные проблемы с ядром HP C.

>>> 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        

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 1085 прямо RET) никогда не будет оправдывать все начальные затраты на настройку и все накладные расходы на дополнительные вызовы, которые имеют значение в случаях небольшой вычислительной плотности, а не в сложных и ресурсоемких вычислительных задачах HP C ( для которого в законе Амдала указано, где находятся основные ограничения для достижения возможного ускорения).

В следующем фрагменте будет показана средняя стоимость одного вызова для f.remote () - звонков, Распределение по 4-CPU ray.remote -процессам обработки по сравнению с простым монопольным c 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" )

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" )

В любом случае, предупреждаем, что преждевременные усилия по оптимизации могут сбить с толку фокус, поэтому не стесняйтесь читать рассказы о настройке производительности, которые так часто представлены здесь, в StackOverflow.

1 голос
/ 06 января 2020

Многопроцессорная обработка создает временные издержки - я думаю, что базовая функция здесь настолько быстрая, что накладные расходы занимают большую часть времени. Действительно ли учебник использует простое целое число в качестве входных данных? Если вы используете большой массив в качестве входных данных, вы должны увидеть улучшение.

...