0

I'm supposed to measure the running time of my code below when testing various file sizes of encryption. However I think the method I am using is merely outputting the current timer of the CPU and not the running time of the program. This was the code to implement but I don't think I did it correctly

long startTime = System.nanoTime();

// call function
obj.callFunction();

long endTime = System.nanoTime();
long timeDifference = endTime - startTime;

Here is my actual program. How should I modify the timer code so it reflects the programs actual running time and not the system clock? Thank you!

import javax.crypto.Cipher;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.SecretKeySpec;


public class AESJava {

    public static void main(String[] args) {


        // returns the current value of the system timer, in nanoseconds
          System.out.print("time in nanoseconds = ");
          System.out.println(System.nanoTime());


        try {



            BufferedReader br = new BufferedReader(new FileReader("key.txt"));
            String key = br.readLine();
            br.close();
            FileInputStream fis = new FileInputStream("original.txt");
            FileOutputStream fos = new FileOutputStream("encrypted.txt");
            encrypt(key, fis, fos);

            FileInputStream fis2 = new FileInputStream("encrypted.txt");
            FileOutputStream fos2 = new FileOutputStream("decrypted.txt");
            decrypt(key, fis2, fos2);

        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public static void encrypt(String key, InputStream is, OutputStream os) throws Throwable {
        encryptOrDecrypt(key, Cipher.ENCRYPT_MODE, is, os);
    }

    public static void decrypt(String key, InputStream is, OutputStream os) throws Throwable {
        encryptOrDecrypt(key, Cipher.DECRYPT_MODE, is, os);
    }

    public static void encryptOrDecrypt(String key, int mode, InputStream is, OutputStream os) throws Throwable {


      SecretKeySpec dks = new SecretKeySpec(key.getBytes(),"AES");
        Cipher cipher = Cipher.getInstance("AES"); 

        if (mode == Cipher.ENCRYPT_MODE) {
            cipher.init(Cipher.ENCRYPT_MODE, dks);
            CipherInputStream cis = new CipherInputStream(is, cipher);
            doCopy(cis, os);        
        } else if (mode == Cipher.DECRYPT_MODE) {
            cipher.init(Cipher.DECRYPT_MODE, dks);
            CipherOutputStream cos = new CipherOutputStream(os, cipher);
            doCopy(is, cos);
        }


    }

    public static void doCopy(InputStream is, OutputStream os) throws IOException {
        byte[] bytes = new byte[128];
        int numBytes;
        while ((numBytes = is.read(bytes)) != -1) {
            os.write(bytes, 0, numBytes);
        }
        os.flush();
        os.close();
        is.close();
    }

}
user3294617
  • 37
  • 1
  • 7
  • 1
    Without using kernel statistics of the underlying OS, your application cannot tell the difference. If you run your code several times, you should get reasonable values, though. – PMF Mar 26 '14 at 05:48
  • To add to PMF's suggestion, as long as the code/data is the same each time, run it multiple times and take the smallest measurement to minimize times for context switches and other OS functions. – Kenny Anderson Mar 26 '14 at 05:52
  • possible duplicate of: http://stackoverflow.com/questions/180158/how-do-i-time-a-methods-execution-in-java – Mohsen Kamrani Mar 26 '14 at 07:01

1 Answers1

1

I'm confused because your first thing is absolutely correct, but then your main method doesn't do what you wrote. It just prints the CPU time (since the epoch) and does not compute the actual wall-time. The easiest way to change your program is to create a method (function) which does what your main method does, then simply insert the initial part. Here is an excerpt:

private static void doMain(){
    try {
        BufferedReader br = new BufferedReader(new FileReader("key.txt"));
        String key = br.readLine();
        br.close();
        FileInputStream fis = new FileInputStream("original.txt");
        FileOutputStream fos = new FileOutputStream("encrypted.txt");
        encrypt(key, fis, fos);

        FileInputStream fis2 = new FileInputStream("encrypted.txt");
        FileOutputStream fos2 = new FileOutputStream("decrypted.txt");
        decrypt(key, fis2, fos2);

    } catch (Throwable e) {
        e.printStackTrace();
    }
}

Then your main method would simply be:

public static void main(String ...args){
     long startTime = System.nanoTime();

     // call function
     doMain();

     long endTime = System.nanoTime();
     long timeDifference = endTime - startTime;
}

A better way to calculate the average time is to to do this several times (a single run which needs nanosecond precision isn't going to be very accurate). So you could do something like:

public static void main(String ...args){
     long startTime = System.nanoTime();

     // call function
     for(int i = 0; i < NUM; ++i)
         doMain();

     long endTime = System.nanoTime();
     long timeDifference = endTime - startTime;
     double avgTime = (double)timeDifference / (double)NUM;
}

I will strongly caution you though that this may not be accurate. There are two things you need to be wary of when trying to time things: 1) will the OS assume the data is already in RAM and 2) will the architecture assume the data is already in the cache. If you run this many times, you will likely be running at the speed of RAM and not at the speed of reading/writing the disk. To truly get an average speed, you need to ensure that the OS has to read/write to disk (which may require many intermediate "dummy" reading/writing).

Jared
  • 940
  • 5
  • 9