Have a look at the code below:
class Test
{
public static void main(String abc[])
{
for( int N=1; N <= 1_000_000_000; N=N*10)
{
long t1 = System.nanoTime();
start(N);
long t2 = System.nanoTime() - t1;
System.out.println("Time taken for " + N + " : " + t2);
}
}
public static void start( int N )
{
int j=1;
for(int i=0; i<=N; i++)
j=j*i;
}
}
The output produced by the above question is:
Time taken for 1 : 7267
Time taken for 10 : 3312
Time taken for 100 : 7908
Time taken for 1000 : 51181
Time taken for 10000 : 432124
Time taken for 100000 : 4313696
Time taken for 1000000 : 9347132
Time taken for 10000000 : 858
Time taken for 100000000 : 658
Time taken for 1000000000 : 750
Questions:
1.) Why is time taken for N=1 unusually greater than the N=10 ? (sometimes it even exceeds N=100)
2.) Why is time taken for N=10M and onwards unusually lower ?
The pattern indicated in the above questions is profound and remains even after many iterations.
Is there any connection to memoization here ?
EDIT:
Thank you for your answers. I thought of replacing the method call with the actual loop. But now, there is no JIT Optimization. Why not ? Is putting the statements in a method facilitating in the optimization process ?
The modified code is below:
class test
{
public static void main(String abc[])
{
for( int k=1; k<=3; k++)
{
for( int N=1; N<=1_000_000_000; N=N*10)
{
long t1 = System.nanoTime();
int j=1;
for(int i=0; i<=N; i++)
j=j*i;
long t2 = System.nanoTime() - t1;
System.out.println("Time taken for "+ N + " : "+ t2);
}
}
}
}
EDIT 2:
The output of above modified code:
Time taken for 1 : 2160
Time taken for 10 : 1142
Time taken for 100 : 2651
Time taken for 1000 : 19453
Time taken for 10000 : 407754
Time taken for 100000 : 4648124
Time taken for 1000000 : 12859417
Time taken for 10000000 : 13706643
Time taken for 100000000 : 136928177
Time taken for 1000000000 : 1368847843
Time taken for 1 : 264
Time taken for 10 : 233
Time taken for 100 : 332
Time taken for 1000 : 1562
Time taken for 10000 : 17341
Time taken for 100000 : 136869
Time taken for 1000000 : 1366934
Time taken for 10000000 : 13689017
Time taken for 100000000 : 136887869
Time taken for 1000000000 : 1368178175
Time taken for 1 : 231
Time taken for 10 : 242
Time taken for 100 : 328
Time taken for 1000 : 1551
Time taken for 10000 : 13854
Time taken for 100000 : 136850
Time taken for 1000000 : 1366919
Time taken for 10000000 : 13692465
Time taken for 100000000 : 136833634
Time taken for 1000000000 : 1368862705
1.) Why is time taken for N=1 unusually greater than the N=10
Because it's the first time the VM has seen that code - it may decide to just interpret it, or it will take a little bit of time JITting it to native code, but probably without optimization. This is one of the "gotchas" of benchmarking Java.
2.) Why is time taken for N=10M and onwards unusually lower ?
At that point, the JIT has worked harder to optimize the code - reducing it to almost nothing.
In particular, if you run this code multiple times (just in a loop), you'll see the effect of the JIT compiler optimizing:
Time taken for 1 : 3732
Time taken for 10 : 1399
Time taken for 100 : 3266
Time taken for 1000 : 26591
Time taken for 10000 : 278508
Time taken for 100000 : 2496773
Time taken for 1000000 : 4745361
Time taken for 10000000 : 933
Time taken for 100000000 : 466
Time taken for 1000000000 : 933
Time taken for 1 : 933
Time taken for 10 : 467
Time taken for 100 : 466
Time taken for 1000 : 466
Time taken for 10000 : 933
Time taken for 100000 : 466
Time taken for 1000000 : 933
Time taken for 10000000 : 467
Time taken for 100000000 : 467
Time taken for 1000000000 : 466
Time taken for 1 : 467
Time taken for 10 : 467
Time taken for 100 : 466
Time taken for 1000 : 466
Time taken for 10000 : 466
Time taken for 100000 : 467
Time taken for 1000000 : 466
Time taken for 10000000 : 466
Time taken for 100000000 : 466
Time taken for 1000000000 : 466
As you can see, after the first the loop takes the same amount of time whatever the input (module noise - basically it's always either ~460ns or ~933ns, unpredictably) which means the JIT has optimized the loop out.
If you actually returned j, and changed the initial value of i to 1 instead of 0, you'll see the kind of results you expect. The change of the initial value of i to 1 is because otherwise the JIT can spot that you'll always end up returning 0.
youre actually benchmarking java's JIT. if i modify yout code a bit:
class Test
{
public static void main(String abc[])
{
for( int N=1; N <= 1_000_000_000; N=N*10)
{
long t1 = System.nanoTime();
start(N);
long t2 = System.nanoTime() - t1;
System.out.println("Time taken for " + N + " : " + t2);
}
for( int N=1; N <= 1_000_000_000; N=N*10)
{
long t1 = System.nanoTime();
start(N);
long t2 = System.nanoTime() - t1;
System.out.println("Time taken for " + N + " : " + t2);
}
}
public static void start( int N )
{
int j=1;
for(int i=0; i<=N; i++)
j=j*i;
}
}
i get this:
Time taken for 1 : 1811
Time taken for 10 : 604
Time taken for 100 : 1510
Time taken for 1000 : 10565
Time taken for 10000 : 104439
Time taken for 100000 : 829173
Time taken for 1000000 : 604
Time taken for 10000000 : 302
Time taken for 100000000 : 0
Time taken for 1000000000 : 0
Time taken for 1 : 0
Time taken for 10 : 302
Time taken for 100 : 0
Time taken for 1000 : 302
Time taken for 10000 : 301
Time taken for 100000 : 302
Time taken for 1000000 : 0
Time taken for 10000000 : 0
Time taken for 100000000 : 0
Time taken for 1000000000 : 302
never benchmark a "cold" system. always repeat every measurement several times and discard the 1st few ones because the optimizations have not yet kicked in
The reason is that 1) you don't return the value, and 2) the result of the calculation is always 0. Eventually the JIT will simply compile the loop away.
You get your expected behaviour if you change your loop to:
public static int start(int N) {
int j = 1;
for (int i = 1; i <= N; i++)
j = j * i;
return j;
}
Note that I have both changed the loop init to int i = 1 and added return j. If I only do one of those, the loop will (eventually) still be compiled away.
This will produce the following series (if executed twice):
Time taken for 1 : 2934
Time taken for 10 : 1466
Time taken for 100 : 3422
Time taken for 1000 : 20534
Time taken for 10000 : 191644
Time taken for 100000 : 1898845
Time taken for 1000000 : 1210489
Time taken for 10000000 : 11884401
Time taken for 100000000 : 115257525
Time taken for 1000000000 : 1061254223
Time taken for 1 : 978
Time taken for 10 : 978
Time taken for 100 : 978
Time taken for 1000 : 2444
Time taken for 10000 : 11244
Time taken for 100000 : 103644
Time taken for 1000000 : 1030089
Time taken for 10000000 : 10448535
Time taken for 100000000 : 107299391
Time taken for 1000000000 : 1072580803
Related
Why does my loop not work?
I'm trying to increment by 5 and the output the time it took to increment with the linear method
for(n=5;n<=10;n=n+5) {
long startTime= System.currentTimeMillis();
System.out.println(startTime);
System.out.println("\nOddanaci(" + n +")\n" );
linear(n);
long endTime= System.currentTimeMillis();
long diff= endTime - startTime;
System.out.println("\n\nThe Total time it took to run this program is\n"+diff);
}
my output
Please enter the a non-negative value to find its Oddonacci sequence: 3
Here is the Oddonacci(3) sequence
1 1 1
The method has been called 1 times.
1537756698523
Oddanaci(5)
1 1 1 3 5
The Total time it took to run this program is
1
1537756698524
Oddanaci(10)
1 1 1 3 5 9 17 31 57 105
The Total time it took to run this program is
0
Why does it output as zero? Doesn't n iterate?
I'm doing a simulation of a Bank where I have one queue and three cashiers; the objective is to obtain statistics, for example the average wait time of the customers. Every cashier has a different attention time and a new client comes enters the bank every 1.5 minutes. The bank just runs 5 hours. Now, is there a way I could program it in real time (cause I think its the only way) and then some way fast foward the jvm in order to get the statistics sooner?
You cannot fast-forward the JVM, but you can program this to simulate in non real-time to get statistics soon. Play with variables that represents time in a small enough unit of time (for example in seconds) and do the simulation normally, increasing the time in one unit. For the queue you can use a deque containing the instants when every client entered the bank, and then iterate over time.
private static int TOTAL_TIME = 5*3600; // Hours to seconds
private static int TIME_BETWEEN_CLIENTS = 90; // In seconds
private static int CASHIERS = 3;
public static void main(String[] args) {
// Create cashiers and assign range of attentions time to each cashier, in seconds
// This is an example for cashiers with 3, 3~6 and 4.5~15 minutes
List<Cashier> cashiers = new ArrayList<Cashier>(CASHIERS);
cashiers.add(new Cashier(180, 180));
cashiers.add(new Cashier(180, 360));
cashiers.add(new Cashier(270, 900));
int time = 0; // Counting variable in seconds
int waitingTime = 0; // Save here all waiting time for all clients
int clients = 0; // Save here all clients
// Register here all available cashiers
ArrayList<Cashier> freeCashiers;
Deque<Integer> queue = new ArrayDeque<Integer>(); // Clients queue
// Iterate until bank closes and all clients have been attended
while (time < TOTAL_TIME || !queue.isEmpty()) {
// New client if the bank is not closed
if (time < TOTAL_TIME && time%TIME_BETWEEN_CLIENTS == 0) {
queue.add(time); // Register customer start waiting time
clients++;
}
// Check for free cashiers when someone is on queue
if (!queue.isEmpty()) {
freeCashiers = new ArrayList<Cashier>(CASHIERS);
for (Cashier c : cashiers) {
if (c.isFree(time))
freeCashiers.add(c);
}
if (!freeCashiers.isEmpty()) {
// Register spent time for the removed client
waitingTime += time - queue.removeFirst();
// Select a random cashier from all the available cashiers
Cashier randomAvailableCashier = freeCashiers.get(Cashier.RANDOM.nextInt(freeCashiers.size()));
// Register when the randomly selected cashier will be free again
randomAvailableCashier.attendNewClient(time);
}
}
time++; // Adds one second
}
// Calculate statistics
int avgWaitingTime = waitingTime/clients; // In seconds
System.out.println("Average waiting time on queue: " + formatTime(avgWaitingTime));
}
/**
* Formats a time in minutes and seconds
* #param time the time in seconds
* #return the formatted time
*/
private static String formatTime(int time) {
StringBuilder result = new StringBuilder();
if (time > 60) {
result.append(time/60).append(" minutes");
time %= 60;
if (time > 0)
result.append(" and ");
else
result.append(".");
}
if (time > 0)
result.append(time).append(" seconds.");
return result.toString();
}
And the Cashier class:
public class Cashier {
public static final Random RANDOM = new Random();
private int minAttentionTime, maxAttentionTime, endTime;
/**
* Constructs new Cashier with a range of possible attention time, in seconds
* #param minAttentionTime in seconds
* #param maxAttentionTime in seconds
*/
public Cashier(int minAttentionTime, int maxAttentionTime) {
this.minAttentionTime = minAttentionTime;
this.maxAttentionTime = maxAttentionTime;
endTime = 0;
}
/**
* Register end time with a random attention time in the range.
* #param currentTime the current time in seconds
*/
public void attendNewClient(int currentTime) {
endTime = currentTime + getRandomNumberInRange(minAttentionTime, maxAttentionTime);
}
/**
* Returns if this cashier is available
* #param currentTime the current time in seconds
* #return true if this cashier is free, false otherwise
*/
public boolean isFree(int currentTime) {
return currentTime >= endTime;
}
/**
* Returns a random number in range [min, max]
* #param min the minimum number, inclusive
* #param max the maximum number, inclusive
* #return a random number in range [min, max]
*/
private int getRandomNumberInRange(int min, int max) {
return RANDOM.nextInt(max - min + 1) + min;
}
public String toString() {
return String.valueOf(endTime);
}
}
With this implementation a new client will go to a random free cashier.
If you instead used a queue system, you could go very quickly. The queue would contain events that are coming up, and a timestamp of when they are due. The queue would be organized by event timestamp. Initially, you could seed this queue with a new customer every 1.5 minutes, and an event for the bank closing in 5 hours. Then you pop the first event off the queue, figure out what that triggers. For example, the first event of a customer entering causes you to increase the customer wait line count. A teller becoming free causes the wait line count to reduce and an agent-customer activity that last for some random time length. This causes a new event (another teller being free) to be placed in the queue. When a teller being free occurs, and there are no customers in line, you set an empty teller flag, which would be checked when you next handle a customer entering event. You could quickly run through an entire day within a few seconds with approach, and you can gather statistics on whatever you want at each event.
If you don't mind to use formulas here is my math. If some cashier has time T1 for processing one person then this cashier processes 1/T1 people per time unit. Number of people processed by N cashiers are added. Coming back from processing speed to processing time we get common time for processing one person by all cashiers:
Tcom = 1/(1/T1 + 1/T2 + ... + 1/Tn)
You compare this time with your 1.5 minutes and if Tcom <= 1.5 minutes then your queue will be mostly empty or consist of 1 person (and average waiting time will be around half of average of Ti). In case Tcom > 1.5 minutes your queue will grow constantly by one person each Tcom - 1.5 minutes. Queue length at the end of working day will be Lavg = 5 * 60 / (Tcom - 15) people (and half of it in average). Each person in head of the queue is removed from it each Tcom minutes. It means that average waiting time is going to be Tcom * Lavg / 2.
I know it's all very rough estimates but why not?
(You deleted your other almost identical question. Here's the long answer i wrote before knowing that. Good that you had this copy, so i didn't waste my time)
Here is the functional pseudo. I actually coded it, out of curiosity.
I use a computer language called APL, which is most likely unknown to you, hence pseudo.
You need to create a 2-dimensional table, ie. an array, with 6 columns. If you cannot create 2-dimensional arrays, you can alternatively (hopefully) create 1-dimensional arrays, ie. lists, where one list holds the values of one column in the table. The important thing is that you can index into arrays. If you use listst, you must extend the code a bit.
You will only work with integer values.
The solution is that you pre-create a table, which holds the customer arrival times in column 1. All other columns hold zeroes to start with. Then you loop through the table, updating the other columns, row-by-row, using a help variable for the cashiers.
Once you have looped through all rows in the table, you extract the desired results from it, by additional calculation.
For the cashiers actions, you need another 2-dimensional table, holding [nr of cashiers] rows and 3 columns. Similarly, you can replace it with 3 lists, one per table column. Columns 2 and 3 in this small table accumulate each cashiers tim ethey spent serving a customer, and time they spent waiting for customers.
You do not work with clock times, but with seconds since day start. For example 8am equals 28800 (ie. number of seconds elapsed since day start). To this value, you can easily add customer arrival times with 90 seconds increments (or any, arbitrary amount of seconds intervals). You pre-create customer arrival times for the entire 5-hour (or any hour) day.
// Create a table with 6 columns. Columns are
// 1: Customer arrival timestamp (first value is 28800) [seconds since midnight]
// 2: The timestamp the customer was attended [seconds since midnight]
// 3: The time a customer had to wait [seconds]
// 4: The time the customer spent with the cashier [seconds]
// 5: The timestamp the customer leaves the bank [seconds since midnight]
// 6: The cashier # that provided service, 1,2 or 3 in your case
//
// If the customer arrive at regular 90 s intervals, the table now looks like this
// (10 first rows only, but you must populate it up to bank closing time)
// which would need ca 200 rows. Call this table "Cust":
// 28800 0 0 0 0 0
// 28890 0 0 0 0 0
// 28980 0 0 0 0 0
// 29070 0 0 0 0 0
// 29160 0 0 0 0 0
// 29250 0 0 0 0 0
// 29340 0 0 0 0 0
// 29430 0 0 0 0 0
// 29520 0 0 0 0 0
// 29610 0 0 0 0 0
//
// Create a variable for the cashiers, 1 row per cashier. Columns are:
// 1. An "action timestamp", initially 28800 [seconds since midnight]
// 2. Accumulation of cashiers attention time [seconds]
// 3. Accumulation of cashiers wait/slack time [seconds]
//
// The table now looks like this
// (add more rows if you have more cashiers). Call this table "Cash":
// 28800 0 0
// 28800 0 0
// 28800 0 0
rows = [numers of rows in Cust]
i = 0
:While i < rows
// Note! This pseudocode uses 0-origin, ie. array[0] is the first element
// Find the row number of Cash with the _smallest_ value in it's column 1
row = [0, 1 or 2] // You commonly first find the smallest number, then compare it against each row. The match is the row with smallest number.
// Attention time for this customer (you said "range of"),
// we use 265 s now, for simplicity, but you can give time another value each time
time = 265
:If Cust[i;0]<Cash[row;0] // Customer has waited
Cust[i;1]=Cash[row;0] // Timestamp this Customer got service
Cust[i;3]=time // Time spent with cashier
Cust[i;4]=Cash[row;0]+time // Timestamp customer leaves the bank
Cust[i;5]=row // Which cashier provided the service
Cash[row;1]+=time // Increase accumulated attend time for cashier
Cash[row;0]+=time // Next timestamp this cashier is free
:Else // Cashier had free time (bank was empty until now)
Cash[row;2]+=Cust[i;0]-Cash[row;0] // Accumulate freetime for cashier
Cash[row;1]+=time // Accumulate attend time for cashier
Cust[i;1]=Cust[i;0] // There was no wait time for customer
Cust[i;3]=time // Time spent with cashier
Cust[i;4]=Cust[i;0]+time // Timestamp customer leaves the bank
Cust[i;5]=row // Which cashier provided the service
Cash[row;0]=Cust[i;0]+time // Next timestamp this cashier is free
:End
i+←1
:End
// Resolve customer wait times, equals [time attended] - [time arrived] and
// populate 3rd column of Cust (you must probably loop), row by row
Cust[n;2] = Cust[n;1] - Cust[n;0] // n = each row
// Cust now looks like this:
// 28800 28800 0 265 29065 0
// 28890 28890 0 265 29155 1
// 28980 28980 0 265 29245 2
// 29070 29070 0 265 29335 0
// 29160 29160 0 265 29425 1
// 29250 29250 0 265 29515 2
// 29340 29340 0 265 29605 0
// 29430 29430 0 265 29695 1
// 29520 29520 0 265 29785 2
// 29610 29610 0 265 29875 0
fnAvg = [function that calculates the average of a list of numbers]
// Extract the results
// Note: For example "Cust[;2]" means _all rows in column 2 of Cust_, ie. a list of numbers
// You don't need to create any lists though, just loop through them all and calculate/accumulate
'Nr of customers attended immediately: ',[number of zeroes in Cust[;2]]
'Nr of customers who waited: ',[number of non-zeroes in Cust[;2]]
'Wait times: ',[all numbers in Cust[;2] - this is a list of numbers]
'Average wait times: ',[avg Cust[;2]]
'Average wait times (only those who waited): ',[avg Cust[;2] elements that are non-zero]
'Total cashier attend times: ',Cash[;1]
'Total cashier free times: ',Cash[;2]
// And finally a verification calc (backwards calc, just for curiosity)
'Check: Total customer existance time: ',Cust[i-1;4]-Cust[0;0]
'Check: Cashier total times (should be close to above value): ',[sum of Cash[n;1 2]]
The result with the arguments above is (numbers are seconds unless otherwise said):
Nr of customers attended immediately: 10
Nr of customers who waited: 0
Wait times: 0 0 0 0 0 0 0 0 0 0
Average wait times: 0
Average wait times (only those who waited): 0
Total cashier attend times: 1060 795 795
Total cashier free times: 15 100 190
Check: Total customer existance time: 1075
Check: Cashier total times (should be close to previous row): 1075 895 985
If you have 20 customers arriving, and use random attend times between 100 and 480 seconds, you may get for example:
Nr of customers served immediately: 8
Nr of customers waited: 12
Wait times: 0 0 0 0 0 14 0 0 0 200 120 147 183 122 149 111 185 178 244 218
Average wait times: 93.55
Average wait times (only those who waited): 155.9166667
Total cashier attend times: 1820 1836 1819
Total cashier free times: 217 197 309
Check: Total customer existance time: 2128
Check: Cashier total times (should be close to previous row): 2037 2033 2128
with Cust (edited: was erratically Cash) table looking like this:
28800 28800 0 160 28960 0
28890 28890 0 433 29323 1
28980 28980 0 114 29094 2
29070 29070 0 194 29264 0
29160 29160 0 117 29277 2
29250 29264 14 149 29413 0
29340 29340 0 470 29810 2
29430 29430 0 390 29820 1
29520 29520 0 417 29937 0
29610 29810 200 253 30063 2
29700 29820 120 389 30209 1
29790 29937 147 155 30092 0
29880 30063 183 445 30508 2
29970 30092 122 169 30261 0
30060 30209 149 216 30425 1
30150 30261 111 403 30664 0
30240 30425 185 408 30833 1
30330 30508 178 220 30728 2
30420 30664 244 173 30837 0
30510 30728 218 200 30928 2
Try with populating the 1st column of Cust with different, more random customer arrivbal times, and for each iteration, the time with values that fit your real environment better.
This pseudo supports both "bank temporarily empty" and "long que all time", and also supports the "Once the bank closes after 5 hours, every client gets attended"-criterion (the remaining customers will be cleaned up").
Good luck! :-)
I came up with a question that initially was going to be a Q/A style question.
The original question:
How much does a higher scale in BigDecimal#divide() affect performance?
So, I created this SSCCE:
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String args[]) {
int[] scales = new int[] {1, 10, 50, 100, 500, 1000, 5000, 100000, 1000000};
for(Integer scale : scales) {
long start = System.nanoTime();
BigDecimal.ONE.divide(BigDecimal.valueOf(7), scale, RoundingMode.HALF_UP);
long end = System.nanoTime();
long elapsed = end - start;
String elapsed_str = String.format("%d mins, %d secs, %d millis, %d nanos",
TimeUnit.NANOSECONDS.toMinutes(elapsed),
TimeUnit.NANOSECONDS.toSeconds(elapsed) - TimeUnit.MINUTES.toSeconds(TimeUnit.NANOSECONDS.toMinutes(elapsed)),
TimeUnit.NANOSECONDS.toMillis(elapsed) - TimeUnit.SECONDS.toMillis(TimeUnit.NANOSECONDS.toSeconds(elapsed)),
elapsed - TimeUnit.MILLISECONDS.toNanos(TimeUnit.NANOSECONDS.toMillis(elapsed))
);
System.out.println("Time for scale = " + scale + ": " + elapsed_str);
}
}
}
The output was thus:
Time for scale = 1: 0 mins, 0 secs, 2 millis, 883903 nanos
Time for scale = 10: 0 mins, 0 secs, 0 millis, 13995 nanos
Time for scale = 50: 0 mins, 0 secs, 1 millis, 138727 nanos
Time for scale = 100: 0 mins, 0 secs, 0 millis, 645636 nanos
Time for scale = 500: 0 mins, 0 secs, 1 millis, 250220 nanos
Time for scale = 1000: 0 mins, 0 secs, 4 millis, 38957 nanos
Time for scale = 5000: 0 mins, 0 secs, 15 millis, 66549 nanos
Time for scale = 100000: 0 mins, 0 secs, 500 millis, 873987 nanos
Time for scale = 1000000: 0 mins, 50 secs, 183 millis, 686684 nanos
As the order of magnitude increases, the performance is affected exponentially. But what had me scratching my head were these lines:
Time for scale = 1: 0 mins, 0 secs, 2 millis, 883903 nanos
Time for scale = 10: 0 mins, 0 secs, 0 millis, 13995 nanos
Time for scale = 50: 0 mins, 0 secs, 1 millis, 138727 nanos
Time for scale = 100: 0 mins, 0 secs, 0 millis, 645636 nanos
Time for scale = 500: 0 mins, 0 secs, 1 millis, 250220 nano
It appears that a scale of 10 is optimal for BigDecimal#divide()? And a scale of 100 is faster than 50? I thought this might just be an anomaly, so I ran it again (this time, omitting the highest two scales because I didn't want to wait 50 seconds :)) and this is the result:
Time for scale = 1: 0 mins, 0 secs, 3 millis, 440903 nanos
Time for scale = 10: 0 mins, 0 secs, 0 millis, 10263 nanos
Time for scale = 50: 0 mins, 0 secs, 0 millis, 833169 nanos
Time for scale = 100: 0 mins, 0 secs, 0 millis, 487492 nanos
Time for scale = 500: 0 mins, 0 secs, 0 millis, 802846 nanos
Time for scale = 1000: 0 mins, 0 secs, 2 millis, 475715 nanos
Time for scale = 5000: 0 mins, 0 secs, 16 millis, 646117 nanos
Again, 10 is considerably faster than 1, and 100 is again faster than 50.
I tried again and again, and 100 was always faster than 50. And a scale of 1 was always slower than everything less than 1000.
Anyone have an explanation?
Java code is optimised dynamically but the first time you run it, it has to be loaded. To avoid confusing results caused by the code being re-compiled as you run it, I suggest
doing your longest run first, not last.
run the test for at least 2 seconds.
ignore the first run of at least 3 to 5 runs (You only have one in this case)
I would keep the scale simple so you can compare all the results. In your case I would do each one at least 1000 times and print the average in micro-seconds.
Have a look at this question :
The code:
class test
{
public static void main(String abc[])
{
for( int k=1; k<=3; k++)
{
for( int N=1; N<=1_000_000_000; N=N*10)
{
long t1 = System.nanoTime();
int j=1;
for(int i=0; i<=N; i++)
j=j*i;
long t2 = System.nanoTime() - t1;
System.out.println("Time taken for "+ N + " : "+ t2);
}
}
}
}
The output of above code:
Time taken for 1 : 2160
Time taken for 10 : 1142
Time taken for 100 : 2651
Time taken for 1000 : 19453
Time taken for 10000 : 407754
Time taken for 100000 : 4648124
Time taken for 1000000 : 12859417
Time taken for 10000000 : 13706643
Time taken for 100000000 : 136928177
Time taken for 1000000000 : 1368847843
Time taken for 1 : 264
Time taken for 10 : 233
Time taken for 100 : 332
Time taken for 1000 : 1562
Time taken for 10000 : 17341
Time taken for 100000 : 136869
Time taken for 1000000 : 1366934
Time taken for 10000000 : 13689017
Time taken for 100000000 : 136887869
Time taken for 1000000000 : 1368178175
Time taken for 1 : 231
Time taken for 10 : 242
Time taken for 100 : 328
Time taken for 1000 : 1551
Time taken for 10000 : 13854
Time taken for 100000 : 136850
Time taken for 1000000 : 1366919
Time taken for 10000000 : 13692465
Time taken for 100000000 : 136833634
Time taken for 1000000000 : 1368862705
In the loop, even though the value of i starts from 0, indicating the product to be zero, there is no JIT Optimization. Why not ?
In the link provided above, I had previously put the for loop in a method call, which the JIT was optimizing. Is putting the statements in a method facilitating in the optimization process ?
In your previous question the JIT optimized away the complete code of the method start without any analysis as to what number happened to be present in the variables upon method return. This is because you chose to make your method void, giving the JIT a dead-easy clue that any values calculated will be discarded.
Contrasting your current example with the one from your previous question, there are no void methods called so naturally the optimization does not occur. Why there is not some other optimization which would help this completely different case is an unanswerable question. There is just no such optimization in the specefic JVM implementation, and the specific JVM invocation, with which you have tested your code.
The loop itself does get jitted (as observed by the slightly lower running times on second and third execution), however eliminating the entire loop is - afaik - only done when the method itself is executed multiple times, because only then the JIT has sufficient runtime information to be sure it can actually eliminate it without consequence.
If I change your code, the loop is eliminated on the third invocation:
public class LoopJit2 {
public static void main(String abc[]) {
for (int x = 0; x < 3; x++) {
loopMethod();
}
}
private static void loopMethod() {
for (int N = 1; N <= 1_000_000_000; N = N * 10) {
long t1 = System.nanoTime();
int j = 1;
for (int i = 0; i <= N; i++)
j = j * i;
long t2 = System.nanoTime() - t1;
System.out.println("Time taken for " + N + " : " + t2);
}
}
}
Time series:
Time taken for 1 : 1466
Time taken for 10 : 1467
Time taken for 100 : 2934
Time taken for 1000 : 20044
Time taken for 10000 : 201422
Time taken for 100000 : 1993200
Time taken for 1000000 : 4038223
Time taken for 10000000 : 11182357
Time taken for 100000000 : 111290192
Time taken for 1000000000 : 1038002176
Time taken for 1 : 1466
Time taken for 10 : 1467
Time taken for 100 : 2934
Time taken for 1000 : 20044
Time taken for 10000 : 10755
Time taken for 100000 : 124667
Time taken for 1000000 : 1010045
Time taken for 10000000 : 10201156
Time taken for 100000000 : 103184413
Time taken for 1000000000 : 1019723107
Time taken for 1 : 978
Time taken for 10 : 1467
Time taken for 100 : 1467
Time taken for 1000 : 1955
Time taken for 10000 : 978
Time taken for 100000 : 489
Time taken for 1000000 : 977
Time taken for 10000000 : 977
Time taken for 100000000 : 978
Time taken for 1000000000 : 978
I want to get data from the database (MySQL) by JPA, I want it sorted by some column value.
So, what is the best practice, to:
Retrieve the data from the database as list of objects (JPA), then
sort it programmatically using some java APIs.
OR
Let the database sort it by using a sorting select query.
Thanks in advance
If you are retrieving a subset of all the database data, for example displaying 20 rows on screen out of 1000, it is better to sort on the database. This will be faster and easier and will allow you to retrieve one page of rows (20, 50, 100) at a time instead of all of them.
If your dataset is fairly small, sorting in your code may be more convenient if you want implement a complex sort. Usually this complex sort can be done in SQL but not as easily as in code.
The short of it is, the rule of thumb is sort via SQL, with some edge cases to the rule.
In general, you're better off using ORDER BY in your SQL query -- this way, if there is an applicable index, you may be getting your sorting "for free" (worst case, it will be the same amount of work as doing it in your code, but often it may be less work than that!).
I ran into this very same question, and decided that I should run a little benchmark to quantify the speed differences. The results surprised me. I would like to post my experience with this very sort of question.
As with a number of the other posters here, my thought was that the database layer would do the sort faster because they are supposedly tuned for this sort of thing. #Alex made a good point that if the database already has an index on the sort, then it will be faster. I wanted to answer the question which raw sorting is faster on non-indexed sorts. Note, I said faster, not simpler. I think in many cases letting the db do the work is simpler and less error prone.
My main assumption was that the sort would fit in main memory. Not all problems will fit here, but a good number do. For out of memory sorts, it may well be that databases shine here, though I did not test that. In the case of in memory sorts all of java/c/c++ outperformed mysql in my informal benchmark, if one could call it that.
I wish I had had more time to more thoroughly compare the database layer vs application layer, but alas other duties called. Still, I couldn't help but record this note for others who are traveling down this road.
As I started down this path I started to see more hurdles. Should I compare data transfer? How? Can I compare time to read db vs time to read a flat file in java? How to isolate the sort time vs data transfer time vs time to read the records? With these questions here was the methodology and timing numbers I came up with.
All times in ms unless otherwise posted
All sort routines were the defaults provided by the language (these are good enough for random sorted data)
All compilation was with a typical "release-profile" selected via netbeans with no customization unless otherwise posted
All tests for mysql used the following schema
mysql> CREATE TABLE test_1000000
(
pk bigint(11) NOT NULL,
float_value DOUBLE NULL,
bigint_value bigint(11) NULL,
PRIMARY KEY (pk )
) Engine MyISAM;
mysql> describe test_1000000;
+--------------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------+------------+------+-----+---------+-------+
| pk | bigint(11) | NO | PRI | NULL | |
| float_value | double | YES | | NULL | |
| bigint_value | bigint(11) | YES | | NULL | |
+--------------+------------+------+-----+---------+-------+
First here is a little snippet to populate the DB. There may be easier ways, but this is what I did:
public static void BuildTable(Connection conn, String tableName, long iterations) {
Random ran = new Random();
Math.random();
try {
long epoch = System.currentTimeMillis();
for (long i = 0; i < iterations; i++) {
if (i % 100000 == 0) {
System.out.println(i + " next 100k");
}
PerformQuery(conn, tableName, i, ran.nextDouble(), ran.nextLong());
}
} catch (Exception e) {
logger.error("Caught General Exception Error from main " + e);
}
}
MYSQL Direct CLI results:
select * from test_10000000 order by bigint_value limit 10;
10 rows in set (2.32 sec)
These timings were somewhat difficult as the only info I had was the time reported after the execution of the command.
from mysql prompt for 10000000 elements it is roughly 2.1 to 2.4 either for sorting bigint_value or float_value
Java JDBC mysql call (similar performance to doing sort from mysql cli)
public static void SortDatabaseViaMysql(Connection conn, String tableName) {
try {
Statement stmt = conn.createStatement();
String cmd = "SELECT * FROM " + tableName + " order by float_value limit 100";
ResultSet rs = stmt.executeQuery(cmd);
} catch (Exception e) {
}
}
Five runs:
da=2379 ms
da=2361 ms
da=2443 ms
da=2453 ms
da=2362 ms
Java Sort Generating random numbers on fly (actually was slower than disk IO read). Assignment time is the time to generate random numbers and populate the array
Calling like
JavaSort(10,10000000);
Timing results:
assignment time 331 sort time 1139
assignment time 324 sort time 1037
assignment time 317 sort time 1028
assignment time 319 sort time 1026
assignment time 317 sort time 1018
assignment time 325 sort time 1025
assignment time 317 sort time 1024
assignment time 318 sort time 1054
assignment time 317 sort time 1024
assignment time 317 sort time 1017
These results were for reading a file of doubles in binary mode
assignment time 4661 sort time 1056
assignment time 4631 sort time 1024
assignment time 4733 sort time 1004
assignment time 4725 sort time 980
assignment time 4635 sort time 980
assignment time 4725 sort time 980
assignment time 4667 sort time 978
assignment time 4668 sort time 980
assignment time 4757 sort time 982
assignment time 4765 sort time 987
Doing a buffer transfer results in much faster runtimes
assignment time 77 sort time 1192
assignment time 59 sort time 1125
assignment time 55 sort time 999
assignment time 55 sort time 1000
assignment time 56 sort time 999
assignment time 54 sort time 1010
assignment time 55 sort time 999
assignment time 56 sort time 1000
assignment time 55 sort time 1002
assignment time 56 sort time 1002
C and C++ Timing results (see below for source)
Debug profile using qsort
assignment 0 seconds 110 milliseconds Time taken 2 seconds 340 milliseconds
assignment 0 seconds 90 milliseconds Time taken 2 seconds 340 milliseconds
assignment 0 seconds 100 milliseconds Time taken 2 seconds 330 milliseconds
assignment 0 seconds 100 milliseconds Time taken 2 seconds 340 milliseconds
assignment 0 seconds 100 milliseconds Time taken 2 seconds 330 milliseconds
assignment 0 seconds 100 milliseconds Time taken 2 seconds 340 milliseconds
assignment 0 seconds 90 milliseconds Time taken 2 seconds 340 milliseconds
assignment 0 seconds 100 milliseconds Time taken 2 seconds 330 milliseconds
assignment 0 seconds 100 milliseconds Time taken 2 seconds 340 milliseconds
assignment 0 seconds 100 milliseconds Time taken 2 seconds 330 milliseconds
Release profile using qsort
assignment 0 seconds 100 milliseconds Time taken 1 seconds 600 milliseconds
assignment 0 seconds 90 milliseconds Time taken 1 seconds 600 milliseconds
assignment 0 seconds 90 milliseconds Time taken 1 seconds 580 milliseconds
assignment 0 seconds 90 milliseconds Time taken 1 seconds 590 milliseconds
assignment 0 seconds 80 milliseconds Time taken 1 seconds 590 milliseconds
assignment 0 seconds 90 milliseconds Time taken 1 seconds 590 milliseconds
assignment 0 seconds 90 milliseconds Time taken 1 seconds 600 milliseconds
assignment 0 seconds 90 milliseconds Time taken 1 seconds 590 milliseconds
assignment 0 seconds 90 milliseconds Time taken 1 seconds 600 milliseconds
assignment 0 seconds 90 milliseconds Time taken 1 seconds 580 milliseconds
Release profile Using std::sort( a, a + ARRAY_SIZE );
assignment 0 seconds 100 milliseconds Time taken 0 seconds 880 milliseconds
assignment 0 seconds 90 milliseconds Time taken 0 seconds 870 milliseconds
assignment 0 seconds 90 milliseconds Time taken 0 seconds 890 milliseconds
assignment 0 seconds 120 milliseconds Time taken 0 seconds 890 milliseconds
assignment 0 seconds 90 milliseconds Time taken 0 seconds 890 milliseconds
assignment 0 seconds 90 milliseconds Time taken 0 seconds 880 milliseconds
assignment 0 seconds 90 milliseconds Time taken 0 seconds 900 milliseconds
assignment 0 seconds 90 milliseconds Time taken 0 seconds 890 milliseconds
assignment 0 seconds 100 milliseconds Time taken 0 seconds 890 milliseconds
assignment 0 seconds 150 milliseconds Time taken 0 seconds 870 milliseconds
Release profile Reading random data from file and using std::sort( a, a + ARRAY_SIZE )
assignment 0 seconds 50 milliseconds Time taken 0 seconds 880 milliseconds
assignment 0 seconds 40 milliseconds Time taken 0 seconds 880 milliseconds
assignment 0 seconds 50 milliseconds Time taken 0 seconds 880 milliseconds
assignment 0 seconds 50 milliseconds Time taken 0 seconds 880 milliseconds
assignment 0 seconds 40 milliseconds Time taken 0 seconds 880 milliseconds
Below is the source code used. Hopefully minimal bugs :)
Java source
Note that internal to JavaSort the runCode and writeFlag need to be adjusted depending on what you want to time. Also note that the memory allocation happens in the for loop (thus testing GC, but I did not see any appreciable difference moving the allocation outside the loop)
public static void JavaSort(int iterations, int numberElements) {
Random ran = new Random();
Math.random();
int runCode = 2;
boolean writeFlag = false;
for (int j = 0; j < iterations; j++) {
double[] a1 = new double[numberElements];
long timea = System.currentTimeMillis();
if (runCode == 0) {
for (int i = 0; i < numberElements; i++) {
a1[i] = ran.nextDouble();
}
}
else if (runCode == 1) {
//do disk io!!
try {
DataInputStream in = new DataInputStream(new FileInputStream("MyBinaryFile.txt"));
int i = 0;
//while (in.available() > 0) {
while (i < numberElements) { //this should be changed so that I always read in the size of array elements
a1[i++] = in.readDouble();
}
}
catch (Exception e) {
}
}
else if (runCode == 2) {
try {
FileInputStream stream = new FileInputStream("MyBinaryFile.txt");
FileChannel inChannel = stream.getChannel();
ByteBuffer buffer = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
//int[] result = new int[500000];
buffer.order(ByteOrder.BIG_ENDIAN);
DoubleBuffer doubleBuffer = buffer.asDoubleBuffer();
doubleBuffer.get(a1);
}
catch (Exception e) {
}
}
if (writeFlag) {
try {
DataOutputStream out = new DataOutputStream(new FileOutputStream("MyBinaryFile.txt"));
for (int i = 0; i < numberElements; i++) {
out.writeDouble(a1[i]);
}
} catch (Exception e) {
}
}
long timeb = System.currentTimeMillis();
Arrays.sort(a1);
long timec = System.currentTimeMillis();
System.out.println("assignment time " + (timeb - timea) + " " + " sort time " + (timec - timeb));
//delete a1;
}
}
C/C++ source
#include <iostream>
#include <vector>
#include <algorithm>
#include <fstream>
#include <cstdlib>
#include <ctime>
#include <cstdio>
#include <math.h>
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#define ARRAY_SIZE 10000000
using namespace std;
int compa(const void * elem1, const void * elem2) {
double f = *((double*) elem1);
double s = *((double*) elem2);
if (f > s) return 1;
if (f < s) return -1;
return 0;
}
int compb (const void *a, const void *b) {
if (*(double **)a < *(double **)b) return -1;
if (*(double **)a > *(double **)b) return 1;
return 0;
}
void timing_testa(int iterations) {
clock_t start = clock(), diffa, diffb;
int msec;
bool writeFlag = false;
int runCode = 1;
for (int loopCounter = 0; loopCounter < iterations; loopCounter++) {
double *a = (double *) malloc(sizeof (double)*ARRAY_SIZE);
start = clock();
size_t bytes = sizeof (double)*ARRAY_SIZE;
if (runCode == 0) {
for (int i = 0; i < ARRAY_SIZE; i++) {
a[i] = rand() / (RAND_MAX + 1.0);
}
}
else if (runCode == 1) {
ifstream inlezen;
inlezen.open("test", ios::in | ios::binary);
inlezen.read(reinterpret_cast<char*> (&a[0]), bytes);
}
if (writeFlag) {
ofstream outf;
const char* pointer = reinterpret_cast<const char*>(&a[0]);
outf.open("test", ios::out | ios::binary);
outf.write(pointer, bytes);
outf.close();
}
diffa = clock() - start;
msec = diffa * 1000 / CLOCKS_PER_SEC;
printf("assignment %d seconds %d milliseconds\t", msec / 1000, msec % 1000);
start = clock();
//qsort(a, ARRAY_SIZE, sizeof (double), compa);
std::sort( a, a + ARRAY_SIZE );
//printf("%f %f %f\n",a[0],a[1000],a[ARRAY_SIZE-1]);
diffb = clock() - start;
msec = diffb * 1000 / CLOCKS_PER_SEC;
printf("Time taken %d seconds %d milliseconds\n", msec / 1000, msec % 1000);
free(a);
}
}
/*
*
*/
int main(int argc, char** argv) {
printf("hello world\n");
double *a = (double *) malloc(sizeof (double)*ARRAY_SIZE);
//srand(1);//change seed to fix it
srand(time(NULL));
timing_testa(5);
free(a);
return 0;
}
This is not completely on point, but I posted something recently that relates to database vs. application-side sorting. The article is about a .net technique, so most of it likely won't be interesting to you, but the basic principles remain:
Deferring sorting to the client side (e.g. jQuery, Dataset/Dataview sorting) may look tempting. And it actually is a viable option for paging, sorting and filtering, if (and only if):
1. the set of data is small, and
1. there is little concern about performance and scalability
From my experience, the systems that meet this kind of criteria are few and far between. Note that it’s not possible to mix and match sorting/paging in the application/database—if you ask the database for an unsorted 100 rows of data, then sort those rows on the application side, you’re likely not going to get the set of data you were expecting. This may seem obvious, but I’ve seen the mistake made enough times that I wanted to at least mention it.
It is much more efficient to sort and filter in the database for a number of reasons. For one thing, database engines are highly optimized for doing exactly the kind of work that sorting and filtering entail; this is what their underlying code was designed to do. But even barring that—even assuming you could write code that could match the kind of sorting, filtering and paging performance of a mature database engine—it’s still preferable to do this work in the database, for the simple reason that it’s more efficient to limit the amount of data that is transferred from the database to the application server.
So for example, if you have 10,000 rows before filtering, and your query pares that number down to 75, filtering on the client results in the data from all 10,000 rows being passed over the wire (and into your app server’s memory), where filtering on the database side would result in only the filtered 75 rows being moved between database and application. his can make a huge impact on performance and scalability.
The full post is here:
http://psandler.wordpress.com/2009/11/20/dynamic-search-objects-part-5sorting/
I'm almost positive that it will be faster to allow the Database to sort it. There's engineers who spend a lot of time perfecting and optimizing their search algorithms, whereas you'll have to implement your own sorting algorithm which might add a few more computations.
I would let the database do the sort, they are generally very good at that.
Let the database sort it. Then you can have paging with JPA easily without readin in the whole resultset.
Well, there is not really a straightforward way to answer this; it must be answered in the context.
Is your application (middle tier) is running in the same node as the database?
If yes, you do not have to worry about the latency between the database and middle tier. Then the question becomes: How big is the subset/resultset of your query? Remember that to sort this is middle tier, you will take a list/set of size N, and either write a custom comparator or use the default Collection comparator. Or, whatever. So at the outset, you are setback by the size N.
But if the answer is no, then you are hit by the latency involved in transferring your resultset from DB to middle tier. And then if you are performing pagination, which is the last thing you should do, you are throwing away 90-95% of that resultset after cutting the pages.
So the wasted bandwidth cannot be justified. Imagine doing this for every request, across your tenant organizations.
However way you look at it, this is bad design.
I would do this in the database, no matter what. Just because almost all applications today demand pagination; even if they don't sending massive resultsets over the wire to your client is a total waste; drags everybody down across all your tenants.
One interesting idea that I am toying with these days is to harness the power of HTML5, 2-way data binding in browser frameworks like Angular, and push some processing back to the browser. That way, you dont end up waiting in the line for someone else before you to finish. True distributed processing. But care must be taken in deciding what can be pushed and what not.
Depends on the context.
TL;DR
If you have the full data in your application server, do it in the application server.
If you have the full dataset that you need on the application server side already then it is better to do it on the application server side because those servers can scale horizontally. The most likely scenarios for this are:
the data set you're retrieving from the database is small
you cached the data on the application server side on startup
You're doing event sourcing and you're building up the data in the application server side anyway.
Don't do it on client side unless you can guarantee it won't impact the client devices.
Databases themselves may be optimized, but if you can pull burden away from them you can reduce your costs overall because scaling the databases up is more expensive than scaling up application servers.