Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <h2>Summary</h2> <p>Below are the original response, update 1, and update 2. Update 1 talks about dealing with the race conditions around the test statistic variables by using concurrency structures. Update 2 is a much simpler way of dealing with the race condition issue. Hopefully no more updates from me - sorry for the length of the response but multithreaded programming is complicated!</p> <h2>Original Response</h2> <blockquote> <p>The only difference between the code is that im using the below threadpool</p> </blockquote> <p>I would say that is an absolutely huge difference. It's difficult to compare the performance of the two languages when their thread pool implementations are completely different blocks of code, written in user space. The thread pool implementation could have enormous impact on performance.</p> <p>You should consider using Java's own built-in thread pools. See <a href="http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html" rel="nofollow noreferrer">ThreadPoolExecutor</a> and the entire <a href="http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html" rel="nofollow noreferrer">java.util.concurrent</a> package of which it is part. The <a href="http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html" rel="nofollow noreferrer">Executors</a> class has convenient static factory methods for pools and is a good higher level interface. All you need is JDK 1.5+, though the newer, the better. The fork/join solutions mentioned by other posters are also part of this package - as mentioned, they require 1.7+.</p> <h2>Update 1 - Addressing race conditions by using concurrency structures</h2> <p>You have race conditions around the setting of <code>FastestMemory</code>, <code>SlowestMemory</code>, and <code>TotalTime</code>. For the first two, you are doing the <code>&lt;</code> and <code>&gt;</code> testing and then the setting in more than one step. This is not atomic; there is certainly the chance that another thread will update these values in between the testing and the setting. The <code>+=</code> setting of <code>TotalTime</code> is also non-atomic: a test and set in disguise.</p> <p>Here are some suggested fixes.</p> <p><strong>TotalTime</strong></p> <p>The goal here is a threadsafe, atomic <code>+=</code> of <code>TotalTime</code>.</p> <pre><code>// At the top of everything import java.util.concurrent.atomic.AtomicLong; ... // In PoolDemo static AtomicLong TotalTime = new AtomicLong(); ... // In Task, where you currently do the TotalTime += piece TotalTime.addAndGet (Duration); </code></pre> <p><strong>FastestMemory / SlowestMemory</strong></p> <p>The goal here is testing and updating <code>FastestMemory</code> and <code>SlowestMemory</code> each in an atomic step, so no thread can slip in between the test and update steps to cause a race condition.</p> <p><strong>Simplest approach</strong>:</p> <p>Protect the testing and setting of the variables using the class itself as a monitor. We need a monitor that contains the variables in order to guarantee synchronized visibility (thanks @A.H. for catching this.) We have to use the class itself because everything is <code>static</code>. </p> <pre><code>// In Task synchronized (PoolDemo.class) { if (Duration &lt; FastestMemory) { FastestMemory = Duration; } if (Duration &gt; SlowestMemory) { SlowestMemory = Duration; } } </code></pre> <p><strong>Intermediate approach</strong>:</p> <p>You may not like taking the whole class for the monitor, or exposing the monitor by using the class, etc. You could do a separate monitor that does not itself contain <code>FastestMemory</code> and <code>SlowestMemory</code>, but you will then run into synchronization visibility issues. You get around this by using the <code>volatile</code> keyword.</p> <pre><code>// In PoolDemo static Integer _monitor = new Integer(1); static volatile long FastestMemory = 2000000; static volatile long SlowestMemory = 0; ... // In Task synchronized (PoolDemo._monitor) { if (Duration &lt; FastestMemory) { FastestMemory = Duration; } if (Duration &gt; SlowestMemory) { SlowestMemory = Duration; } } </code></pre> <p><strong>Advanced approach</strong>:</p> <p>Here we use the <code>java.util.concurrent.atomic</code> classes instead of monitors. Under heavy contention, this should perform better than the <code>synchronized</code> approach. Try it and see.</p> <pre><code>// At the top of everything import java.util.concurrent.atomic.AtomicLong; . . . . // In PoolDemo static AtomicLong FastestMemory = new AtomicLong(2000000); static AtomicLong SlowestMemory = new AtomicLong(0); . . . . . // In Task long temp = FastestMemory.get(); while (Duration &lt; temp) { if (!FastestMemory.compareAndSet (temp, Duration)) { temp = FastestMemory.get(); } } temp = SlowestMemory.get(); while (Duration &gt; temp) { if (!SlowestMemory.compareAndSet (temp, Duration)) { temp = SlowestMemory.get(); } } </code></pre> <p>Let me know what happens after this. It may not fix your problem, but the race condition around the very variables that track your performance is too dangerous to ignore.</p> <p>I originally posted this update as a comment but moved it here so that I would have room to show code. This update has been through a few iterations - thanks to <a href="https://stackoverflow.com/users/947357/a-h">A.H.</a> for catching a bug I had in an earlier version. Anything in this update supersedes anything in the comment.</p> <p>Last but not least, an excellent source covering all this material is <a href="http://rads.stackoverflow.com/amzn/click/0321349601" rel="nofollow noreferrer">Java Concurrency in Practice</a>, the best book on Java concurrency, and one of the best Java books overall.</p> <h2>Update 2 - Addressing race conditions in a much simpler way</h2> <p>I recently noticed that your current code will never terminate unless you add <code>executorService.shutdown()</code>. That is, the non-daemon threads living in that pool must be terminated or else the main thread will never exit. This got me to thinking that since we have to wait for all threads to exit, why not compare their durations after they finished, and thus bypass the concurrent updating of <code>FastestMemory</code>, etc. altogether? This is simpler and could be faster; there's no more locking or CAS overhead, and you are already doing an iteration of <code>FileArray</code> at the end of things anyway.</p> <p>The other thing we can take advantage of is that your concurrent updating of <code>FileArray</code> is perfectly safe, since each thread is writing to a separate cell, and since there is no reading of <code>FileArray</code> during the writing of it.</p> <p>With that, you make the following changes:</p> <pre><code>// In PoolDemo // This part is the same, just so you know where we are for(int i = 0; i&lt;Iterations; i++) { Task t = new Task(i); executor.execute(t); } // CHANGES BEGIN HERE // Will block till all tasks finish. Required regardless. executor.shutdown(); executor.awaitTermination(10, TimeUnit.SECONDS); for(int j=0; j&lt;FileArray.length; j++){ long duration = FileArray[j]; TotalTime += duration; if (duration &lt; FastestMemory) { FastestMemory = duration; } if (duration &gt; SlowestMemory) { SlowestMemory = duration; } new PrintStream(fout).println(FileArray[j] + ","); } . . . // In Task // Ending of Task.run() now looks like this long Finish = System.currentTimeMillis(); long Duration = Finish - Start; FileArray[this.ID] = (int)Duration; System.out.println("Individual Time " + this.ID + " \t: " + (Duration) + " ms"); </code></pre> <p>Give this approach a shot as well.</p> <p>You should definitely be checking your C# code for similar race conditions.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload