Статьи

Счетчик параллельных операций Off Heap

Счетчик параллельных вычислений является частью почти каждой системы, он используется для сбора данных, синхронизации потоков и т. Д. Java имеет хорошую поддержку счетчика на основе кучи.

Там может быть случай использования, когда вам нужен счетчик, который может быть разделен между процессором.

Как построить межпроцессные счетчики

База данных

Это первый вариант, который приходит на ум, последовательность базы данных является счетчиком, который может использоваться несколькими процессами. Весь параллелизм обрабатывается базой данных. Это хороший вариант для начинающих, но мы знаем типы накладных расходов (сеть, блокировки и т. Д.), Которые вы получаете с базой данных. Только Ларри Элисион будет рад этому, а не вы!

Какой-то сервер

Вы можете разработать какое-нибудь серверное / промежуточное программное обеспечение, которое предоставляет такой тип сервиса. Эта опция все равно будет иметь задержку в сети, маршал / демаршал накладные расходы.

Файл с отображенной памятью

Вы можете использовать файл с отображенной памятью, чтобы сделать это. Мне пришла в голову мысль посмотреть презентацию PeterLawrey «Потокобезопасный межпроцессный доступ к общей памяти в Java» .

Проблемы, связанные с многопроцессорным счетчиком.

Видимость данных

Изменения, сделанные одним процессом, должны быть видны всему процессу. Эта проблема может быть решена с помощью отображенного файла памяти. Операционная система дает гарантию на это, и модель памяти Java поддерживает это, чтобы сделать возможным.

Поток безопасности

В счетчиках много писателей, поэтому безопасность потоков становится большой проблемой. Сравнение и замена — одна из опций для работы с несколькими авторами. Можно ли использовать CAS для работы вне кучи? да, это возможно, добро пожаловать в Unsafe. Используя Memorymapped & Unsafe, можно использовать CAS для работы без кучи.

В этом блоге я поделюсь своим экспериментом с отображением памяти с использованием CAS.

Как?

Как получить адрес памяти

MappedByteBuffer использует DirectByteBuffer, который находится вне памяти кучи. Таким образом, можно получить виртуальный адрес памяти и использовать небезопасные для выполнения операции CAS. Давайте посмотрим на код.

1
2
3
4
FileChannel fc = new RandomAccessFile(fileName, "rw").getChannel();
// Map 8 bytes for long value.
mem = fc.map(FileChannel.MapMode.READ_WRITE, 0, 8);
startAddress = ((DirectBuffer) mem).address();

Выше кода создайте файл отображения памяти размером 8 байтов и получите виртуальный адрес. Этот адрес может быть использован для чтения / записи содержимого файла отображения памяти.

Как писать / читать в потоке безопасным образом

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public boolean increment() {
        long orignalValue = readVolatile(startAddress);
        long value = convert(orignalValue);
        return UnsafeUtils.unsafe.compareAndSwapLong(null,
                startAddress,orignalValue, convert(value + 1));
    }
 
    public long get() {
        long orignalValue = readVolatile(startAddress);
        return convert(orignalValue);
    }
 
    // Only unaligned is implemented
    private static long readVolatile(long position) {
        if (UnsafeUtils.unaligned()) {
            return UnsafeUtils.unsafe.getLongVolatile(null, position);
        }
        throw new UnsupportedOperationException();
    }
 
    private static long convert(long a) {
        if (UnsafeUtils.unaligned()) {
            return (UnsafeUtils.nativeByteOrder ? a : Long.reverseBytes(a));
        }
        throw new UnsupportedOperationException();
    }

Важной функцией для просмотра являются readVolatile и увеличение readVolatile для чтения непосредственно из памяти, а для увеличения используется небезопасный режим для выполнения CAS по адресу, полученному из MemoryByteBuffer.

Производительность

Некоторые показатели производительности из моей системы. Каждый поток увеличивает счетчик на 1 миллион раз.

Счетчик + Статистика

Производительность счетчика приличная, так как количество потоков увеличивается, начинают происходить сбои CAS, а производительность начинает снижаться. Производительность этих счетчиков может быть улучшена за счет наличия нескольких сегментов для уменьшения конкуренции за запись. Я напишу об этом в следующем блоге.

Вывод

  • Файл, отображаемый в память, очень мощный, его можно использовать для разработки множества вещей, таких как коллекции вне кучи, IPC, координация потоков вне кучи и т. Д.
  • Файл с отображенной памятью открывает ворота для программирования без ГХ.

Весь код, используемый в этом блоге, доступен на github .

Ссылка: Счетчик параллельных операций Off Heap от нашего партнера JCG Ашкрита Шарма в блоге Are you ready .