Исходный код бэкэнда для numpy .multiply () настроен на многопроцессорность / многопоточность? - PullRequest
0 голосов
/ 09 марта 2020

Я не смог найти исходный код или документацию, описывающую, как написан алгоритм для np.multiply().

Я не смог найти его в руководствах:

https://docs.scipy.org/doc/numpy/reference/generated/numpy.multiply.html

https://docs.scipy.org/doc/numpy-1.9.3/reference/generated/numpy.multiply.html

Кто-нибудь знает, настроен ли исходный код бэкенда для np.multiply() на многопроцессорность / многопоточность? Причина, по которой я спрашиваю, состоит в том, что я пишу свои собственные коды для вычисления продукта Kronecker, который использует параллельное программирование (joblib.Parallel), но когда я тестировал время скорости, np.kron() (который использует np.multiply()) все еще работает быстрее, чем мой код с параллельным программированием.

Редактировать:

Это код, который я написал для моего продукта Kronecker:

from itertools import product
from joblib import Parallel, delayed
from functools import reduce
from operator import mul
import numpy as np

lst = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
arr = np.array(lst)
n = 2

def test1(arr, n):
    flat = np.ravel(arr).tolist()
    gen = (list(a) for a in product(flat, repeat=n))

    results = Parallel(n_jobs=-1)(delayed(reduce)(mul, x) for (x) in gen)

    nrows = arr.shape[0]
    ncols = arr.shape[1]

    arr_multi_dim = np.array(results).reshape((nrows, ncols)*n)
    arr_final = np.concatenate(np.concatenate(arr_multi_dim, axis=1), axis=1)

    return arr_final

1 Ответ

1 голос
/ 09 марта 2020

Ваши усилия здесь и здесь все еще пытаются тратить больше средств на дополнительные накладные расходы (из-за затрат на реализацию процесса и затрат на распространение данных при передаче параметров туда и обратно (для удаленного шага вычисления там и возврата результатов и консолидации назад) и go в совершенно противоположном направлении, чем numpy один.

Эффективность находится на стороне numpy , благодаря тщательно спроектированной конструкции ядра без GIL, которая также может использовать векторизованную обработку (т. е. для вычисления большего количества вещей за один шаг инструкции процессора - из-за известного использования выровненный, ILP и AVX-подобный инструментарий процессора).

Учитывая эти сильные преимущества + numpy -разумная обработка на месте / нулевое копирование (повторное использование кэшированных данных L1 / L2 / L3 идет на много порядков на порядок быстрее, чем любые ваши попытки настроить и использовать набор распределенной обработки, при этом приходится платить дополнительные расходы за каждую RAM-копию на SER / DES + IP C -marshall + RAM-копию на SER / DE S + compute + RAM-копия на SER / DES + IP-маркер C + RAM-копия на SER / DES), интеллектуальный код numpy почти во всех случаях превзойдет любую другую попытку сделать то же самое.


Никогда не забывайте о дополнительных расходах и изучайте и понимайте неблагоприятные последствия масштабирования :

             0.1 ns - NOP
             0.3 ns - XOR, ADD, SUB
             0.5 ns - CPU L1 dCACHE reference           (1st introduced in late 80-ies )
             0.9 ns - JMP SHORT
             1   ns - speed-of-light (a photon) travel a 1 ft (30.5cm) distance -- will stay, throughout any foreseeable future :o)
?~~~~~~~~~~~ 1   ns - MUL ( i**2 = MUL i, i )~~~~~~~~~ doing this 1,000 x is 1 [us]; 1,000,000 x is 1 [ms]; 1,000,000,000 x is 1 [s] ~~~~~~~~~~~~~~~~~~~~~~~~~
           3~4   ns - CPU L2  CACHE reference           (2020/Q1)
             5   ns - CPU L1 iCACHE Branch mispredict
             7   ns - CPU L2  CACHE reference
            10   ns - DIV
            19   ns - CPU L3  CACHE reference           (2020/Q1 considered slow on 28c Skylake)
            71   ns - CPU cross-QPI/NUMA best  case on XEON E5-46*
           100   ns - MUTEX lock/unlock
           100   ns - own DDR MEMORY reference
           135   ns - CPU cross-QPI/NUMA best  case on XEON E7-*
           202   ns - CPU cross-QPI/NUMA worst case on XEON E7-*
           325   ns - CPU cross-QPI/NUMA worst case on XEON E5-46*
        10,000   ns - Compress 1K bytes with a Zippy PROCESS
        20,000   ns - Send     2K bytes over 1 Gbps  NETWORK
       250,000   ns - Read   1 MB sequentially from  MEMORY
       500,000   ns - Round trip within a same DataCenter
?~~~ 2,500,000   ns - Read  10 MB sequentially from  MEMORY~~(about an empty python process to copy on spawn)~~~~ x ( 1 + nProcesses ) on spawned process instantiation(s), yet an empty python interpreter is indeed not a real-world, production-grade use-case, is it?
    10,000,000   ns - DISK seek
    10,000,000   ns - Read   1 MB sequentially from  NETWORK
?~~ 25,000,000   ns - Read 100 MB sequentially from  MEMORY~~(somewhat light python process to copy on spawn)~~~~ x ( 1 + nProcesses ) on spawned process instantiation(s)
    30,000,000   ns - Read 1 MB sequentially from a  DISK
?~~ 36,000,000   ns - Pickle.dump() SER a 10 MB object for IPC-transfer and remote DES in spawned process~~~~~~~~ x ( 2 ) for a single 10MB parameter-payload SER/DES + add an IPC-transport costs thereof or NETWORK-grade transport costs, if going into [distributed-computing] model Cluster ecosystem
   150,000,000   ns - Send a NETWORK packet CA -> Netherlands
  |   |   |   |
  |   |   | ns|
  |   | us|
  | ms|
...