【发布时间】:2018-07-31 23:56:32
【问题描述】:
我运行了一个简单的测试,通过在循环中加密字节缓冲区来测量 Java 9 中的AES-GCM 性能。结果有些混乱。本机(硬件)加速似乎有效 - 但并非总是如此。更具体地说,
- 在循环中加密 1MB 缓冲区时,前约 50 秒的速度为约 60 MB/秒。然后它跳到 1100 MB/秒,并保持在那里。 JVM 是否决定在 50 秒(或 3GB 数据)后激活硬件加速?可以配置吗? 我在哪里可以了解新的 AES-GCM 实施 (besides here)。
- 加密 100MB 缓冲区时,硬件加速根本不会启动。速度为 60 MB/秒。
我的测试代码如下所示:
int plen = 1024*1024;
byte[] input = new byte[plen];
for (int i=0; i < input.length; i++) { input[i] = (byte)i;}
byte[] nonce = new byte[12];
...
// Uses SunJCE provider
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] key_code = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
SecretKey key = new SecretKeySpec(key_code, "AES");
SecureRandom random = new SecureRandom();
long total = 0;
while (true) {
random.nextBytes(nonce);
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] cipherText = cipher.doFinal(input);
total += plen;
// print delta_total/delta_time, once in a while
}
2019 年 2 月更新:已修改 HotSpot 以解决此问题。该修复适用于 Java 13,并且还向后移植到 Java 11 和 12。
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8201633, https://hg.openjdk.java.net/jdk/jdk/rev/f35a8aaabcb9
2019 年 7 月 16 日更新:新发布的 Java 版本(Java 11.0.4)修复了这个问题。
【问题讨论】:
-
我认为你应该阅读How to write a correct micro-benchmark in Java 并应用那里描述的技术。他们可能会对您正在测量的内容有更好的了解。例如,您应该在基准测试中包含一个热身阶段。您看到的情况可能是因为 JIT 编译器会在 50 秒后启动并优化您的代码。
-
似乎调用次数对优化触发器很重要,当然,考虑到相同的时间,处理大量小缓冲区意味着比处理几个大缓冲区更多的调用。注意通过重复调用
update处理大缓冲区的可能性,最后调用doFinal处理最后一个块...... -
@Lii 你说得对,预热很可能会有所帮助,但这不是微基准,它需要相当长的时间并且应该会更好。
-
@Eugene 这不是关于采用不同的分支。我尝试了不同的缓冲区大小和不同的缓冲区大小。您可以在一秒钟内预热代码,方法是用一个很小的缓冲区经常执行它以获得优化,然后用一个巨大的缓冲区调用相同的代码,仍然可以从已经应用的优化中受益。这表明重要的仅仅是调用的数量。当您重构代码以始终通过重复的
update操作和doFinal处理缓冲区的一小部分时,总缓冲区大小变得无关紧要…… -
@gg123 恕我直言,这应该是一个错误报告。大块的加密应该自动拆分,解密应该找到一些解决方案。
标签: java performance encryption java-11 aes-gcm