0

I am looking to divide up an array to process in parallel, but am struggling to split up the array when the size does not divide nicely by the number of threads (since it rounds down). I think I am missing something obvious, but how could I handle this? I can't just round up, since then it would go past the index.

final int NUM_THREADS = Integer.parseInt(args[1]);
int size = g.nodes.length / NUM_THREADS;

for( int i = 0; i < NUM_THREADS; i++ ) {
    Runnable task = new GraphSortRunnable(i*size, (i+1)*size, g.nodes);
    // code continues
 }
Felm
  • 123
  • 8

2 Answers2

2

Solution

Now, if your goal is 2 chunks, then 13 / 2 = 6.5, but 13 / 6 = 2 + 1/2, so still not good. You have to round up, which is called ceiling.

So, two ways to round up when doing int calculations.

  1. Convert to double and use Math.ceiling(): chunkSize = (int)Math.ceiling((double)arraySize / (double)threadCount).

  2. Rely of the fact that int / int is always rounded down (truncated), so ceiling(a/b) = truncate((a + b - 1) / b), or in your case: chunkSize = (arraySize + threadCount - 1) / threadCount.

Example

If you had 10 values and 4 threads, the above code would result in chunk size of 3, so your threads would likely get 3,3,3,1.

If you want 3,3,2,2, it gets more complicated. Basically, repeat the chunk size calculation after each chunk is calculated.

  • First chunk is ceil(10/4) = 3, so that leaves 7 values and 3 threads.
  • Second chunk is ceil(7/3) = 3, so that leaves 4 values and 2 threads.
  • Third chunk is ceil(4/2) = 2, so that leaves 2 values and 1 thread.
  • Fourth thunk is 2.

CODE

And finally to prove my point, here's the code:

public static void main(String[] args) {
    System.out.println(splitEvenly(10, 2));
    System.out.println(splitEvenly(10, 3));
    System.out.println(splitEvenly(10, 4));
    System.out.println(splitEvenly(500, 13));
    System.out.println(splitEvenly(5, 10));
}
public static List<Integer> splitEvenly(int valueCount, int threadCount) {
    List<Integer> chunks = new ArrayList<>();
    int valuesLeft = valueCount;
    for (int threadsLeft = threadCount; threadsLeft > 0; threadsLeft--) {
        int chunk = (valuesLeft + threadsLeft - 1) / threadsLeft;
        chunks.add(chunk);
        valuesLeft -= chunk;
    }
    return chunks;
}

Output:

[5, 5]
[4, 3, 3]
[3, 3, 2, 2]
[39, 39, 39, 39, 39, 39, 38, 38, 38, 38, 38, 38, 38]
[1, 1, 1, 1, 1, 0, 0, 0, 0, 0]
Andreas
  • 154,647
  • 11
  • 152
  • 247
  • 3
    Not sure about the need for sarcasm ( I smell sarcasm in your answer but I may be wrong). Does it really solve the problem though... If you have a table of size 13 and 5 threads you would surely give 3 cells to each one of four thread and then just 1 cell to the last one no ? – LBes Aug 20 '15 at 23:22
  • Yeah that's what I was getting at. I understand remainders obviously, but I couldn't see how to write general code in the case where there is a remainder. Because say there are 10 nodes and 4 threads. You could assign 2,2,2,4 simply, but wouldn't 2,2,3,3 be better? – Felm Aug 20 '15 at 23:26
  • Not really sure this piece of code is correct: chunkSize = (arraySize + threadCount - 1) / threadCount. If size = 13 and nbThreads = 3 then we get (13+3-1) / 3 = 5 however 3 * 5 = 15 which is greater than 13... Or maybe I didn't understand your explanation – LBes Aug 20 '15 at 23:32
  • @LBesancon You are correct, and you want 5 so your 3 threads are (5,5,3). if chunk size was 4 your threads would be (4,4,4) but that's only 12 so you have 1 unprocessed value. – Andreas Aug 20 '15 at 23:35
  • You're, in your edit, trying to offer the best way to distribute the cells over the different threads, yet you only take a particular example. I think OP is looking for an algorithm that would work in ANY case. What you're offering here is not enough, it works on a particular case but not all of them. @Felm am I right in thinking that you'd like to get a general "all possible cases" algorithm ? I think the answer is not as simple as just using remainders contrary to what you first hinted in your answer – LBes Aug 20 '15 at 23:36
  • 2
    @LBesancon The algorithm described in the example works for all numbers. Calculate first chunk size, subtract chunk size from value count and decrement thread count, rinse and repeat until done. – Andreas Aug 20 '15 at 23:39
  • Alright it seems that it does it's just that I misunderstood what you consider as threadCount for me it was the total number of threads in the first place ! Sorry for that then – LBes Aug 20 '15 at 23:46
  • Definitely gets the upvote then for the complete answer and the examples :-) – LBes Aug 20 '15 at 23:51
  • Removed sarcasm part of answer – Andreas Aug 20 '15 at 23:54
2

You can use Java8 stream API. It is simpler than custom splitting:

public class Main {

   public static void main(String[] args) throws Exception {
      Object[] gnode = new Object[10];
      Arrays.stream(gnode).parallel().forEach(Main::processOneNode);
   }

   public static void processOneNode(Object node){
       System.out.println(Thread.currentThread().getName());
   }
}

You can customize it. See Custom thread pool in Java 8 parallel stream

ForkJoinPool forkJoinPool = new ForkJoinPool(20);
forkJoinPool.submit(() -> Arrays.stream(gnode).parallel().forEach(Main::processOneNode)).get();
Community
  • 1
  • 1
sibnick
  • 3,995
  • 20
  • 20