hudi
hudi

Reputation: 16525

Why first enumeration of encrypting data takes so long

I want to create performance test where I am encrypting data using AES with PKCPadding5/7 depends on provider(IBM or BouncyCastle) on aix system. My question is why first enumeration is so slow and other so fast ?

When I run test with parameters:

Padding: AES/CBC/PKCS7Padding
Provider: BC version 1.47
Number of iteration: 1000

results is:

Time: 392080057 ns
Time: 174662 ns
Time: 160906 ns
Time: 169938 ns
Time: 154344 ns
Time: 155125 ns
Time: 157344 ns
Time: 157203 ns
Time: 157611 ns
Time: 158123 ns

Parameters

Padding: AES/CBC/PKCS5Padding
Provider: IBMJCE version 1.7
Number of iteration: 1000

results is:

Time: 13410344 ns
Time: 185007 ns
Time: 182562 ns
Time: 170687 ns
Time: 203156 ns
Time: 189980 ns
Time: 182608 ns
Time: 174670 ns
Time: 176842 ns
Time: 174463 ns

Code:

for (int j = 0; j < pocetIteraci; j++) {
    String text = randomString(46);

    long time = System.currentTimeMillis();
    sifruj(padding, provider, generateVectot(),text);

    time = System.currentTimeMillis() - time;
    System.out.println("Time: " + time + " ms");
}

public static byte[] sifruj(String padding, Provider provider, IvParameterSpec finalniInicializacniVektor,String text)
        throws Exception {
    return zpracuj(padding, provider, text.getBytes("UTF-8"), finalniInicializacniVektor, Cipher.ENCRYPT_MODE);
}

public static byte[] zpracuj(String padding, Provider provider, byte[] data,
        IvParameterSpec finalniInicializacniVektor, int mode) throws Exception {
    final SecretKeySpec klicSpec = new SecretKeySpec(klic, ALGORITHM_AES);
    final Cipher sifra = Cipher.getInstance(padding, provider);
    sifra.init(mode, klicSpec, finalniInicializacniVektor);

    return sifra.doFinal(data);

}

UPDATED:

The most different (As I expected) is when creating first instance of cipher:

long timeTemp = System.nanoTime();
final Cipher sifra = Cipher.getInstance(padding, provider);
timeTemp = System.nanoTime() - timeTemp;
System.out.println("XXXXXXXXXXXXXXXXX " + timeTemp + " ns");

Why?

Upvotes: 1

Views: 784

Answers (2)

Ebbe M. Pedersen
Ebbe M. Pedersen

Reputation: 7488

Crypto providers usually takes a bit to load. This can be seen by forcing it to load. Here i try with the IBM JCE provider):

public class Test {
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        Security.addProvider(new com.ibm.crypto.provider.IBMJCE());
        long endTime = System.currentTimeMillis();

        System.out.println("Time to load: " + (endTime - startTime));
    }
}

Gives:

Time to load: 72

My guess is that you hit this load time the first time you use anything from the provider.

Upvotes: 0

Jon Skeet
Jon Skeet

Reputation: 1500893

Revised answer

There's time taken to load and JIT-compile the code on the first call - that's why typically benchmarks don't include the first run. (In fact, many JVMs re-JIT more and more aggressively, so the performance improves over time - so good benchmarks run code lots of times before timing it.)

Additionally, it's not unreasonable to think that a crypto provider may well do some one-time setup, especially if it's trying its best to get some decent entropy. (It could be using SecureRandom internally, for example.) You might want to time the Cipher.getInstance, Cipher.init and Cypher.doFinal method calls separately, to determine which is the slow part.

Finally, you haven't shown us all the code (generateVectot?) so there could be things lurking there.

Initial answer

Apparently randomString is:

simple function which generate random string with certain length and yes is use SecureRandom which instance is crateted just once

Given that you're looking for something which is expensive (as creating and using a SecureRandom instance is) and only happens once, this sounds like a smoking gun to me.

I suggest you add timing (using System.nanoTime, not System.currentTimeMillis) within randomString to test that hypothesis.


I'd also strongly suggest that you don't use the overload of String.getBytes which uses the platform default encoding - instead, always specify the encoding explicitly.

Upvotes: 2

Related Questions