Process forking for parallelism in Ruby - Time & Space Complexity
When using process forking, we want to see how the program's running time changes as we create more child processes.
We ask: How does splitting work into parallel processes affect the total time spent?
Analyze the time complexity of the following code snippet.
def parallel_work(tasks)
pids = []
tasks.each do |task|
pid = Process.fork do
task.call
exit!
end
pids << pid
end
pids.each { |pid| Process.wait(pid) }
end
This code runs a list of tasks in parallel by creating a new process for each task, then waits for all to finish.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Loop over each task to fork a process.
- How many times: Once per task, so as many times as tasks count.
- Secondary operation: Loop to wait for each child process to finish.
- Dominant operation: Forking processes for each task, since it starts parallel work.
As the number of tasks grows, the program creates more processes, but they run at the same time.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 forks + 10 waits, tasks run in parallel |
| 100 | 100 forks + 100 waits, tasks run in parallel |
| 1000 | 1000 forks + 1000 waits, tasks run in parallel |
Pattern observation: The number of forks and waits grows linearly with tasks, but total time depends on longest task, not sum.
Time Complexity: O(n)
This means the number of operations grows linearly with the number of tasks, but actual runtime can be much faster due to parallel execution.
[X] Wrong: "Forking many processes always makes the program run n times faster."
[OK] Correct: Creating many processes adds overhead and system limits; also, tasks run in parallel but total time depends on the slowest task, not just number of tasks.
Understanding how process forking affects time helps you explain parallelism clearly and shows you can think about real program performance beyond just code logic.
"What if we limited the number of child processes running at the same time? How would that change the time complexity?"
Practice
fork method do in Ruby?Solution
Step 1: Understand the purpose of
Theforkforkmethod creates a new process, called the child, which runs alongside the original parent process.Step 2: Identify the behavior of the child process
The child process runs the code block given toforkindependently, allowing parallel execution.Final Answer:
It creates a new child process that runs concurrently with the parent. -> Option BQuick Check:
forkcreates child process = A [OK]
- Thinking fork pauses the process
- Confusing fork with process termination
- Assuming fork merges processes
fork in Ruby?Solution
Step 1: Recall the correct
Theforksyntaxforkmethod takes a block with curly braces or do-end to run code in the child process.Step 2: Check each option's syntax
child_pid = fork { puts 'Hello from child' } correctly assigns the child process ID and uses a block with curly braces. Options A, C, and D have syntax errors or incorrect usage.Final Answer:
child_pid = fork { puts 'Hello from child' } -> Option DQuick Check:
Use block with fork = B [OK]
- Omitting braces for the block
- Assigning fork incorrectly
- Using wrong order of arguments
pid = fork do puts 'Child process' end Process.wait(pid) puts 'Parent process'
Solution
Step 1: Understand the fork and wait behavior
Theforkcreates a child process that prints 'Child process'. The parent waits for the child to finish usingProcess.wait(pid).Step 2: Determine output order
The child prints first, then the parent prints 'Parent process' after the child ends.Final Answer:
Child process Parent process -> Option AQuick Check:
Child prints before parent after wait = C [OK]
- Assuming parent prints first
- Ignoring Process.wait effect
- Confusing output order
fork:
pid = fork do puts 'Child running' end puts 'Parent running' Process.wait
Solution
Step 1: Check usage of Process.wait
TheProcess.waitmethod should be called with the child's PID to wait for that specific process.Step 2: Identify the error
In the code,Process.waitis called without any argument, which waits for any child process but can cause unexpected behavior or errors if multiple children exist.Final Answer:
Process.wait called without PID argument -> Option AQuick Check:
Always pass PID to Process.wait = A [OK]
- Calling Process.wait without PID
- Confusing fork return value
- Misplacing puts statements
fork and wait for both to finish before continuing. Which code correctly achieves this?Solution
Step 1: Understand how to fork multiple processes
Eachforkcreates a child process. To run two tasks in parallel, fork twice and store both child PIDs.Step 2: Wait for both child processes
UseProcess.wait(pid)for each PID to ensure both finish before continuing.Step 3: Analyze options
pids = [] pids << fork { task1 } pids << fork { task2 } pids.each { |pid| Process.wait(pid) } puts 'Both done' correctly forks twice, stores PIDs, waits for both, then prints. Others either don't wait or misuse fork.Final Answer:
pids = [] pids << fork { task1 } pids << fork { task2 } pids.each { |pid| Process.wait(pid) } puts 'Both done' -> Option CQuick Check:
Fork twice, wait both PIDs = D [OK]
- Not waiting for child processes
- Calling Process.wait inside child
- Forking once for multiple tasks
Ready to prove it?
Pass the test and this pattern moves unknown
