Мое объяснение может не решить вашу проблему, так как оно во многом зависит от вашей реальной среды выполнения, но когда я запускаю ваш код в моей системе, пропускная способность ограничивается дисковым вводом / выводом, а не вычислением хеша. Проблема не решается переключением на NIO, а просто вызвана тем, что вы читаете файл очень маленькими кусочками (16 КБ). Увеличение размера буфера (buff) в моей системе до 1 МБ вместо 16 КБ более чем вдвое увеличивает пропускную способность, но при> 50 МБ / с я все еще ограничен скоростью диска и не могу полностью загрузить одно ядро ЦП.
Кстати: вы можете значительно упростить вашу реализацию, обернув DigestInputStream вокруг FileInputStream, прочитав файл и получив вычисленный хеш из DigestInputStream вместо того, чтобы вручную перетасовывать данные из RandomAccessFile в MessageDigest, как в вашем коде.
Я провел несколько тестов производительности с более старыми версиями Java, и, похоже, здесь есть существенное различие между Java 5 и Java 6. Однако я не уверен, что реализация SHA оптимизирована или виртуальная машина выполняет код намного быстрее. Производительность, которую я получаю с разными версиями Java (буфер 1 МБ):
- Sun JDK 1.5.0_15 (клиент): 28 МБ / с, ограничено ЦП
- Sun JDK 1.5.0_15 (сервер): 45 МБ / с, ограничено ЦП
- Sun JDK 1.6.0_16 (клиент): 42 МБ / с, ограничено ЦП
- Sun JDK 1.6.0_16 (сервер): 52 МБ / с, ограничено дисковым вводом-выводом (загрузка ЦП 85-90%)
Мне было немного любопытно, как ассемблерная часть реализуется в реализации SHA CryptoPP, так как результаты теста производительности указывают, что алгоритму SHA-256 требуется только 15,8 циклов ЦП / байт на Opteron. Я, к сожалению, не смог собрать CryptoPP с gcc на cygwin (сборка завершилась успешно, но сгенерированный exe сразу же не удалось), но я построил тест производительности с VS2005 (конфигурация выпуска по умолчанию) с поддержкой ассемблера в CryptoPP и без нее и сравнивал с Java SHA Реализация на буфере в памяти, исключая любые дисковые операции ввода-вывода, я получаю следующие результаты на Phenom 2.5 ГГц:
- Sun JDK1.6.0_13 (сервер): 26,2 такта / байт
- CryptoPP (только C ++): 21,8 циклов / байт
- CryptoPP (ассемблер): 13,3 циклов / байт
Оба теста вычисляют хэш SHA пустого байтового массива 4 ГБ, перебирая его по кусочкам по 1 МБ, которые передаются в MessageDigest # update (Java) или в функцию CryptoPP SHA256.Update (C ++).
Я смог собрать и протестировать CryptoPP с gcc 4.4.1 (-O3) на виртуальной машине под управлением Linux и получил только ок. половина пропускной способности по сравнению с результатами VS exe. Я не уверен, сколько различий вносит вклад в виртуальную машину и сколько вызвано тем, что VS обычно производит лучший код, чем gcc, но у меня нет способа получить более точные результаты от gcc прямо сейчас.