ವಿಷಯಕ್ಕೆ ತೆರಳಿ

ಮಲ್ಟಿಪ್ರೊಸೆಸಿಂಗ್ (Multiprocessing)

ಮಲ್ಟಿಪ್ರೊಸೆಸಿಂಗ್ ಎನ್ನುವುದು ಹಲವು ಪ್ರೊಸೆಸ್‌ಗಳನ್ನು (processes) ಏಕಕಾಲದಲ್ಲಿ ಚಲಾಯಿಸುವ ಒಂದು ತಂತ್ರ. ಪ್ರತಿ ಪ್ರೊಸೆಸ್‌ಗೂ ತನ್ನದೇ ಆದ ಮೆಮೊರಿ ಸ್ಪೇಸ್ ಮತ್ತು ಪೈಥಾನ್ ಇಂಟರ್‌ಪ್ರಿಟರ್ ಇರುತ್ತದೆ. ಇದು CPU-ಬೌಂಡ್ (ಗಣಿತದ ಲೆಕ್ಕಾಚಾರಗಳಂತಹ) ಕಾರ್ಯಗಳಿಗೆ ಅತ್ಯಂತ ಉಪಯುಕ್ತವಾಗಿದೆ, ಏಕೆಂದರೆ ಇದು ಗ್ಲೋಬಲ್ ಇಂಟರ್‌ಪ್ರಿಟರ್ ಲಾಕ್ (GIL) ನ ಮಿತಿಯನ್ನು ಮೀರಿ, ನಿಜವಾದ ಸಮಾನಾಂತರತೆಯನ್ನು (true parallelism) ಸಾಧಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ.

CPU-ಬೌಂಡ್ ಕಾರ್ಯಗಳು: ಸಂಕೀರ್ಣ ಗಣಿತದ ಲೆಕ್ಕಾಚಾರಗಳು, ಡೇಟಾ ವಿಶ್ಲೇಷಣೆ, ಇಮೇಜ್ ಪ್ರೊಸೆಸಿಂಗ್, ವಿಡಿಯೋ ಎನ್‌ಕೋಡಿಂಗ್ ಮುಂತಾದ ಕಾರ್ಯಗಳು. ಈ ಸಂದರ್ಭಗಳಲ್ಲಿ, ಪ್ರೊಸೆಸರ್ ನಿರಂತರವಾಗಿ ಕೆಲಸ ಮಾಡುತ್ತದೆ.

ಪೈಥಾನ್‌ನಲ್ಲಿ ಮಲ್ಟಿಪ್ರೊಸೆಸಿಂಗ್

ಪೈಥಾನ್‌ನಲ್ಲಿ ಮಲ್ಟಿಪ್ರೊಸೆಸಿಂಗ್ ಅನ್ನು multiprocessing ಮಾಡ್ಯೂಲ್ ಬಳಸಿ ಕಾರ್ಯಗತಗೊಳಿಸಲಾಗುತ್ತದೆ. ಇದರ API threading ಮಾಡ್ಯೂಲ್ ಅನ್ನು ಹೋಲುತ್ತದೆ, ಇದು ಬಳಕೆಯನ್ನು ಸುಲಭಗೊಳಿಸುತ್ತದೆ.

ಉದಾಹರಣೆ: ಒಂದು ದೊಡ್ಡ ಸಂಖ್ಯೆಯವರೆಗಿನ ವರ್ಗಗಳನ್ನು (squares) ಲೆಕ್ಕಾಚಾರ ಮಾಡುವ ಕಾರ್ಯವನ್ನು ಹಲವು ಪ್ರೊಸೆಸ್‌ಗಳಲ್ಲಿ ಹಂಚಿ ಚಲಾಯಿಸೋಣ.

import multiprocessing
import time

def calculate_square(numbers, result_queue):
    """ಸಂಖ್ಯೆಗಳ ವರ್ಗವನ್ನು ಲೆಕ್ಕಾಚಾರ ಮಾಡುವ ಫಂಕ್ಷನ್"""
    process_name = multiprocessing.current_process().name
    print(f"ಪ್ರೊಸೆಸ್ {process_name}: ಪ್ರಾರಂಭವಾಯಿತು")
    for n in numbers:
        result_queue.put((n, n * n))
    print(f"ಪ್ರೊಸೆಸ್ {process_name}: ಮುಗಿಯಿತು")

if __name__ == "__main__":
    numbers_to_process = range(1, 11)

    # ಪ್ರೊಸೆಸ್‌ಗಳ ನಡುವೆ ಡೇಟಾ ಹಂಚಿಕೊಳ್ಳಲು ಕ್ಯೂ (Queue)
    result_queue = multiprocessing.Queue()

    # ಪ್ರೊಸೆಸ್‌ಗಳನ್ನು ರಚಿಸುವುದು
    p1 = multiprocessing.Process(target=calculate_square, args=(numbers_to_process[:5], result_queue), name="P1")
    p2 = multiprocessing.Process(target=calculate_square, args=(numbers_to_process[5:], result_queue), name="P2")

    start_time = time.time()

    # ಪ್ರೊಸೆಸ್‌ಗಳನ್ನು ಪ್ರಾರಂಭಿಸುವುದು
    p1.start()
    p2.start()

    # ಪ್ರೊಸೆಸ್‌ಗಳು ಮುಗಿಯುವವರೆಗೆ ಕಾಯುವುದು
    p1.join()
    p2.join()

    end_time = time.time()

    print(f"\nಎಲ್ಲಾ ಪ್ರೊಸೆಸ್‌ಗಳು {end_time - start_time:.4f} ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮುಗಿದಿವೆ.")

    # ಕ್ಯೂನಿಂದ ಫಲಿತಾಂಶಗಳನ್ನು ಪಡೆಯುವುದು
    print("ಫಲಿತಾಂಶಗಳು:")
    while not result_queue.empty():
        num, square = result_queue.get()
        print(f"{num} ರ ವರ್ಗ: {square}")
ಔಟ್‌ಪುಟ್ (ಅಂದಾಜು):
ಪ್ರೊಸೆಸ್ P1: ಪ್ರಾರಂಭವಾಯಿತು
ಪ್ರೊಸೆಸ್ P2: ಪ್ರಾರಂಭವಾಯಿತು
ಪ್ರೊಸೆಸ್ P1: ಮುಗಿಯಿತು
ಪ್ರೊಸೆಸ್ P2: ಮುಗಿಯಿತು

ಎಲ್ಲಾ ಪ್ರೊಸೆಸ್‌ಗಳು 0.0781 ಸೆಕೆಂಡುಗಳಲ್ಲಿ ಮುಗಿದಿವೆ.
ಫಲಿತಾಂಶಗಳು:
1 ರ ವರ್ಗ: 1
2 ರ ವರ್ಗ: 4
3 ರ ವರ್ಗ: 9
4 ರ ವರ್ಗ: 16
5 ರ ವರ್ಗ: 25
6 ರ ವರ್ಗ: 36
7 ರ ವರ್ಗ: 49
8 ರ ವರ್ಗ: 64
9 ರ ವರ್ಗ: 100
10 ರ ವರ್ಗ: 121
ಗಮನಿಸಿ: if __name__ == "__main__": ಬ್ಲಾಕ್ ಬಳಸುವುದು ಅತ್ಯಗತ್ಯ. ಏಕೆಂದರೆ, ಪ್ರತಿ ಚೈಲ್ಡ್ ಪ್ರೊಸೆಸ್ ಕೂಡ ಸ್ಕ್ರಿಪ್ಟ್ ಅನ್ನು ಇಂಪೋರ್ಟ್ ಮಾಡುತ್ತದೆ. ಈ ಬ್ಲಾಕ್ ಇಲ್ಲದಿದ್ದರೆ, ಚೈಲ್ಡ್ ಪ್ರೊಸೆಸ್‌ಗಳು ಮತ್ತೆ ಹೊಸ ಪ್ರೊಸೆಸ್‌ಗಳನ್ನು ರಚಿಸಲು ಪ್ರಯತ್ನಿಸುತ್ತವೆ, ಇದು ಅನಂತ ಲೂಪ್‌ಗೆ ಕಾರಣವಾಗುತ್ತದೆ.

ಬಳಕೆಯ ಸಂದರ್ಭಗಳು (Use Cases)

  1. ಡೇಟಾ ಪ್ರೊಸೆಸಿಂಗ್: ದೊಡ್ಡ ಡೇಟಾಸೆಟ್‌ಗಳನ್ನು ಭಾಗಗಳಾಗಿ ವಿಂಗಡಿಸಿ, ಪ್ರತಿ ಭಾಗವನ್ನು ಪ್ರತ್ಯೇಕ ಪ್ರೊಸೆಸ್‌ನಲ್ಲಿ ಸಂಸ್ಕರಿಸುವುದು.
  2. ವೈಜ್ಞಾನಿಕ ಲೆಕ್ಕಾಚಾರಗಳು: ಸಂಕೀರ್ಣ ಸಿಮ್ಯುಲೇಶನ್‌ಗಳು ಮತ್ತು ಮಾಡೆಲಿಂಗ್.
  3. ಇಮೇಜ್ ಮತ್ತು ವಿಡಿಯೋ ಪ್ರೊಸೆಸಿಂಗ್: ಫಿಲ್ಟರ್‌ಗಳನ್ನು ಅನ್ವಯಿಸುವುದು, ಫ್ರೇಮ್‌ಗಳನ್ನು ಸಂಸ್ಕರಿಸುವುದು.
  4. ಸಮಾನಾಂತರ ಪರೀಕ್ಷೆ (Parallel Testing): ಹಲವು ಟೆಸ್ಟ್ ಕೇಸ್‌ಗಳನ್ನು ಏಕಕಾಲದಲ್ಲಿ ಚಲಾಯಿಸುವುದು.

ಮಿತಿಗಳು ಮತ್ತು ಸವಾಲುಗಳು

1. ಮೆಮೊರಿ ಬಳಕೆ

ಪ್ರತಿ ಪ್ರೊಸೆಸ್‌ಗೂ ತನ್ನದೇ ಆದ ಮೆಮೊರಿ ಸ್ಪೇಸ್ ಇರುವುದರಿಂದ, ಮಲ್ಟಿಪ್ರೊಸೆಸಿಂಗ್ ಮಲ್ಟಿಥ್ರೆಡಿಂಗ್‌ಗಿಂತ ಹೆಚ್ಚು ಮೆಮೊರಿಯನ್ನು ಬಳಸುತ್ತದೆ. ದೊಡ್ಡ ಸಂಖ್ಯೆಯ ಪ್ರೊಸೆಸ್‌ಗಳನ್ನು ರಚಿಸುವುದು ಸಿಸ್ಟಮ್ ಸಂಪನ್ಮೂಲಗಳ ಮೇಲೆ ಹೆಚ್ಚು ಹೊರೆಯಾಗಬಹುದು.

2. ಪ್ರೊಸೆಸ್‌ಗಳ ನಡುವಿನ ಸಂವಹನ (Inter-Process Communication - IPC)

ಪ್ರೊಸೆಸ್‌ಗಳು ತಮ್ಮ ಮೆಮೊರಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳುವುದಿಲ್ಲವಾದ್ದರಿಂದ, ಅವುಗಳ ನಡುವೆ ಡೇಟಾವನ್ನು ಹಂಚಿಕೊಳ್ಳುವುದು ಸಂಕೀರ್ಣವಾಗಿರುತ್ತದೆ. ಇದನ್ನು "Inter-Process Communication (IPC)" ಎನ್ನುತ್ತಾರೆ.

multiprocessing ಮಾಡ್ಯೂಲ್ IPC ಗಾಗಿ ಕೆಲವು ಸಾಧನಗಳನ್ನು ಒದಗಿಸುತ್ತದೆ: * ಕ್ಯೂ (Queue): ಪ್ರೊಸೆಸ್‌ಗಳ ನಡುವೆ ಸಂದೇಶಗಳನ್ನು (ಡೇಟಾ) ಕಳುಹಿಸಲು ಬಳಸಲಾಗುತ್ತದೆ. ಇದು ಪ್ರೊಸೆಸ್-ಸೇಫ್ ಆಗಿದೆ. * ಪೈಪ್ (Pipe): ಎರಡು ಪ್ರೊಸೆಸ್‌ಗಳ ನಡುವೆ ದ್ವಿಮುಖ ಸಂವಹನಕ್ಕೆ ಬಳಸಲಾಗುತ್ತದೆ. * ಹಂಚಿಕೆಯಾದ ಮೆಮೊರಿ (Shared Memory): Value ಮತ್ತು Array ಬಳಸಿ, ಪ್ರೊಸೆಸ್‌ಗಳ ನಡುವೆ ಸರಳ ಡೇಟಾ ಪ್ರಕಾರಗಳನ್ನು ಹಂಚಿಕೊಳ್ಳಬಹುದು.

ಈ IPC ಮೆಕ್ಯಾನಿಸಮ್‌ಗಳು ಡೇಟಾವನ್ನು ಸೀರಿಯಲೈಸ್ (serialize) ಮತ್ತು ಡಿಸೀರಿಯಲೈಸ್ (deserialize) ಮಾಡಬೇಕಾಗುತ್ತದೆ (ಇದನ್ನು "pickling" ಎನ್ನುತ್ತಾರೆ), ಇದು ಸ್ವಲ್ಪ ಓವರ್‌ಹೆಡ್ ಅನ್ನು ಸೇರಿಸುತ್ತದೆ.

3. ಪ್ರೊಸೆಸ್ ರಚನೆಯ ಓವರ್‌ಹೆಡ್

ಹೊಸ ಪ್ರೊಸೆಸ್ ಅನ್ನು ರಚಿಸುವುದು, ಹೊಸ ಥ್ರೆಡ್ ಅನ್ನು ರಚಿಸುವುದಕ್ಕಿಂತ ಹೆಚ್ಚು ಸಮಯ ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ. ಏಕೆಂದರೆ, ಆಪರೇಟಿಂಗ್ ಸಿಸ್ಟಮ್ ಪ್ರತಿ ಪ್ರೊಸೆಸ್‌ಗೆ ಪ್ರತ್ಯೇಕ ಮೆಮೊರಿ ಸ್ಪೇಸ್ ಮತ್ತು ಇತರ ಸಂಪನ್ಮೂಲಗಳನ್ನು ಹಂಚಿಕೆ ಮಾಡಬೇಕಾಗುತ್ತದೆ. ಆದ್ದರಿಂದ, ತುಂಬಾ ಚಿಕ್ಕ ಕಾರ್ಯಗಳಿಗೆ ಮಲ್ಟಿಪ್ರೊಸೆಸಿಂಗ್ ಬಳಸುವುದು ಅಸಮರ್ಥವಾಗಬಹುದು.

ಮಲ್ಟಿಥ್ರೆಡಿಂಗ್ vs ಮಲ್ಟಿಪ್ರೊಸೆಸಿಂಗ್

ವೈಶಿಷ್ಟ್ಯ ಮಲ್ಟಿಥ್ರೆಡಿಂಗ್ (Multithreading) ಮಲ್ಟಿಪ್ರೊಸೆಸಿಂಗ್ (Multiprocessing)
ಸಮಾನಾಂತರತೆ ಸಮಕಾಲೀನತೆ (Concurrency), ನಿಜವಾದ ಸಮಾನಾಂತರತೆ ಇಲ್ಲ (GIL ನಿಂದಾಗಿ). ನಿಜವಾದ ಸಮಾನಾಂತರತೆ (True Parallelism).
ಮೆಮೊರಿ ಒಂದೇ ಮೆಮೊರಿ ಸ್ಪೇಸ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತವೆ. ಕಡಿಮೆ ಮೆಮೊರಿ ಬಳಕೆ. ಪ್ರತಿ ಪ್ರೊಸೆಸ್‌ಗೂ ಪ್ರತ್ಯೇಕ ಮೆಮೊರಿ ಸ್ಪೇಸ್. ಹೆಚ್ಚು ಮೆಮೊರಿ ಬಳಕೆ.
ಡೇಟಾ ಹಂಚಿಕೆ ಸುಲಭ, ಆದರೆ ರೇಸ್ ಕಂಡೀಷನ್‌ಗಳನ್ನು ತಡೆಯಲು ಲಾಕ್‌ಗಳು ಬೇಕು. ಕಷ್ಟ. IPC (ಕ್ಯೂ, ಪೈಪ್) ಮೆಕ್ಯಾನಿಸಮ್‌ಗಳು ಬೇಕು.
ಉತ್ತಮವಾದದ್ದು I/O-ಬೌಂಡ್ ಕಾರ್ಯಗಳಿಗೆ (ನೆಟ್‌ವರ್ಕಿಂಗ್, ಫೈಲ್ I/O). CPU-ಬೌಂಡ್ ಕಾರ್ಯಗಳಿಗೆ (ಗಣಿತ, ಡೇಟಾ ಪ್ರೊಸೆಸಿಂಗ್).
ರಚನೆಯ ಓವರ್‌ಹೆಡ್ ಕಡಿಮೆ. ಹೆಚ್ಚು.
ದೋಷ ನಿರ್ವಹಣೆ ಒಂದು ಥ್ರೆಡ್ ಕ್ರ್ಯಾಶ್ ಆದರೆ, ಇಡೀ ಪ್ರೊಸೆಸ್ ಕ್ರ್ಯಾಶ್ ಆಗಬಹುದು. ಒಂದು ಪ್ರೊಸೆಸ್ ಕ್ರ್ಯಾಶ್ ಆದರೆ, ಇತರ ಪ್ರೊಸೆಸ್‌ಗಳ ಮೇಲೆ ಪರಿಣಾಮ ಬೀರದು.

ಯಾವಾಗ ಯಾವುದನ್ನು ಬಳಸಬೇಕು?

  • ನಿಮ್ಮ ಕಾರ್ಯವು ಹೆಚ್ಚಾಗಿ ಬಾಹ್ಯ ಸಂಪನ್ಮೂಲಗಳಿಗಾಗಿ (ನೆಟ್‌ವರ್ಕ್, ಡಿಸ್ಕ್) ಕಾಯುತ್ತಿದ್ದರೆ, ಮಲ್ಟಿಥ್ರೆಡಿಂಗ್ ಬಳಸಿ.
  • ನಿಮ್ಮ ಕಾರ್ಯವು ಹೆಚ್ಚಾಗಿ ಪ್ರೊಸೆಸರ್ ಅನ್ನು ಬಳಸುತ್ತಿದ್ದರೆ (ಭಾರೀ ಲೆಕ್ಕಾಚಾರಗಳು), ಮಲ್ಟಿಪ್ರೊಸೆಸಿಂಗ್ ಬಳಸಿ.