Эффективен ли метод System.arraycopy() в Java для малых массивов?
Эффективен ли метод System.arraycopy()
в Java для малых массивов, или из-за того, что это нативный метод, его производительность может быть значительно ниже по сравнению с простым циклом и вызовом функции?
Влияют ли нативные методы на производительность, создавая дополнительную нагрузку при переходе через мост между Java и системными вызовами?
5 ответ(ов)
Расширяя то, что написал Сид, очень вероятно, что System.arraycopy
является встроенной функцией JIT, что означает, что когда код вызывает System.arraycopy
, скорее всего, он обращается к конкретной реализации JIT (как только JIT помечает System.arraycopy
как «горячий»), которая не выполняется через интерфейс JNI, поэтому не имеет обычных накладных расходов, связанных с нативными методами.
В общем, выполнение нативных методов действительно связано с некоторыми накладными расходами (вызов через интерфейс JNI, а также некоторые внутренние операции JVM не могут выполняться при выполнении нативных методов). Но это не значит, что если метод помечен как "native", вы именно так его и выполняете через JNI. JIT может делать весьма неожиданные вещи.
Самый простой способ проверить это, как уже предложено, — написать небольшой бенчмарк, внимательно следя за обычными предостережениями, касающимися микробенчмарков на Java (сначала прогреть код, избегать кода без побочных эффектов, поскольку JIT просто оптимизирует его как no-op и т.д.).
Ваш код бенчмарка выглядит довольно хорошо. Вижу, вы проводите тесты для сравнения производительности метода System.arraycopy()
и ручного копирования массивов. Ваша структура теста также выглядит логично.
Обратите внимание на некоторые замечания:
Выбор
copyCount
иtestRep
: Вы правильно отметили, что количество копий (copyCount
) влияет на необходимость "прогрева" JVM. Для больших значенийcopyCount
(например, 10 миллионов) прогрев достигается быстрее. Если вы хотите получить надежные результаты для меньших значений (copyCount
= 1 миллион), разумно увеличитьtestRep
, чтобы результаты были более стабильными.Результаты: Ваши наблюдения о производительности между
System.arraycopy()
и ручным циклом соответствуют тому, что многие разработчики замечают в своих тестах:- При
copySize
= 24 обе реализации имеют схожее время выполнения, что может быть связано с оптимизациями JIT-компилятора. - Для меньших
copySize
ручный цикл показывает большую скорость, что может объясняться меньшим накладным временем вызова метода, поскольку для небольших массивов ручная реализация может быть оптимизирована лучше. - С увеличением
copySize
System.arraycopy()
начинает превосходить ручной цикл, что делает его более эффективным для работы с большими массивами.
- При
Настройки окружения: Вы упомянули свою конфигурацию (CPU Intel Core 2 Duo E8500, Java SE 1.6.0_35-b10, Eclipse 3.7.2). Важно помнить, что результаты тестов могут варьироваться в зависимости от архитектуры вашей машины и версии Java. Рассмотрите возможность тестирования на последней версии JDK и, возможно, на более современной аппаратуре для более актуальных результатов.
Рекомендации по улучшению: Если вы хотите получить более точные результаты, подумайте о том, чтобы изолировать ваши бенчмарки от других нагрузок, которые могут выполняться на системе, и использовать более продвинутые инструменты для бенчмаркинга, такие как JMH (Java Microbenchmark Harness).
Ваши результаты и выводы очень полезны для сообщества, спасибо за их публикацию! Не стесняйтесь задавать дополнительные вопросы или делиться новыми данными.
Это действительно важный вопрос. Например, в методе java.nio.DirectByteBuffer.put(byte[])
автор пытается избежать копирования через JNI для небольшого количества элементов.
// Эти числа представляют собой границы, при которых мы эмпирически
// определили, что средняя стоимость вызова JNI превышает
// затраты на поэлементное копирование. Эти числа могут изменяться со временем.
static final int JNI_COPY_TO_ARRAY_THRESHOLD = 6;
static final int JNI_COPY_FROM_ARRAY_THRESHOLD = 6;
Что касается System.arraycopy()
, мы можем проанализировать, как его использует JDK. Например, в ArrayList
метод System.arraycopy()
всегда применяется, никогда не используется "поэлементное копирование", независимо от длины (даже если это 0). Поскольку ArrayList
очень ориентирован на производительность, мы можем сделать вывод, что System.arraycopy()
является самым эффективным способом копирования массивов независимо от длины.
System.arraycopy
использует операцию memmove
для перемещения слов, а для перемещения других примитивных типов в C за кулисами применяется ассемблер. Таким образом, метод стремится выполнить перемещение максимально эффективно в зависимости от ситуации.
В байт-кодах происходит нативное выполнение, поэтому, вероятно, производительность будет лучше, чем в случае цикла.
При использовании цикла байт-коды должны исполняться, что приведет к накладным расходам. В то время как копирование массива должно выполняться как обычная операция memcopy, что может быть более эффективным.
StringBuilder против конкатенации строк в toString() в Java
Как прочитать большой текстовый файл построчно с помощью Java?
Почему 2 * (i * i) быстрее, чем 2 * i * i в Java?
Преобразование Set в List без создания нового списка
Знаете ли вы о каких-либо инструментах для анализа логов сборки мусора в Java? [закрыто]