Using Parallel For in Java to Compute PI using Monte Carlo Algorithm


The easiest Parallelism in Java could be achieved by the java.util.stream.Stream utility.

As a good coding exercise, the following Java code will compute the Math PI constant based on the Monte Carlo simulation.

We use Arrays.stream([T]).sequential() or parallel, which will be type of Stream<T> and then can be invoked with .forEach().

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import java.util.stream.Stream;
import java.util.Arrays;
import java.time.LocalTime;
 
public class MonteCarloPISimulationUsingParallelForInJava {
    private final static Object lock = new Object();
    private final static int TotalSamples = 10000;
    private static int total = 0;
 
    private static Stream<integer> prepare(int threadNumber) {
        Integer[] Samples = new Integer[threadNumber];
        Arrays.fill(Samples, TotalSamples/threadNumber);
        if (threadNumber > 1)
            return Arrays.stream(Samples).parallel();
        return Arrays.stream(Samples).sequential();
    }
    
    public static void main (String[] args) {
        System.out.println("-------\nRunning sequential\n-------");
        run(prepare(1));
 
        System.out.println("-------\nRunning parallel\n-------");
        run(prepare(10));
    }
 
    public static void run (Stream<integer> stream) {
        long startTime = System.nanoTime();
        total = 0;      
        stream.forEach(s -> {           
            // System.out.println(LocalTime.now() + " - value: " + s + " - thread: " + Thread.currentThread().getName());            
            int n = 0;
            for (int i = 0; i < s; ++ i) {
                double x = Math.random();
                double y = Math.random();
                if (x * x + y * y <= 1) { 
                      n ++; // count the points that fall in the circle
                }                                                   
            }
            synchronized(lock) { // race condition protection
                total += n;
            }            
        });
        long endTime = System.nanoTime();
        long duration = (endTime - startTime);  //divide by 1000000 to get milliseconds.
        System.out.println((duration / 1000000) + " ms");
        System.out.println("PI = " + total * 4.0 / TotalSamples);
    }
}
import java.util.stream.Stream;
import java.util.Arrays;
import java.time.LocalTime;

public class MonteCarloPISimulationUsingParallelForInJava {
	private final static Object lock = new Object();
	private final static int TotalSamples = 10000;
	private static int total = 0;

	private static Stream<integer> prepare(int threadNumber) {
        Integer[] Samples = new Integer[threadNumber];
        Arrays.fill(Samples, TotalSamples/threadNumber);
        if (threadNumber > 1)
        	return Arrays.stream(Samples).parallel();
        return Arrays.stream(Samples).sequential();
	}
	
    public static void main (String[] args) {
        System.out.println("-------\nRunning sequential\n-------");
        run(prepare(1));

        System.out.println("-------\nRunning parallel\n-------");
        run(prepare(10));
    }

    public static void run (Stream<integer> stream) {
    	long startTime = System.nanoTime();
    	total = 0;    	
        stream.forEach(s -> {        	
            // System.out.println(LocalTime.now() + " - value: " + s + " - thread: " + Thread.currentThread().getName());            
            int n = 0;
            for (int i = 0; i < s; ++ i) {
            	double x = Math.random();
            	double y = Math.random();
            	if (x * x + y * y <= 1) { 
            	      n ++; // count the points that fall in the circle
            	}            	                                 	
            }
            synchronized(lock) { // race condition protection
            	total += n;
            }            
        });
        long endTime = System.nanoTime();
        long duration = (endTime - startTime);  //divide by 1000000 to get milliseconds.
        System.out.println((duration / 1000000) + " ms");
        System.out.println("PI = " + total * 4.0 / TotalSamples);
    }
}

Let’s run the code and the parallel version seems slightly faster.

——-
Running sequential
——-
62 ms
PI = 3.1504
——-
Running parallel
——-
10 ms
PI = 3.1092

However, if we slightly change the number of samplings, we might have the opposite results.

——-
Running sequential
——-
122 ms
PI = 3.140424
——-
Running parallel
——-
162 ms
PI = 3.141008

According to StackOverFlow, the Streams parallelism has overheads and we should always start with the .sequential() unless it has performance issues.

The concurrent code has been implemented as an anonymous function. The elements in the Array are processed in parallel. The elements in the array represent the number of the sub-samplings in the above Monte Carlo simulation.

In a concurrent block of code, we need to protect the variable if we want to change the value. In Java, we use the lock to be synchronized.

Monte Carlo Simulation Algorithms to compute the Pi based on Randomness:

–EOF (The Ultimate Computing & Technology Blog) —

GD Star Rating
loading...
854 words
Last Post: 3 Types of Coding Languages That Will Intrigue & Educate Youngsters
Next Post: Bash Command to Find Out the IPs that Hit Your Server

The Permanent URL is: Using Parallel For in Java to Compute PI using Monte Carlo Algorithm

Leave a Reply