Ractor for true parallelism in Ruby - Time & Space Complexity
When using Ractors in Ruby, we want to understand how running code in parallel affects the total time it takes.
We ask: How does adding more Ractors change the work done and the time spent?
Analyze the time complexity of this Ruby code using Ractors.
numbers = (1..1000).to_a
ractor1 = Ractor.new(numbers[0..499]) do |nums|
nums.map { |n| n * 2 }
end
ractor2 = Ractor.new(numbers[500..999]) do |nums|
nums.map { |n| n * 2 }
end
result1 = ractor1.take
result2 = ractor2.take
combined = result1 + result2
This code splits an array into two parts and processes each part in a separate Ractor to run in parallel.
Look at what repeats and how often.
- Primary operation: Mapping over each half of the array to multiply numbers.
- How many times: Each Ractor processes about 500 elements once.
- Parallelism: Two Ractors run these operations at the same time.
As the input size grows, the total work grows, but running in parallel changes how time adds up.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 multiplications split across 2 Ractors |
| 100 | 100 multiplications split across 2 Ractors |
| 1000 | 1000 multiplications split across 2 Ractors |
Pattern observation: Total operations grow linearly with input size, but time per Ractor depends on its share, potentially halving the time.
Time Complexity: O(n / p)
This means the time grows linearly with input size but is divided by the number of Ractors (p) running in parallel.
[X] Wrong: "Using more Ractors always makes the program run faster by the same amount."
[OK] Correct: Creating and communicating between Ractors has overhead, so adding too many can slow things down instead of speeding them up.
Understanding how parallelism affects time helps you explain real-world performance improvements clearly and confidently.
What if we increased the number of Ractors to 10? How would the time complexity change?
Practice
Ractor in Ruby?Solution
Step 1: Understand Ractor's role
Ractor is designed to enable true parallel execution in Ruby, using multiple CPU cores.Step 2: Compare options
Options A, B, and C do not relate to parallelism or CPU core usage.Final Answer:
To run code truly in parallel on multiple CPU cores -> Option AQuick Check:
Ractor = true parallelism [OK]
- Confusing Ractor with threads
- Thinking Ractor is for UI or database
- Assuming Ractor runs code sequentially
Solution
Step 1: Identify Ractor creation syntax
Ractor is created usingRactor.newwith a block of code to run in parallel.Step 2: Eliminate other options
Thread, Process, and Fiber are different concurrency tools, not Ractor.Final Answer:
Ractor.new { puts 'Hello from Ractor' } -> Option AQuick Check:
Use Ractor.new to create Ractors [OK]
- Using Thread.new instead of Ractor.new
- Confusing Process.spawn with Ractor
- Using Fiber.new which is cooperative, not parallel
r = Ractor.new { 5 * 5 }
result = r.take
puts resultSolution
Step 1: Understand Ractor block execution
The block{ 5 * 5 }runs inside the Ractor and returns 25.Step 2: Use
taketo get the resultr.takereceives the value returned by the Ractor block, which is 25.Final Answer:
25 -> Option DQuick Check:
Ractor returns 25, take receives it [OK]
- Expecting string '5 * 5' instead of calculation
- Confusing take with yield
- Assuming nil or error without communication
r = Ractor.new do Ractor.yield 10 20 end puts r.take puts r.take
Solution
Step 1: Understand Ractor.yield and r.take
Ractor.yield sends a value to the main thread, and r.take receives it. The first r.take gets 10.Step 2: Analyze second r.take call
The block returns 20 after yield, which is the final value. The second r.take gets 20. After that, the Ractor is closed. A third r.take would cause error, but here only two calls are made, so no error.Step 3: Check the code carefully
Both r.take calls are valid; no error occurs.Final Answer:
No error, code runs fine -> Option CQuick Check:
Ractor.yield and take work twice safely [OK]
- Thinking Ractor.yield is invalid inside Ractor
- Expecting error on second take call
- Assuming Ractor.new needs arguments
Solution
Step 1: Split the range correctly
We want two Ractors, one summing 1 to 50, the other 51 to 100.Step 2: Check the operations and combining results
r1 = Ractor.new { (1..50).sum } r2 = Ractor.new { (51..100).sum } puts r1.take + r2.take sums each half and adds results correctly. r1 = Ractor.new { (1..100).sum } r2 = Ractor.new { (1..100).sum } puts r1.take + r2.take sums full range twice (wrong). r1 = Thread.new { (1..50).sum } r2 = Thread.new { (51..100).sum } puts r1.value + r2.value uses Thread, not Ractor. r1 = Ractor.new { (1..50).reduce(:+) } r2 = Ractor.new { (51..100).reduce(:+) } puts r1.take * r2.take multiplies sums instead of adding.Final Answer:
r1 = Ractor.new { (1..50).sum } r2 = Ractor.new { (51..100).sum } puts r1.take + r2.take -> Option BQuick Check:
Split range, sum halves, add results [OK]
- Using Thread instead of Ractor
- Summing full range twice
- Multiplying sums instead of adding
Ready to prove it?
Pass the test and this pattern moves unknown
