Search code examples
javamultithreadingperformancerunnable

How to implement multithreading in a Java ray tracer


I'm writing a ray tracing program in Java and have implemented multithreading using Runnable interface. Each thread renders a portion of the 800 vertical lines. When using two threads, they will render 400 lines each. For 8 threads, 100 lines each, and so on.

My solution is currently working, but the render time doesn't decrease when more threads are working in parallel. My CPU has 8 threads, and the usage is not 100% when rendering on 8 threads.

class Multithread implements Runnable {
  Camera camera;
  CountDownLatch latch;
  ...

  //Constructor for thread
  Multithread(Scene s, Camera c, int thread, int threadcount, CountDownLatch cdl){
      camera = c;
      latch = cdl;
      ...
  }

  public void run(){
      try{
          ...
          //This is the render function
          camera.render(...);

          //When all threads unlatch, main class will write PNG
          latch.countDown();
      }
      catch (Exception e){System.out.println ("Exception is caught");}
  }
}
public class Camera {
    //The final pixel values are stored in the 2D-array
    ColorDbl[][] finalImage;
    
    Camera(int w){
        Width = w;
        finalImage = new ColorDbl[w][w]
    }

    //Start rendering
    void render(Scene S, int start, int end){

        //Create temporary, partial image
        ColorDbl[][] tempImage = new ColorDbl[Width][Width];

        Ray r;
        ColorDbl temp;
        //Render lines of pixels in the interval start-end
        for(int j = start; j < end; ++j){
            for(int i = 0; i < Width; ++i){
                r = new Ray(...);
                temp = r.CastRay(...);
                tempImage[i][j] = temp;
            }
        }

        //Copy rendered lines to final image
        for(int j=start; j<end; ++j){
            for(int i=0; i<Width; ++i){
                finalImage[i][j] = tempImage[i][j];
            }
        }
    }

    public static void main(String[] args) throws IOException{
        //Create camera and scene
        Camera camera = new Camera(800);
        Scene scene = new Scene();

        //Create threads
        int threadcount = 4;
        CountDownLatch latch = new CountDownLatch(threadcount);
        for (int thread=0; thread<threadcount; thread++){
            new Thread(new Multithread(scene, camera, thread, threadcount, latch)).start();
        }

        //Wait for threads to finish
        try{
          latch.await();
        }catch(InterruptedException e){System.out.println ("Exception");}

        //Write PNG
        c.write(...);
    }
}

When using 2 threads instead of 1, I expect almost a doubling of render speed, but instead, it takes 50% longer. I don't expect anyone to solve my issue, but I would really appreciate some guidance when it comes to implementing multithreading. Am I going about this the wrong way?


Solution

  • I fixed the issue, and I finally understand why it didn't work.

    By doing some debugging using VisualVM I noticed that all threads but one were blocked at all time. My initial workaround was to duplicate the Scene object that was passed to every thread. It solved the issue but it wasn't elegant and it didn't make sense to me. It turns out the real solution is much simpler.

    I was using Vector<> as a container for the geometry in my scene class. Vector<> is a synchronized container that doesn't allow multiple threads to access it at the same time. By placing all the objects in the scene in an ArrayList<> instead, I get much cleaner code, less memory usage, and better performance.

    VisualVM was critical for finding the blocking, and I thank Philipp Claßen for the advice since I would have never resolved this otherwise.