2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу Пред.  1, 2
 
 Re: Ускорение кода на Python
Сообщение22.10.2018, 15:11 


29/12/13
127
Pphantom в сообщении #1348330 писал(а):
Так замедлить выполнение, вообще говоря, тривиальной задачи еще постараться надо.

Pphantom в сообщении #1348330 писал(а):
Это я к тому, что ТС вообще-то нужны не терминологические споры, а производительный код.
Замедлился код, это разумеется, это же специально, для примера т.к. wait(дожидается пока одна задача отработает) и специально sleep(задержка) , это же пример, с демонстрацией особенностей, как вариант, куда ТС мог-бы попробовать копать.

Я слегка "поправил/изуродовал" этот код, чтобы наглядней была "параллельность" и время сравнить
код: [ скачать ] [ спрятать ]
Используется синтаксис Python
#!/usr/bin/env python3

import asyncio  
import time  
import random

from datetime import datetime


def custom_sleep():  
     print('SLEEP', datetime.now())
     time.sleep(1)    

def factorial(name, number):  
    f = 1
    for i in range(2, number+1):
        print('Task {}: Compute factorial({})'.format(name, i))
        custom_sleep()
        f *= i
    print('Task {}: factorial({}) is {}\n'.format(name, number, f))


async def custom_sleep_async(asynchr=False):  
     print('SLEEP', datetime.now())
     await asyncio.sleep(1)
 
#     await asyncio.sleep(random.randrange(1,5,1))
#     await time.sleep(1)

async def factorial_async(name, number):  
    f = 1
    for i in range(2, number+1):
        print('Task {}: Compute factorial({})'.format(name, i))
        #await custom_sleep()
        await custom_sleep_async()
        f *= i

    print('Task {}: factorial({}) is {}\n'.format(name, number, f))

start = time.time()  
#не парралельно
print("\n------------------------\nstart without asyncio \n start time: {}".format(start))
factorial("A", 3)
factorial("B", 4)
end = time.time()
print("end {} ; Total time : {}\n".format(end,end-start))


loop = asyncio.get_event_loop()

#tasks = [  
#    asyncio.ensure_future(factorial("A", 3)),
#    asyncio.ensure_future(factorial("B", 4)),
#]
start = time.time()
#параллельно  
print("\n------------------------\nstart with asyncio \n start time: {}".format(start))
features=asyncio.gather(factorial_async("A", 3),factorial_async("B", 4))
#loop.run_until_complete(asyncio.wait(tasks))  
loop.run_until_complete(features)  

loop.close()

end = time.time()  
print("end {} ; Total time:{} \n".format(end,end-start))
 

вывод:
код: [ скачать ] [ спрятать ]
Используется синтаксис Text

start without asyncio
 start time: 1540209754.7767594
Task A: Compute factorial(2)
SLEEP 2018-10-22 16:02:34.776855
Task A: Compute factorial(3)
SLEEP 2018-10-22 16:02:35.778229
Task A: factorial(3) is 6

Task B: Compute factorial(2)
SLEEP 2018-10-22 16:02:36.779652
Task B: Compute factorial(3)
SLEEP 2018-10-22 16:02:37.781049
Task B: Compute factorial(4)
SLEEP 2018-10-22 16:02:38.782444
Task B: factorial(4) is 24

end 1540209759.7838647 ; Total time : 5.007105350494385


------------------------
start with asyncio
 start time: 1540209759.7845988
Task B: Compute factorial(2)
SLEEP 2018-10-22 16:02:39.784975
Task A: Compute factorial(2)
SLEEP 2018-10-22 16:02:39.785099
Task B: Compute factorial(3)
SLEEP 2018-10-22 16:02:40.786684
Task A: Compute factorial(3)
SLEEP 2018-10-22 16:02:40.786846
Task B: Compute factorial(4)
SLEEP 2018-10-22 16:02:41.788436
Task A: factorial(3) is 6

Task B: factorial(4) is 24

end 1540209762.7905009 ; Total time:3.005902051925659
 

видно, что работают в одно и тоже время "параллельно". последовательно занимает 5сек, а параллельно 3. несложно это приспособить куда надо. Этот asyncio, можно также комбинировать с concurrent.futures и ещё примеры, так можно добиться того, что нужно. в разных потоках и пр. А более конкретно, чтобы с конкретным кодом, так для этого нужна конкретная задача.

Про терминологию, тут именно терминологическая путаница. это concurrent code.
Цитата:
For instance, The Art of Concurrency defines the difference as follows:

A system is said to be concurrent if it can support two or more actions in progress at the same time. A system is said to be parallel if it can support two or more actions executing simultaneously. The key concept and difference between these definitions is the phrase "in progress."

This definition says that, in concurrent systems, multiple actions can be in progress (may not be executed) at the same time. Meanwhile, multiple actions are simultaneously executed in parallel systems. In fact, concurrency and parallelism are conceptually overlapped to some degree, but "in progress" clearly makes them different.

https://takuti.me/note/parallel-vs-concurrent/

видео на тему - 'Concurrency Is Not Parallelism'

Эти понятия слегка различаются особенностями, но оба про "параллельность" в том смысле, что задачи исполняются одновременно, в одно и тоже время. Поэтому я и сказал, что "асинхронность" это по сути разновидность "параллельности". это грубо, но убежден, что близко к истине. Просто concurrency дает больше возможностей и позволяет более эффективно решать задачи.

-- 22.10.2018, 15:26 --

mihaild в сообщении #1348333 писал(а):
Если бы фьючерсы считались параллельно, то в вашем примере sleep'ы в A и B запускались одновременно.


Посмотрите, последний мой пример. там нам наглядней параллельность. и всё что я думаю по теме изложил.

 Профиль  
                  
 
 Re: Ускорение кода на Python
Сообщение22.10.2018, 15:50 
Супермодератор
Аватара пользователя


09/05/12
16582
Кронштадт
Seman в сообщении #1348353 писал(а):
Замедлился код, это разумеется, это же специально, для примера т.к. wait(дожидается пока одна задача отработает) и специально sleep(задержка) , это же пример, с демонстрацией особенностей, как вариант, куда ТС мог-бы попробовать копать.
А зачем туда копать? У ТС, судя по описанию, чисто вычислительная задача, которую надо запустить в большом числе экземпляров, независимых друг от друга. Разделяемые ресурсы отсутствуют, конкуренция за них, как следствие, тоже, поэтому асинхронность не нужна (более того, даже вредна, поскольку сама требует процессорное время на свою поддержку). Так что (если отвлечься от векторизации) выгоднее просто посадить по процессу на каждое имеющееся в наличии ядро. Если же ядро одно - считать все по порядку, чтобы не тратить время на ненужные переключения.

 Профиль  
                  
 
 Re: Ускорение кода на Python
Сообщение22.10.2018, 15:54 
Заслуженный участник
Аватара пользователя


16/07/14
2852
Москва
Seman в сообщении #1348353 писал(а):
Эти понятия слегка различаются особенностями, но оба про "параллельность" в том смысле, что задачи исполняются одновременно, в одно и тоже время. Поэтому я и сказал, что "асинхронность" это по сути разновидность "параллельности". это грубо, но убежден, что близко к истине.
Не исполняются, а "исполняются". Т.е. одна задача может выполняться, пока другая ждет (ничего не делает). А ТС нужно чтобы несколько задач выполнялись одновременно. Хорошо видно, что заменить в ваших примерах async.sleep на time.sleep (и тем более на что-то cpu-intensive) и получить выигрыш по времени не получится.

Собственно в статье есть красивые картинки, чем asynchronous отличается от parallel.
Parallel Изображение
Asynchronous single thread Изображение
Asynchronous multi thread Изображение

 Профиль  
                  
 
 Re: Ускорение кода на Python
Сообщение22.10.2018, 16:58 


29/12/13
127
Pphantom в сообщении #1348362 писал(а):
А зачем туда копать? У ТС, судя по описанию, чисто вычислительная задача

Ну, так он же, мог бы разобраться по примерам и ссылкам как работать с asyncio , распределитель им свою задачу на задачи, и решить устраивает это его или нет.

Pphantom в сообщении #1348362 писал(а):
Разделяемые ресурсы отсутствуют, конкуренция за них, как следствие, тоже, поэтому асинхронность не нужна (более того, даже вредна, поскольку сама требует процессорное время на свою поддержку). Так что (если отвлечься от векторизации) выгоднее просто посадить по процессу на каждое имеющееся в наличии ядро. Если же ядро одно - считать все по порядку, чтобы не тратить время на ненужные переключения.


Если посадить на ядро, (одно ядро - один процесс?) ,т.е. имеется ввиду "посадить" по процессу(с одним потоком) на ядро, на несколько ядер, то это должно быть медленней чем один процесс на одно ядром(всего один процесс и одно ядро ), но со многими потоками(если потоков во втором случае, значительно больше чем процессов в первом случае). (уверен на 100%) И как Вы собрались выбирать ядро на котором процессу работать?

Про асинхронность, я думал и думаю, с одной стороны, да, она требует времени на свою поддержку. Но, во первых это удобней, дает больше контроля над задачами. Во вторых, она должна быть эффективней, т.к. умней умеет распределять задачи по потокам. Это можно проверить, - посчитать что нибудь используя threading в n-потоках и asyncio тоже для n-задач, и сравнить. С asyncio должно быть быстрей. (на 100% - не уверен, нужно проверить)

mihaild в сообщении #1348364 писал(а):
Не исполняются, а "исполняются". Т.е. одна задача может выполняться, пока другая ждет (ничего не делает). А ТС нужно чтобы несколько задач выполнялись одновременно. Хорошо видно, что заменить в ваших примерах async.sleep на time.sleep (и тем более на что-то cpu-intensive) и получить выигрыш по времени не получится.

Так потоки тоже можно запускать поочередно, запустить один и ждать пока он завершится, потом запускать следующий. asyncio - распределяет задачи как считает эффективней. Она быстрей работает чем без неё последовательно. Вот убрал задержки вообще:

код: [ скачать ] [ спрятать ]
Используется синтаксис Python
#!/usr/bin/env python3

import asyncio  
import time  
import random



from datetime import datetime


def custom_sleep():  
     print('SLEEP', datetime.now())
     time.sleep(1)    

def factorial(name, number):  
    f = 1
    for i in range(2, number+1):
#        print('Task {}: Compute factorial({})'.format(name, i))
        #custom_sleep()
        f *= i
#    print('Task {}: factorial({}) is {}\n'.format(name, number, f))


async def custom_sleep_async(asynchr=False):  
#     print('SLEEP', datetime.now())
     await asyncio.sleep(1)
 
#     await asyncio.sleep(random.randrange(1,5,1))
#     await time.sleep(1)

async def factorial_async(name, number):  
    f = 1
    for i in range(2, number+1):
   #     print('Task {}: Compute factorial({})'.format(name, i))
        #await custom_sleep()
        #await custom_sleep_async()
        f *= i

  #  print('Task {}: factorial({}) is {}\n'.format(name, number, f))

start = time.time()  
#не парралельно
print("\n------------------------\nstart without asyncio \n start time: {}".format(start))
factorial("A", 100000)
factorial("B", 200000)
end = time.time()
print("end {} ; Total time : {}\n".format(end,end-start))

loop = asyncio.get_event_loop()

#tasks = [  
#    asyncio.ensure_future(factorial("A", 3)),
#    asyncio.ensure_future(factorial("B", 4)),
#]
start = time.time()
#параллельно  
print("\n------------------------\nstart with asyncio \n start time: {}".format(start))
features=asyncio.gather(factorial_async("A", 100000),factorial_async("B", 200000))
#loop.run_until_complete(asyncio.wait(tasks))  
loop.run_until_complete(features)  

loop.close()

end = time.time()  
print("end {} ; Total time:{} \n".format(end,end-start))
 


разница в пять секунд

Используется синтаксис Text
------------------------
start without asyncio
 start time: 1540216199.8434155
end 1540216230.092785 ; Total time : 30.249369382858276


------------------------
start with asyncio
 start time: 1540216230.0933397
end 1540216255.4429772 ; Total time:25.349637508392334
 

 Профиль  
                  
 
 Re: Ускорение кода на Python
Сообщение22.10.2018, 17:23 
Супермодератор
Аватара пользователя


09/05/12
16582
Кронштадт
Seman в сообщении #1348376 писал(а):
Ну, так он же, мог бы разобраться по примерам и ссылкам как работать с asyncio , распределитель им свою задачу на задачи, и решить устраивает это его или нет.
Так очевидно же, что нет. На двух ядрах у вас все это будет работать секунд 15, а так вы упорно оптимизируете переключение на одном ядре (что, конечно, тоже может оказаться полезным, но во вторую очередь).

 Профиль  
                  
 
 Re: Ускорение кода на Python
Сообщение22.10.2018, 17:55 
Заслуженный участник
Аватара пользователя


16/07/14
2852
Москва
Seman в сообщении #1348376 писал(а):
разница в пять секунд
Погрешность измерения + разогрев.
Если сделать чуть более правильно:
код: [ скачать ] [ спрятать ]
Используется синтаксис Python
#!/usr/bin/env python3

import asyncio
import time
import multiprocessing

NS = [100000, 100000]

def factorial(number):
    f = 1
    for i in range(2, number+1):
        f *= i
    return f


async def factorial_async(number):
    f = 1
    for i in range(2, number+1):
        f *= i
    return f


def run_sync():
    return sum(map(factorial, NS))


def run_async():
    loop = asyncio.get_event_loop()
    features = asyncio.gather(*map(factorial_async, NS))
    result = loop.run_until_complete(features)
    return sum(result)


def run_parallel():
    p = multiprocessing.Pool(processes=2)
    return sum(p.map(factorial, NS))


def get_times(f):
    times = []
    for i in range(5):
        t = time.time()
        f()
        times.append(time.time() - t)
    times.sort()
    return times[2]


print("sync: %f" % (get_times(run_sync), ))
print("async: %f" % (get_times(run_async), ))
print("parallel: %f" % (get_times(run_parallel), ))
 
то получим
  1. sync: 5.465852 
  2. async: 5.150062 
  3. parallel: 2.867452 


Кстати, из-за GIL в питоне разбиение по потокам параллельности тоже не даст.

В любом случае, для ускорения кода, который реально что-то делает (а не просто ждет), нужно чтобы несколько ядер были загружены одновременно. asyncio этого не обеспечивает (что легко видеть если посмотреть на использование CPU скриптом выше).

 Профиль  
                  
 
 Re: Ускорение кода на Python
Сообщение24.10.2018, 12:00 


27/11/10
200
Seman, вы путаете параллельность и конкурентность. В дополнение к этому, необходимо понимать как устроен GIL в Python. CPU-bound задачи никогда не ускорить конкурентностью. Конкурентное выполнение представлено в современном Python asyncio, модуль threading предлагает системные потоки, но их выполнение зависит от интерпретатора Python. В СPython есть GIL, поэтому одновременно может выполняться только один поток (в итоге можно думать как о конкурентном выполнении), а вот в Jython полноценные параллельные потоки от JVM. Чтобы как-то преодолеть GIL есть модуль multiprocessing, как упоминал mihaild выше. Но создание новых процессов достаточно затратно по ресурсам, поэтому простые задачи лучше в них не отправлять.

 Профиль  
                  
 
 Re: Ускорение кода на Python
Сообщение27.11.2018, 16:35 


20/10/12
231
Пишу с опозданием, но в итоге Numba значительно помогла и профилирование (aka profile-guided optimization).
На данном этапе в дальнейшем ускорении нет необходимости :-)
Всем огромное спасибо!

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 23 ]  На страницу Пред.  1, 2

Модераторы: Karan, PAV, Toucan, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group