Parallel processing with no deep meaning in Python

When solving a code puzzle on a site, I couldn't think of a solution at all, so I decided to brute force the forbidden.

The problem is to find m such that f (m) == c for the given function f and constant c.

First, simply

The result of implementing without thinking for the time being is the following code.

solution1


import sys

def solve(m):
    # f,c is defined
    return f(m) == c

def solution1():
    limit = int(sys.argv[1])
    ans = [m for m in range(limit) if solve(m)]
    print ans

I wondered how long the processing time would take, so I prepared a profiler.

profile


import cProfile

if __name__ == '__main__':
    profiler = cProfile.Profile()
    profiler.runcall(solution1)
    profiler.print_stats()

And run.

solution1_profile_result


% python mp.py 100000000

(The solution is omitted)

200000003 function calls in 333.009 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
100000000   78.739    0.000  299.934    0.000 mp.py:12(solve)
        1   30.856   30.856  333.009  333.009 mp.py:31(solution1)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
100000000  221.195    0.000  221.195    0.000 {pow}
        1    2.219    2.219    2.219    2.219 {range}

Fanned, parallel, and ...

When I was wondering if I knew the answer for the time being, I heard a voice from the side saying, "Why don't you parallelize www bufefe www" (Note: The remarks are distorted by my malicious intent). While thinking, "By the way, I haven't investigated the parallel processing of Python," I will implement a parallelized version.

solution2


import math
import sys
from multiprocessing import Process, Queue


def task(q, num_range):
    #To pass data to the main side via a queue,There is no essential meaning in this program.
    #Mirita Cutter Dagger
    q.put([m for m in num_range if solve(m)])


def solution2():
    queue = Queue()
    limit = int(sys.argv[1])
    worker_num = int(sys.argv[2])
    task_size = int(math.ceil(float(limit) / float(worker_num)))

    #Split the list to process,Pass to each process.
    # ref. http://iogi.hatenablog.com/entry/split-list-into-sublist
    num_ranges = izip_longest(*[iter(range(limit))]*task_size)
    processes = [Process(group=None, target=task, args=(queue, num_range))
                 for num_range in num_ranges]

    for process in processes:
        process.start()

    for process in processes:
        print queue.get()
        process.join()

And, the processing time is almost half.

solution2_profile_result


% python mp.py 100000000 4

(The solution is omitted)

1021 function calls (1018 primitive calls) in 182.498 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 __future__.py:48(<module>)
        1    0.000    0.000    0.000    0.000 __future__.py:74(_Feature)
        7    0.000    0.000    0.000    0.000 __future__.py:75(__init__)
        1    0.005    0.005    0.042    0.042 __init__.py:102(Pipe)
        1    0.004    0.004    0.084    0.084 __init__.py:213(Queue)
        1    0.000    0.000    0.000    0.000 connection.py:117(Listener)
        1    0.000    0.000    0.000    0.000 connection.py:183(Pipe)
        1    0.000    0.000    0.000    0.000 connection.py:246(SocketListener)
        1    0.013    0.013    0.037    0.037 connection.py:35(<module>)
        1    0.000    0.000    0.000    0.000 connection.py:426(ConnectionWrapper)
        1    0.000    0.000    0.000    0.000 connection.py:448(XmlListener)
        1    0.000    0.000    0.000    0.000 forking.py:113(Popen)
        4    0.000    0.000    0.027    0.007 forking.py:115(__init__)
       (Abbreviation)

Attack on Titan memory

The previous implementation uses the range function to generate a list when splitting a list. Therefore, the memory usage is great.

solution2.png

This problem was solved by using the xrange function instead of the range function. The xrange function returns an iterator instead of a list, so it doesn't consume memory all at once.

solution3


import math
import sys
from multiprocessing import Process, Queue


def solution3():
    queue = Queue()
    limit = int(sys.argv[1])
    worker_num = int(sys.argv[2])
    task_size = int(math.ceil(float(limit) / float(worker_num)))

    gen = xrange(limit)
    processes = [Process(
                 group=None,
                 target=task,
                 args=(queue, islice(gen, task_size * i, task_size * (i + 1))))
                 for i in xrange(worker_num)]

    for process in processes:
        process.start()

    for process in processes:
        print queue.get()
        process.join()

So, execution + memory consumption at that time. The execution time has hardly changed, but the memory consumption has decreased dramatically.

solution3_profile_result


% python mp.py 100000000 4

(The solution is omitted)

1019 function calls (1016 primitive calls) in 187.432 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 __future__.py:48(<module>)
        1    0.000    0.000    0.000    0.000 __future__.py:74(_Feature)
        7    0.000    0.000    0.000    0.000 __future__.py:75(__init__)
        1    0.006    0.006    0.047    0.047 __init__.py:102(Pipe)
        1    0.005    0.005    0.082    0.082 __init__.py:213(Queue)
        1    0.000    0.000    0.000    0.000 connection.py:117(Listener)
        1    0.000    0.000    0.000    0.000 connection.py:183(Pipe)
        1    0.000    0.000    0.000    0.000 connection.py:246(SocketListener)
        1    0.015    0.015    0.041    0.041 connection.py:35(<module>)
        1    0.000    0.000    0.000    0.000 connection.py:426(ConnectionWrapper)
        1    0.000    0.000    0.000    0.000 connection.py:448(XmlListener)
        1    0.000    0.000    0.000    0.000 forking.py:113(Popen)
        4    0.000    0.000    0.001    0.000 forking.py:115(__init__)
       10    0.000    0.000    0.000    0.000 forking.py:130(poll)
        4    0.000    0.000    0.000    0.000 forking.py:146(wait)
        1    0.014    0.014    0.019    0.019 forking.py:35(<module>)
       (Abbreviation)

solution3.png

(Supplement) Difference of range function in Python2 / 3

In Python3, the range function returns an iterator called the range class, so the above correspondence is unnecessary. Rather, the xrange function is obsolete.

// Python3
% python3
  Python 3.3.0 (default, Mar  2 2013, 11:44:00)
  [GCC 4.2.1 Compatible Apple LLVM 4.2 (clang-425.0.24)] on darwin
  Type "help", "copyright", "credits" or "license" for more information.
  >>> a = range(5)
  >>> a
  range(0, 5)
  >>> type(a)
  <class 'range'>
  >>>
  >>> xrange(5)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    NameError: name 'xrange' is not defined

// Python2
% python
  Python 2.7.3 (default, May 14 2012, 22:41:33)
  [GCC 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.1.00)] on darwin
  Type "help", "copyright", "credits" or "license" for more information.
  >>> a = range(5)
  >>> a
  [0, 1, 2, 3, 4]
  >>> type(a)
  <type 'list'>
  >>>

Recommended Posts

Parallel processing with no deep meaning in Python
[Python] Easy parallel processing with Joblib
Read files in parallel with Python
Easy image processing in Python with Pillow
File processing in Python
How to do multi-core parallel processing with python
Multithreaded processing in python
Parallel download in Python
Creating an exe file with Python PyInstaller: PC freezes in parallel processing
Queue processing in Python
Python: Deep Learning in Natural Language Processing: Basics
Image processing with Python
Receive a list of the results of parallel processing in Python with starmap
Parallel processing with multiprocessing
An introduction to Python distributed parallel processing with Ray
Image processing with Python (Part 2)
100 Language Processing with Python Knock 2015
UTF8 text processing in python
Parallel processing with local functions
Scraping with selenium in Python
"Apple processing" with OpenCV3 + Python3
Working with LibreOffice in Python
Scraping with chromedriver in python
Debugging with pdb in Python
Run Python unittests in parallel
Acoustic signal processing with Python (2)
Acoustic signal processing with Python
Working with sounds in Python
Scraping with Selenium in Python
Asynchronous processing (threading) in python
Parallel processing with Parallel of scikit-learn
Image processing with Python (Part 1)
Scraping with Tor in Python
Tweet with image in Python
Combined with permutations in Python
Image processing with Python (Part 3)
Image Processing Collection in Python
Using Python mode in Processing
[Python] Image processing with scikit-image
Number recognition in images with Python
Testing with random numbers in Python
Python parallel processing (multiprocessing and Joblib)
GOTO in Python with Sublime Text 3
Working with LibreOffice in Python: import
100 Language Processing Knock with Python (Chapter 1)
Scraping with Selenium in Python (Basic)
100 Language Processing Knock Chapter 1 in Python
CSS parsing with cssutils in Python
Numer0n with items made in Python
Open UTF-8 with BOM in Python
100 Language Processing Knock with Python (Chapter 3)
Image processing with Python 100 knocks # 3 Binarization
Use rospy with virtualenv in Python3
Use Python in pyenv with NeoVim
There is no switch in python
Python parallel / parallel processing sample code summary
Heatmap with Dendrogram in Python + matplotlib
Password generation in texto with python
Use OpenCV with Python 3 in Window
Image processing with Python 100 knocks # 2 Grayscale
Until dealing with python in Atom