То, что я искал, было упомянуто в «Учебнике Java по параллелизму», мне просто пришлось заглянуть глубже.В данном случае это был класс AtomicIntegerArray.К сожалению, он недостаточно эффективен для моих нужд.Я запускаю несколько тестов, возможно, им стоит поделиться.
Я приблизительно оценил стоимость различных методов доступа к памяти, запустив их много раз и усреднив истекшее время, разбив все до одной средней операции чтения или записи.
Я использовал размер целочисленного массива 50000 и повторял каждый метод тестирования 100 раз, затем усреднял результаты.Тесты чтения выполняют 50000 случайных (ish) чтений.Результаты показывают приблизительное время одного доступа для чтения / записи.Тем не менее, это нельзя назвать точным измерением, но я считаю, что оно дает хорошее представление о затратах времени на различные методы доступа.Однако на разных процессорах или с разными числами эти результаты могут быть совершенно разными в зависимости от разных размеров кэша и тактовых частот.
Таким образом, получаются следующие результаты:
- Время заполнения с помощью набора равно: 15.922673ns
- Время заполнения с помощью lazySet: 4.5303152ns
- Время атомарного чтения: 9.146553ns
- Синхронизированное время чтения: 57.858261399999996ns
- SingleВремя заполнения с резьбой: 0,2879112 нс
- Время чтения с одной резьбой: 0,3152002 нс
- Время неизменного копирования: 0,2920892 нс
- Время неизменного чтения: 0,650578 нс
В точках 1 и 2 показан результат записи в массив AtomicIntegerArray с последовательной записью.В какой-то статье я рассказал о хорошей эффективности метода lazySet (), поэтому я хотел его протестировать.Обычно метод over () выполняется более чем в 4 раза, однако разные размеры массива показывают разные результаты.
Точки 3 и 4 показывают разницу между «атомарным» доступом и синхронизированным доступом (синхронизированный геттер)к одному элементу массива через случайное чтение (ish) четырьмя различными потоками одновременно.Это ясно указывает на преимущества «атомарного» доступа.
Поскольку первые четыре значения выглядели шокирующе высокими, я действительно хотел измерить время доступа без многопоточности, поэтому я получил результаты пунктов 5 и 6. Япытался копировать и модифицировать методы из предыдущих тестов, чтобы сделать код максимально близким.Конечно, могут быть оптимизации, на которые я не могу повлиять.
Тогда просто из любопытства я придумываю пункты 7. и 8., которые имитируют неизменный доступ.Здесь один поток создает массив (путем последовательной записи) и передает его ссылку на другой поток, который выполняет случайный (ish) доступ к нему для чтения.
Результаты сильно меняются, если параметры изменяются, например,размер массива или количество запущенных методов.
Вывод: если алгоритм чрезвычайно интенсивно использует память (большое количество операций чтения из одного и того же небольшого массива, прерываемое короткими вычислениями - как в моем случае),многопоточность может замедлить расчет, а не ускорить его.Но если он имеет много операций чтения по сравнению с размером массива, может быть полезно использовать неизменяемую копию массива и использовать несколько потоков.