In the code below I call a function via a threadpool many times. Within this function I keep track of the fastest execution of the function via a global attributed called FastestMemory.
However, when I print out the value, after the threadpool loop, I am given the original value, as if the global variable is not the same as the variable being updated per loop iteration. I just have 2000000000 returned back, when I know the value of FastestMemory does get assigned to (for example) 253475.
1) Do I need to re-structure this code in order to be able to keep track of the fastest iteration?
2) I seem to be able to execute this code VERY fast, it is taking (ob average) less than 1 millisecond per iteration on four Xeon x7550. Is this normal or is my timing wrong somewhere? C# is taking about 400 miliseconds on average?!?
public class PoolDemo {
static long FastestMemory = 2000000000;
static long SlowestMemory = 0;
static long TotalTime;
static long[] FileArray;
static DataOutputStream outs;
static FileOutputStream fout;
public static void main(String[] args) throws InterruptedException, FileNotFoundException {
int Iterations = Integer.parseInt(args[0]);
int ThreadSize = Integer.parseInt(args[1]);
FileArray = new long[Iterations];
fout = new FileOutputStream("server_testing.csv");
// fixed pool, unlimited queue
ExecutorService service = Executors.newFixedThreadPool(ThreadSize);
ThreadPoolExecutor executor = (ThreadPoolExecutor) service;
for(int i = 0; i<Iterations; i++) {
Task t = new Task(i);
executor.execute(t);
}
executor.shutdown();
service.shutdown();
System.out.println("Fastest: " + FastestMemory);
for(int j=0; j<FileArray.length; j++){
new PrintStream(fout).println(FileArray[j] + ",");
}
}
private static class Task implements Runnable {
private int ID;
static Byte myByte = 0;
public Task(int index) {
this.ID = index;
}
@Override
public void run() {
long Start = System.nanoTime();
int Size1 = 100000;
int Size2 = 2 * Size1;
int Size3 = Size1;
byte[] list1 = new byte[Size1];
byte[] list2 = new byte[Size2];
byte[] list3 = new byte[Size3];
for(int i=0; i<Size1; i++){
list1[i] = myByte;
}
for (int i = 0; i < Size2; i=i+2)
{
list2[i] = myByte;
}
for (int i = 0; i < Size3; i++)
{
byte temp = list1[i];
byte temp2 = list2[i];
list3[i] = temp;
list2[i] = temp;
list1[i] = temp2;
}
long Finish = System.nanoTime();
long Duration = Finish - Start;
FileArray[this.ID] = Duration;
TotalTime += Duration;
System.out.println("Individual Time " + this.ID + " \t: " + (Duration) + " nanoseconds");
if(Duration < FastestMemory){
FastestMemory = Duration;
}
if (Duration > SlowestMemory)
{
SlowestMemory = Duration;
}
}
}
}
The problem is that your main thread is exiting without waiting for the tasks submitted to the executor to terminate.
You can achieve by simply adding a call to ExecutorService#awaitTermination after the call to ExecutorService#shutdown
.
Another problem that you will have is that you have not considering thread safety for your static long
values keeping track of the fastest times. You will either need to add synchronize
blocks or use an AtomicLong
to get safe Compare-And-Set operations.
long
values. Synchronize blocks will be slow, AtomicLong will be faster but harder to use - Tim Bender 2012-04-04 16:59
Task
Callable<Long>
instead of Runnable
, then you can return the durations and compare them in the main thread instead of having to worry about thread safety for those static long
s - trutheality 2012-04-04 17:19
ExecutorService#invokeAll
) - Tim Bender 2012-04-04 19:54
shutdown()
only rejects new tasks submitted to the pool but does nothing with already submitted and running tasks. In order to wait for all tasks to finish you should call:
executor.awaitTermination(1, TimeUnit.MINUTES);
service.awaitTermination(1, TimeUnit.MINUTES);
Also access to FastestMemory
and SlowestMemory
must be somehow synchronized, for example:
synchronized(PoolDemo.class) {
FastestMemory = Math.min(FastestMemory, Duration);
SlowestMemory = Math.max(SlowestMemory, Duration);
}
BTW according to Java Naming Conventions:
all instance, class, and class constants are in mixed case with a lowercase first letter.
First your not waiting for any of the tasks to finish. You need to wait for the executor service to complete.
executor.awaitTermination(...);
Also executor and service both refer to the same object, so there is no need to shutdown both. Actually I don't see any reason to even have executor. All the methods you are calling are part of ExecutorService.
Next the changes to your long variables are not thread safe. What happens if another thread changes the value after your comparisons. Even reads and writes are not atomic for long variables. You should be using AtomicLongs for these variables.
You will want to get the current value from the AtomicLong, compare it to the run of the current value. If the new value indicates that an update should happen, use compareAndSet to make sure no one else has changed the value. If someone else has changed it, run the check again.