10

I have created a (rather large) program that takes quite a long time to finish, and I started looking into ways to speed up the program.

I found that if I open task manager while the program is running only one core is being used.

After some research, I found this website: Why does multiprocessing use only a single core after I import numpy? which gives a solution of os.system("taskset -p 0xff %d" % os.getpid()), however this doesn't work for me, and my program continues to run on a single core.

I then found this: is python capable of running on multiple cores?, which pointed towards using multiprocessing.

So after looking into multiprocessing, I came across this documentary on how to use it https://docs.python.org/3/library/multiprocessing.html#examples

I tried the code:

from multiprocessing import Process

def f(name):
    print('hello', name)

if __name__ == '__main__':
    p = Process(target=f, args=('bob',))
    p.start()
    p.join()

a = input("Finished")

After running the code (not in IDLE) It said this:

Finished
hello bob
Finished

Note: after it said Finished the first time I pressed enter

So after this I am now even more confused and I have two questions

First: It still doesn't run with multiple cores (I have an 8 core Intel i7)

Second: Why does it input "Finished" before its even run the if statement code (and it's not even finished yet!)

Rlz
  • 1,649
  • 2
  • 13
  • 36
  • Yes, because you only have a single process, so it will only use a single core. – juanpa.arrivillaga Aug 25 '17 at 18:43
  • Also, are you using `numpy`? Because then that link doesn't seem relevant. – juanpa.arrivillaga Aug 25 '17 at 18:43
  • So how would I use multiple cores in the situation of having to do lots of multiplying / subtracting in a for statement? e.g. `for each in range(1000): a = a*each` – Rlz Aug 25 '17 at 18:46
  • How are you testing/verifying that is running on multiple cores? – Preston Martin Aug 25 '17 at 18:46
  • @juanpa.arrivillaga no I'm not using numpy but I was saying that it didn't work in my scenario – Rlz Aug 25 '17 at 18:47
  • @PrestonM using Task Manager in details it shows the cpu % and amount of processes – Rlz Aug 25 '17 at 18:49
  • Why is your code slow to begin with? More often than not, you can get a 100x+ optimization with better code compared to a 4x improvement and additional complexities with multiprocessing. – Alexander Aug 25 '17 at 18:49
  • @Alexander It is not the fault of the code it is running as optimally as (as far as a I know) possible. It is a hard task and therefore is not easy to solve making it take a long time. Interesting fact though :) – Rlz Aug 25 '17 at 18:51
  • Is it slow because of bottlenecks such as network/database/disk access issues, or because of computation? If the latter, you are probably not doing it efficiently (especially if you are not using pandas/numpy). – Alexander Aug 25 '17 at 18:53
  • @Alexander It's because of the computation. The ram, disk etc is fine – Rlz Aug 25 '17 at 18:54
  • Use a profiler to understand what is slow, then focus on optimizing that. https://stackoverflow.com/questions/582336/how-can-you-profile-a-script – Alexander Aug 25 '17 at 18:56
  • @Alexander ok thanks I will use that to to speed up / optimise my code. Do you think for now you could post an answer to how to use all 8 cores? Then it should be 8x faster, and with the supposed 100x faster code optimisation that makes it 800x faster! Wow! – Rlz Aug 25 '17 at 18:58
  • Is `a = input("Finished")` nested within your `if` statement, our outside it? Also, refer to this for examples: https://pymotw.com/2/multiprocessing/basics.html – Alexander Aug 25 '17 at 19:13
  • @Alexander Sorry for the late reply but my actual code that I'm using is more like this: def start(): asks for inputs inputs goes to def race(): does maths goes to def finish(): tells you how you did (output). Note: at the end of all the functions it says start() which starts the first function. It is the race function that I am trying to use multiple cores for – Rlz Aug 25 '17 at 21:43
  • The reason I asked is to check if 1) you are declaring a module level constant named `a`, in which case you will be required to type your input when the module is *first* loaded, or 2) it was just an indentation error in your code snipper. If the former, you should put it at the top of your module immediately below the imports. Because the module loads before `main` starts, it first asks you for this input before running main. – Alexander Aug 25 '17 at 21:48
  • @Alexander Thanks you have solved the second problem :) but there is still one to go! Could you please post this as an answer? An implementation into my current code would help (With the race function etc) – Rlz Aug 25 '17 at 21:55
  • @Alexander By the way the a = input() was to stop the code when it was finished, otherwise it just finishes in console and closes. Seems like you still have two questions to answer again ;) – Rlz Aug 25 '17 at 22:00
  • indent it with your other code block from main. – Alexander Aug 25 '17 at 22:01
  • @Alexander Oh yes makes sense now. Can't believe I missed that! As for the implementation in the code, could you post that and the other answer as an actual answer? These comments are going on quite a lot now haha. Then I can also mark it as the answer and up vote. Thanks for your help! – Rlz Aug 25 '17 at 22:07
  • Will do in a couple of hours. – Alexander Aug 25 '17 at 22:08

1 Answers1

25

To answer your second question first, "Finished" is printed to the terminal because a = input("Finished") is outside of your if __name__ == '__main__': code block. It is thus a module level constant which gets assigned when the module is first loaded and will execute before any code in the module runs.

To answer the first question, you only created one process which you run and then wait to complete before continuing. This gives you zero benefits of multiprocessing and incurs overhead of creating the new process.

Because you want to create several processes, you need to create a pool via a collection of some sort (e.g. a python list) and then start all of the processes.

In practice, you need to be concerned with more than the number of processors (such as the amount of available memory, the ability to restart workers that crash, etc.). However, here is a simple example that completes your task above.

import datetime as dt
from multiprocessing import Process, current_process
import sys

def f(name):
    print('{}: hello {} from {}'.format(
        dt.datetime.now(), name, current_process().name))
    sys.stdout.flush()

if __name__ == '__main__':
    worker_count = 8
    worker_pool = []
    for _ in range(worker_count):
        p = Process(target=f, args=('bob',))
        p.start()
        worker_pool.append(p)
    for p in worker_pool:
        p.join()  # Wait for all of the workers to finish.

    # Allow time to view results before program terminates.
    a = input("Finished")  # raw_input(...) in Python 2.

Also note that if you join workers immediately after starting them, you are waiting for each worker to complete its task before starting the next worker. This is generally undesirable unless the ordering of the tasks must be sequential.

Typically Wrong

worker_1.start()
worker_1.join()

worker_2.start()  # Must wait for worker_1 to complete before starting worker_2.
worker_2.join()

Usually Desired

worker_1.start()
worker_2.start()  # Start all workers.

worker_1.join()
worker_2.join()   # Wait for all workers to finish.

For more information, please refer to the following links:

Alexander
  • 105,104
  • 32
  • 201
  • 196