4

I was asked to check calculation time depending on number of threads working on the problem. Therefore I had written a program that calculates integral using Monte Carlo method. I am dividing the range for number of threads. After that I stats threads, which calculate their part, and finally sum partial results to get general one.

The problem is that time of calculation increases with number of threads instead of decreasing (i7 processor, Windows 7)

A few people working on it, and we do not know why is that. I hope someone will give me an advice. I attach code:

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue;


public class Runner {

private static final int MAXT = 10; // maksymalna ilość wątków
static PrintWriter outM;
static PrintWriter outMTime;

public static void main(String[] args){

    double xp = 2;
    double xk = 3;




    filesOp();

    // Wypisywanie kolumn tabeli
    for(int threadNumber=1; threadNumber<=MAXT; threadNumber++){
            outM.print("\t"+ threadNumber);
            outMTime.print("\t"+ threadNumber);
        }

    double time1;
    double time2;

    //double startTime=System.currentTimeMillis(); // Przed wystartowaniem programu

    for(int n=10000; n<=10000000; n=n*10){

        System.out.println("Licze dla: " + n + " punktow.");


            outM.print("\n"+n);
            outMTime.print("\n"+n);


        for(int threadNumber=1; threadNumber<=MAXT; threadNumber++){

            outM.print("\t");
            outMTime.print("\t");

            time1=System.nanoTime();
                multiThread(xp, xk, n, threadNumber);
            time2=System.nanoTime();

            outMTime.print((time2-time1)/1000000);
            // czas pracy dla danej liczby wątków

        }

    }

    outM.close();
    outMTime.close();

}


public static void multiThread(double xp, double xk, int n, int threadNumber){
    // Funkcja licząca całkę wielowątkowo.
    // Całka do policzenia jest dzielona pomiędzy wątki

    ArrayList<Thread> threadList = new ArrayList<Thread>();
    ConcurrentLinkedQueue<Double> results = new ConcurrentLinkedQueue<Double>();

    for(int i=0; i<threadNumber; i++){ 

        MonteCarlo mc = new MonteCarlo( xp+(i*((xk-xp)/threadNumber)), xp+((i+1)*((xk-xp)/threadNumber)), (int)(n/threadNumber), results);


        Thread t = new Thread(mc);
        threadList.add(t);
        t.start();

    }

    //for(int j=0; j<threadNumber; j++){ // pętla czeka na zakończenie wątków
    for(Thread t : threadList){
        try {
            //while(t.isAlive()){}
            //threadList.get(j).join();
            t.join();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    double wynik = 0;
    //for(int k=0; k<results.size(); k++){
    for(double r: results){ 
        //wynik = wynik + results.remove();
        wynik= wynik + r;
    }


    outM.print(wynik);
}



public static void filesOp(){
    File fileTemp;

    fileTemp = new File("wyniki.txt");
    if (fileTemp.exists()) fileTemp.delete();


    fileTemp = new File("pomiary.txt");
    if (fileTemp.exists()) fileTemp.delete();


    try {

        outM = new PrintWriter(new FileWriter("wyniki.txt", true));
        outMTime = new PrintWriter(new FileWriter("pomiary.txt", true));    
    } catch (Exception e) {
        e.printStackTrace();
    }
}


}


public class MonteCarlo implements Runnable{

    double xp; 
    double xk; 
    long n;
    ConcurrentLinkedQueue<Double> results;

    MonteCarlo(double xp, double xk, long n, ConcurrentLinkedQueue<Double> results){
        this.xp=xp;
        this.xk=xk;
        this.n=n;
        this.results=results;
    }

    //funkcja dla ktorej obliczamy calke
    private static double func(double x) {
        return x*x+3;
    }


    private static double funcIn(double x, double y) {
        if (( y > 0) && (y <= func(x)))
            return 1;
        else if (( y > 0) && (y <= func(x)))
            return -1;
        return 0;
    }

    //random number from a to b
    private static double randomPoint(double a, double b) {
        return  a + Math.random() * (b-a);
    }  

    public void run(){      
        double yp, yk, calka;
        int pointsIn;


        yp = 0;
        yk = Math.ceil(Math.max(func(xp), func(xk)));

        pointsIn = 0;

        for (long i=0; i<n; i++) {
        pointsIn += funcIn(randomPoint(xp, xk), randomPoint(yp, yk));
        }

        calka = (pointsIn / (double)n) * ((xk-xp) * (yk-yp));       

        results.add(calka);

        }


}

And the example of results:

1 2 3 4 5 6 7 8 9 10

10000 6.185818 2.821405 3.721287 3.470309 4.068365 3.604195 4.323075 4.192455 6.159694 4.239105

100000 10.994522 15.874134 34.992323 40.851124 36.199631 49.54579 45.122417 61.427132 55.845435 60.7661

1000000 108.653008 274.443662 340.274574 407.054352 437.455361 469.853467 496.849012 584.519687 571.09329 594.152023

10000000 1066.059033 2877.947652 3600.551966 4175.707089 4488.434247 5081.572093 5501.217804 6374.335759 6128.274553 6339.043475

Niko
  • 812
  • 9
  • 22
  • You should not include the time it takes to create and start a thread in your calculations, they take a significant time to create and start. Also, creating thousands of objects could also skew your statistics. – OldCurmudgeon Apr 24 '15 at 10:25

2 Answers2

5

The problem most likely lies in

private static double randomPoint(double a, double b) {
    return  a + Math.random() * (b-a);
}  

Math.random() performs poorly under heavy contention. If you are using java 7 or later, try this instead:

private static double randomPoint(double a, double b) {
    return ThreadLocalRandom.current().nextDouble(a, b);
}
Misha
  • 27,433
  • 6
  • 62
  • 78
  • Switching to `ThreadLocalRandom` made the execution time of `randomPoint` go up from 151 seconds to 203 seconds (on my computer, java 8) – Aerus Apr 24 '15 at 11:07
  • @Aerus Well, on my machine it resolved the problem (Java 8 too). – AlexW Apr 24 '15 at 11:27
  • Agree. The same problem as [here](http://stackoverflow.com/questions/23663178/inefficient-threads-in-java) – apangin Apr 24 '15 at 11:39
  • Great! Now it works great! I was not expecting that the problem could be there! But i don't really get why is that :) – Niko Apr 24 '15 at 13:30
  • @Niko If you are curious, you can just explore, how `Math.random()` is implemented. You will see a while loop `do {...} while (!seed.compareAndSet(oldseed, nextseed))`. The problem is in this `compareAndSet` - under heavy load, it often returns `false`, so a loop body has to be executed many times before it returns new random value. – AlexW Apr 24 '15 at 14:07
0

Using static funtions frequently is one of the pitfalls in Multithreading. A more general answer can be found in this post already.

Community
  • 1
  • 1
Liebertee
  • 88
  • 9