Python Multiprocessing: CPU-Bound Tasks සඳහා සමාන්තරකරණය SC Guide

Python Multiprocessing: CPU-Bound Tasks සඳහා සමාන්තරකරණය SC Guide

Python Concurrency: Mastering Multiprocessing for Performance SC Guide

ඔබ Python වල වැඩ කරන developer කෙනෙක් නම්, කවදා හරි ඔබේ program එකක් හරි task එකක් හරි ගොඩක් වෙලා යනවා කියලා හිතලා ඇති නේද? විශේෂයෙන්ම, ගණනය කිරීම් ගොඩක් තියෙන (CPU-bound) වැඩ කරනකොට, program එක run වෙන speed එක මදි කියලා හිතුන වෙලාවල් ඇති. මේකට solution එකක් තමයි Concurrency නැත්නම් Parallelism කියන්නේ. ඒ කියන්නේ එකම වෙලාවේ වැඩ කිහිපයක් එකට run කිරීම.

Python වල Concurrency ගැන කතා කරනකොට, threads ගැන වගේම processes ගැනත් කතා කරන්න වෙනවා. Threads වලට වඩා processes කියන එක CPU-bound tasks වලට ගොඩක් වැදගත් වෙන්නේ Python වල තියෙන Global Interpreter Lock (GIL) කියන දේ නිසා. GIL එක නිසා එක වෙලාවකදී එක thread එකකට විතරයි Python bytecode execute කරන්න පුළුවන් වෙන්නේ. ඒත් processes වලදී මේ GIL එක bypass කරන්න පුළුවන්. ඒ කියන්නේ ඔබේ computer එකේ තියෙන multiple CPU cores වල සම්පූර්ණ බලයම ගන්න processes වලට පුළුවන්.

මේ blog post එකෙන් අපි Python වල `multiprocessing` module එක ගැන ගැඹුරින් ඉගෙන ගමු. මොකද මේක තමයි Python වල CPU-bound tasks වලට true parallelism දෙන්න පුළුවන් ප්‍රධානම ක්‍රමය. අපි බලමු processes හදාගන්නේ කොහොමද, ඒවා අතර data share කරගන්නේ කොහොමද, මේවා භාවිතා කරනකොට එන challenges මොනවද, සහ හොඳම පුරුදු මොනවද කියලා. එහෙනම්, Python code වලට speed එකක් දෙන්න ලෑස්ති වෙමු!

1. Concurrency (සමාන්තරකරණය) යනු කුමක්ද? Threads vs. Processes

අපි මුලින්ම බලමු Concurrency කියන්නේ මොකක්ද කියලා. සරලව කිව්වොත්, Concurrency කියන්නේ වැඩ කිහිපයක් එකවර සිදුකරනවා වගේ පෙනීම. Parallelism කියන්නේ ඇත්තටම එකම වෙලාවේ වැඩ කිහිපයක් සිදුකිරීම. උදාහරණයක් විදිහට, එක කොළුවෙක් බෝල දෙකක් අහසට දානකොට ඒක Concurrency. බෝල දෙකක් අහසට දාන්න කොල්ලෝ දෙන්නෙක් ඉන්නවනම් ඒක Parallelism.

Python වල Concurrency ආකාර දෙකක් තියෙනවා: Threads සහ Processes.

Threads (තිරිංග)

threading module එකෙන් අපිට threads හදන්න පුළුවන්. Threads කියන්නේ එක program එකක් ඇතුලේ තියෙන, එකම memory space එක share කරන, වෙන වෙනම execute වෙන්න පුළුවන් කුඩා කොටස්. Threads වල ප්‍රධාන වාසිය තමයි ඒවා lightweight වීම සහ data share කරගන්න ලේසි වීම. ඒත් Python වල තියෙන ප්‍රධානම බාධාව තමයි Global Interpreter Lock (GIL) එක. මේ GIL එක නිසා එක වෙලාවකදී එක thread එකකට විතරයි Python bytecode execute කරන්න පුළුවන් වෙන්නේ. ඒ නිසා, CPU එක ගොඩක් භාවිතා කරන (CPU-bound) tasks වලදී threads භාවිතා කිරීමෙන් performance එකේ ලොකු වැඩිවීමක් බලාපොරොත්තු වෙන්න අමාරුයි. Threads ගොඩක් හොඳ I/O-bound tasks වලට. ඒ කියන්නේ network request කරනකොට, files read/write කරනකොට වගේ operations වලදී, thread එකක් I/O operation එකක් වෙනකම් බලාගෙන ඉන්න අතරේ GIL එක අනිත් thread එකකට දීලා අනිත් thread එකට වැඩ කරන්න පුළුවන්. නමුත් CPU එක continuously run කරන්න වෙන tasks වලදී GIL එක බාධාවක් වෙනවා.

Processes (ක්‍රියාවලි)

multiprocessing module එකෙන් අපිට processes හදන්න පුළුවන්. Processes කියන්නේ OS එක විසින් වෙන වෙනම run කරන, තමන්ටම කියලා වෙනම memory space එකක් තියෙන programs. මේවා එකිනෙකට ස්වාධීනයි. මේ නිසා, එක් process එකක් crash වුණත් අනෙක් processes වලට බලපෑමක් වෙන්නේ නැහැ. Processes වල ප්‍රධානම වාසිය තමයි ඒවාට GIL එකේ බලපෑමක් නැති වීම. මොකද, හැම process එකකටම තමන්ගේම Python interpreter instance එකක් තියෙනවා. ඒ නිසා, ඔබේ computer එකේ multiple CPU cores තියෙනවනම්, ඔබට එකවර processes කිහිපයක් වෙන වෙනම CPU cores මත run කරලා true parallelism ලබාගන්න පුළුවන්. මේක CPU-bound tasks වල performance එක වැඩි කරන්න තියෙන හොඳම ක්‍රමයයි.

සරලව කිව්වොත්,

  • Threads: එකම ගෙදර (process එක) ඇතුලේ ඉන්න අය වගේ. හැමෝටම එකම කුස්සියක් (memory space) තියෙනවා. ඒත් එක පාරට එක්කෙනෙක්ට විතරයි කෑම හදන්න පුළුවන් (GIL). I/O-bound tasks වලට හොඳයි.
  • Processes: වෙන වෙනම ගෙවල් වල ඉන්න අය වගේ. හැමෝටම තමන්ගේම කුස්සියක් (memory space) තියෙනවා. එක පාරට කී දෙනෙක්ට වුණත් කෑම හදන්න පුළුවන් (no GIL restriction across processes). CPU-bound tasks වලට හොඳයි.

2. Python වල Multiprocessing Module එක

Python වල `multiprocessing` module එකෙන් අපිට processes හදන්න, ඒවා control කරන්න, සහ ඒවා අතර data share කරන්න අවශ්‍ය හැම පහසුකමක්ම ලබා දෙනවා. අපි බලමු මේකේ මූලිකම දේවල්.

Process එකක් හදලා run කරන්නේ කොහොමද?

multiprocessing module එකේ තියෙන `Process` class එක තමයි ප්‍රධානම එක. අපි මේකෙන් object එකක් හදලා, ඒකට run කරන්න ඕන function එක (`target`) සහ ඒ function එකට දෙන්න ඕන arguments (`args`) දෙන්න ඕන. ඊට පස්සේ `start()` method එකෙන් process එක පටන් ගන්නවා, සහ `join()` method එකෙන් main program එකට පුළුවන් මේ process එක ඉවර වෙනකම් බලාගෙන ඉන්න.

අපි උදාහරණයක් බලමු. සංඛ්‍යාවක වර්ගඵලය ගණනය කරන function එකක් processes කිහිපයකින් run කරන්නේ කොහොමද කියලා.


import multiprocessing
import time

def calculate_square(number):
    """දෙන ලද සංඛ්‍යාවේ වර්ගඵලය ගණනය කර print කරයි.
    CPU-bound task එකක් simulate කිරීම සඳහා ትንሽ කාලයක් නතර කරයි.
    """
    print(f"Process {multiprocessing.current_process().name}: Calculating square of {number}...")
    time.sleep(1) # CPU-bound task එකක් simulate කිරීමට
    result = number * number
    print(f"Process {multiprocessing.current_process().name}: Square of {number} is {result}")
    return result

if __name__ == "__main__":
    numbers = [1, 2, 3, 4, 5]
    processes = []

    # එක් එක් සංඛ්‍යාව සඳහා process එකක් සාදන්න
    for num in numbers:
        process = multiprocessing.Process(target=calculate_square, args=(num,))
        processes.append(process)
        process.start() # Process එක ආරම්භ කරන්න

    # සියලු processes අවසන් වන තෙක් බලා සිටින්න
    for process in processes:
        process.join() 

    print("\nAll processes finished.")

මේ code එකෙන් මොකද වෙන්නේ කියලා බලමු:

  • අපි `calculate_square` කියලා function එකක් හදනවා. මේක `number` එකක වර්ගඵලය ගණනය කරලා print කරනවා. අපි `time.sleep(1)` දාලා තියෙන්නේ, මේක CPU-bound task එකක් වගේ simulate කරන්නයි.
  • `if __name__ == "__main__":` block එක ඇතුලේ තමයි processes ටික හදන්නේ. මේ block එක ඉතාම වැදගත්. Windows වල process එකක් start කරනකොට parent process එකේ code එක import කරනවා. මේ block එක නැත්නම් infinite recursion එකක් වෙන්න පුළුවන්.
  • `numbers` කියන list එකේ තියෙන හැම number එකකටම අපි `multiprocessing.Process` object එකක් හදනවා.
  • `target` කියන argument එකට අපි run කරන්න ඕන function එක (`calculate_square`) දෙනවා.
  • `args` කියන argument එකට, `target` function එකට pass කරන්න ඕන arguments දෙනවා. මේක tuple එකක් වෙන්න ඕන. එක argument එකක් වුණත් tuple එකක් විදිහට දෙන්න ඕන (e.g., `(num,)`).
  • `process.start()` කියලා දුන්නම අලුත් process එකක් හදලා `calculate_square` function එක run කරන්න පටන් ගන්නවා.
  • `processes.append(process)` කරලා අපි හැම process එකක්ම list එකකට එකතු කරගන්නවා.
  • අන්තිමට, `for process in processes: process.join()` කියලා දුන්නම, main program එක හැම child process එකක්ම ඉවර වෙනකම් බලාගෙන ඉන්නවා. `join()` නැත්නම් main program එක child processes ඉවර වෙන්න කලින්ම ඉවර වෙලා යන්න පුළුවන්.

මේ code එක run කරලා බලන්න. output එකේ දැකගන්න පුළුවන් හැම process එකක්ම එකම වෙලාවේ, වෙන වෙනම run වෙනවා.

3. Inter-Process Communication (IPC): දත්ත හුවමාරු කරගන්නේ කොහොමද?

අපි කලින් කතා කළා වගේ, processes වලට තමන්ටම කියලා වෙනම memory space එකක් තියෙනවා. ඒ නිසා, එක process එකක තියෙන data තවත් process එකකට කෙලින්ම access කරන්න බැහැ. මේක තමයි Inter-Process Communication (IPC) කියන එකේ අවශ්‍යතාවය මතු වෙන්නේ. Python වල `multiprocessing` module එකෙන් IPC කරන්න විවිධ ක්‍රම සපයනවා:

a. Queues (තේරුම් ගැනීමට පහසුම ක්‍රමය)

`Queue` කියන්නේ processes අතර messages pass කරන්න තියෙන safe සහ simple ක්‍රමයක්. එක process එකකට data `put()` කරන්න පුළුවන්, අනිත් process එකකට `get()` කරන්න පුළුවන්. `Queue` එක thread-safe වගේම process-safe. ඒ කියන්නේ එකවර processes කිහිපයකට Queue එකට access කරන්න පුළුවන්.


import multiprocessing

def worker_function(queue, data):
    """දත්ත සකස් කර ප්‍රතිඵල Queue එකට යොදවයි."""
    print(f"Process {multiprocessing.current_process().name}: Processing {data}")
    result = data * 2
    queue.put(result)

if __name__ == "__main__":
    my_queue = multiprocessing.Queue()
    processes = []
    input_data = [10, 20, 30, 40]

    for data in input_data:
        p = multiprocessing.Process(target=worker_function, args=(my_queue, data))
        processes.append(p)
        p.start()

    for p in processes:
        p.join()

    # Queue එකෙන් ප්‍රතිඵල ලබාගන්න
    results = []
    while not my_queue.empty():
        results.append(my_queue.get())

    print(f"\nAll results: {results}")

මේ උදාහරණයෙන්, `worker_function` එක input data එක දෙගුණ කරලා `my_queue` එකට දානවා. Main process එක හැම process එකක්ම ඉවර වුණාට පස්සේ `my_queue` එකෙන් results ටික එකින් එක අරගන්නවා.

b. Pipes (දෙපැත්තටම සන්නිවේදනය සඳහා)

`Pipe` කියන්නේ process දෙකක් අතර දත්ත හුවමාරු කරගන්න පුළුවන් bidirectional communication channel එකක්. Pipe එකක් හදනකොට `Connection` objects දෙකක් return කරනවා. එකක් parent process එකට, අනිත් එක child process එකට. මේ objects වලින් `send()` සහ `recv()` methods භාවිතා කරලා data යවන්නත්, ගන්නත් පුළුවන්.

c. Shared Memory (බෙදාගත් මතකය)

`multiprocessing` module එකෙන් `Value` සහ `Array` වගේ shared memory objectsත් සපයනවා. මේවා multiple processes වලට access කරන්න පුළුවන්. ඒ වගේම `Manager` class එක භාවිතා කරලා shared `list`, `dict`, `Queue` වගේ complex data structures හදන්නත් පුළුවන්. මේවා භාවිතා කරන්නේ සාමාන්‍ය Python data structures වගේමයි, හැබැයි මේවා process-safe විදිහට හැසිරෙනවා.


import multiprocessing

def update_shared_value(value, array, idx):
    value.value += 1 # shared Value එක update කරනවා
    array[idx] = value.value * 10 # shared Array එක update කරනවා

if __name__ == "__main__":
    # Shared Value එකක් සාදන්න
    # 'i' කියන්නේ integer type එක. 0 කියන්නේ ආරම්භක අගයයි.
    shared_int_value = multiprocessing.Value('i', 0)

    # Shared Array එකක් සාදන්න
    # 'i' කියන්නේ integer type එක. 4 කියන්නේ elements ගණනයි.
    shared_int_array = multiprocessing.Array('i', 4)

    processes = []

    for i in range(4):
        p = multiprocessing.Process(target=update_shared_value, args=(shared_int_value, shared_int_array, i))
        processes.append(p)
        p.start()

    for p in processes:
        p.join()

    print(f"\nFinal Shared Value: {shared_int_value.value}")
    print(f"Final Shared Array: {list(shared_int_array)}")

මේ උදාහරණයෙන් `shared_int_value` සහ `shared_int_array` කියන shared memory objects වලට multiple processes වලින් access කරලා update කරන හැටි පෙන්නනවා. `Value` එකේ `value` attribute එක හරහා අගය ලබාගන්නත්, update කරන්නත් පුළුවන්.

4. Multiprocessing වලදී එන ගැටළු සහ විසඳුම් (Challenges and Solutions)

`multiprocessing` එකෙන් අපිට හොඳ performance එකක් ගන්න පුළුවන් වුණාට, මේක භාවිතා කරනකොට අපිට පොඩි පොඩි අභියෝග වලට මුහුණ දෙන්න වෙනවා. මේවා ගැන අවබෝධයක් තියෙන එකෙන් අපිට problems වලින් බේරෙන්න පුළුවන්.

a. Overhead of Process Creation (Process හැදීමේ අධික පිරිවැය)

Threads වලට වඩා processes හදන එකට වැඩි resource සහ කාලයක් යනවා. මොකද, හැම process එකකටම වෙනම memory space එකක් සහ Python interpreter එකක් load කරන්න වෙනවා. ඒ නිසා, පොඩි පොඩි tasks ගොඩකට processes ගොඩක් හදන එක කාර්යක්ෂම නැහැ. මේකෙන් program එකේ performance එක අඩු වෙන්නත් පුළුවන්.

  • විසඳුම: `multiprocessing.Pool` භාවිතා කරන්න. `Pool` එකෙන් process set එකක් maintain කරනවා. Task එකක් ආවම Pool එකේ තියෙන free process එකකට දෙනවා. අලුතෙන් process එකක් හදනවා වෙනුවට existing process එකක් re-use කරන නිසා overhead එක අඩු වෙනවා.

b. Pickling Issues (Pickle කිරීමේ ගැටළු)

Processes අතර data share කරනකොට, හෝ `target` function එකට arguments pass කරනකොට, Python objects 'pickle' වෙන්න පුළුවන් වෙන්න ඕන. Pickle කිරීම කියන්නේ Python object එකක් byte stream එකක් බවට convert කිරීමයි. මේ byte stream එකෙන් ආයෙත් original object එකට convert කරන්න පුළුවන්. Python objects ගොඩක් pickle කරන්න පුළුවන් වුණාට, සමහර objects (e.g., file handles, socket objects, lambda functions, nested functions without proper wrapping) pickle කරන්න බැහැ. මේක Python beginner කෙනෙක්ට එන පොදු ගැටළුවක්.

  • විසඳුම: `multiprocessing` හරහා යවන හැම object එකක්ම pickleable බව තහවුරු කරගන්න. අවශ්‍ය නම්, custom classes වලට `__getstate__` සහ `__setstate__` methods implement කරන්න පුළුවන්. Simple data types (numbers, strings, lists, dictionaries) සාමාන්‍යයෙන් pickleable.

c. Debugging Challenges (දෝෂ නිවාරණයේ අභියෝග)

Multiple processes එකවර run වෙනකොට debugging කරන එක ටිකක් අමාරුයි. Tracebacks වගේ දේවල් කලවම් වෙන්න පුළුවන්. Breakpoints හරියට වැඩ නොකරන්නත් පුළුවන්.

  • විසඳුම: Logging හොඳින් භාවිතා කරන්න. හැම process එකකටම තමන්ගේම log එකක් තියෙනවා නම් debugging ලේසියි. Process ID (PID) සහ process name log වලට එකතු කරන්න. Interactive debuggers (e.g., PDB) multiprocessing එක්ක භාවිතා කරනකොට පොඩි challenges තියෙන්න පුළුවන්, ඒ නිසා print statements සහ logging හොඳින් භාවිතා කරන්න පුරුදු වෙන්න.

d. Resource Management (සම්පත් කළමනාකරණය)

අපි processes ගොඩක් හදනකොට CPU, memory වගේ system resources වැඩිපුර භාවිතා වෙනවා. වැඩියෙන් processes හැදුවොත් system එක slow වෙන්න පුළුවන්. විශේෂයෙන්ම Memory consumption එක වැඩි වෙන්න පුළුවන්. මොකද හැම process එකටම තමන්ගේම memory space එකක් තියෙන නිසා.

  • විසඳුම: හදන processes ගණන ඔබේ CPU cores ගණනට සීමා කරන්න. `multiprocessing.cpu_count()` භාවිතා කරලා ඔබේ CPU එකේ cores ගණන දැනගන්න පුළුවන්. `Pool` එකේ `processes` argument එකට මේක pass කරන්න පුළුවන්. Memory usage එක ගැනත් සැලකිලිමත් වෙන්න.

5. හොඳම පුරුදු (Best Practices)

Multiprocessing වලින් උපරිම ප්‍රයෝජන ගන්න නම්, මේ හොඳම පුරුදු ටික මතක තියාගන්න.

a. CPU-bound tasks වලට පමණක් භාවිතා කරන්න

මේක තමයි Multiprocessing භාවිතා කිරීමේ ප්‍රධානම හේතුව. If your task involves heavy computation and doesn't spend much time waiting for external resources (like network or disk), then `multiprocessing` is your go-to solution. I/O-bound tasks වලට `threading` හෝ `asyncio` වගේ දේවල් වඩාත් සුදුසුයි.

b. කාර්යයන් සරලව තබා ගන්න (Keep tasks simple and focused)

සෑම process එකකටම පැවරෙන කාර්යය සරල සහ පැහැදිලි එකක් වෙන්න ඕන. Complex logic එකක් processes කිහිපයකට බෙදලා දුන්නම maintain කරන්න අමාරු වෙනවා වගේම debuggingත් අමාරු වෙනවා. එක process එකක් එක දෙයක් කරනවා නම් හොඳයි.

c. IPC අවම කරන්න (Minimize Inter-Process Communication)

Processes අතර data send කරන එකට overhead එකක් තියෙනවා. මොකද, data serialize කරලා (pickle කරලා) යවලා, ආයෙත් deserialize කරන්න ඕන. ඒ නිසා, processes අතර data flow එක හැකිතාක් අඩු කරන්න. Processes වලට අවශ්‍ය හැම data එකක්ම මුලින්ම දීලා, ඒවායින් අවසාන result එක විතරක් ආපහු ගන්නවා නම් හොඳයි.

d. Pool එක භාවිතා කරන්න (Leverage `multiprocessing.Pool`)

ඔබට tasks ගොඩක් තියෙනවා නම්, ඒ tasks ටික processes කිහිපයකට බෙදලා දෙන්න ඕන නම්, `multiprocessing.Pool` එක ඉතාම ප්‍රයෝජනවත්. `Pool` එකෙන් ඔබට processes set එකක් හදන්න පුළුවන්. ඒ processes වලට ඔබේ tasks ටික distribute කරන්න පුළුවන්. `map()`, `apply()`, `map_async()`, `apply_async()` වගේ methods මේකට භාවිතා කරන්න පුළුවන්. මේකෙන් process creation overhead එක අඩු වෙනවා වගේම code එකත් clean වෙනවා.


import multiprocessing
import time

def heavy_computation(num):
    """CPU-bound calculation simulate කරයි."""
    time.sleep(1) # CPU වැඩක් simulate කරන්න
    return num * num

if __name__ == "__main__":
    numbers = [1, 2, 3, 4, 5, 6, 7, 8]
    
    # CPU cores ගණනට සමාන processes ගණනක් සහිත Pool එකක් සාදන්න
    num_processes = multiprocessing.cpu_count()
    print(f"Using {num_processes} processes for the Pool.")

    with multiprocessing.Pool(processes=num_processes) as pool:
        # map() method එකෙන් function එක numbers list එකේ හැම element එකකටම apply කරනවා
        # ප්‍රතිඵල ලැබෙන තෙක් බලා සිටිනවා
        results = pool.map(heavy_computation, numbers)

    print(f"\nResults from Pool: {results}")
    print("All computations finished using a Pool.")

Pool එක `with` statement එකක් එක්ක භාවිතා කරන එක හොඳ පුරුද්දක්. මොකද එතකොට `pool.close()` සහ `pool.join()` automatically call වෙනවා.

e. Graceful Shutdown and Error Handling

ඔබේ processes වලට signals handle කරන්න පුළුවන් වෙන්න ඕන (e.g., Ctrl+C), සහ clean shutdown එකක් වෙන්න ඕන. Processes වල errors ආවොත් ඒවා main process එකට report කරන විදිහට code කරන්න. Logging හොඳින් භාවිතා කරන එකත් වැදගත්.

අවසාන වශයෙන්

Python වල `multiprocessing` module එක කියන්නේ ඔබේ CPU-bound programs වල performance එක වැඩි කරන්න තියෙන හරිම බලවත් tool එකක්. Python වල තියෙන GIL එක bypass කරලා, ඔබේ computer එකේ තියෙන හැම CPU core එකකම උපරිම ප්‍රයෝජන ගන්න මේකෙන් පුළුවන්.

අපි මේ post එකෙන් ඉගෙනගත්තේ:

  • Concurrency සහ Parallelism අතර වෙනස සහ Threads vs. Processes වල විශේෂතා.
  • `multiprocessing.Process` භාවිතා කරලා processes හදන හැටි සහ ඒවා control කරන හැටි.
  • Queue, Pipe, සහ Shared Memory වගේ ක්‍රම භාවිතා කරලා processes අතර data share කරන හැටි.
  • Multiprocessing භාවිතා කරනකොට එන පොදු ගැටළු (overhead, pickling issues) සහ ඒවාට විසඳුම්.
  • `multiprocessing.Pool` භාවිතා කිරීම වගේම අනෙකුත් හොඳම පුරුදු.

හරි, දැන් ඔයාට `multiprocessing` module එක ගැන හොඳ අවබෝධයක් ඇති. මේ දැනුමෙන් ඔබේ Python programs වල speed එක වැඩි කරගන්න පුළුවන්. මතක තියාගන්න, හැම task එකකටම `multiprocessing` ඕන වෙන්නේ නැහැ. CPU intensive tasks වලට මේක හොඳම solution එක. ඒත් I/O intensive tasks වලට `threading` හෝ `asyncio` වඩාත් සුදුසු වෙන්න පුළුවන්.

අදම මේවා ඔබේ Project එකකට දාලා බලන්න! පොඩි script එකක් හදලා ඔබේ computer එකේ cores කිහිපයක් එකවර වැඩ කරන හැටි බලන්න. ඔබට මේ ගැන තවත් ප්‍රශ්න තියෙනවා නම්, හෝ ඔබේ අත්දැකීම් ගැන කියන්න කැමති නම්, පහලින් comment කරන්න. අපි හැමෝම එකතු වෙලා තව දුරටත් ඉගෙන ගමු!