Я хотел бы реализовать простой ограничитель скорости в java, чтобы научиться использовать jmh. Простой проект github создается по адресу https://github.com/William1104/rate-limiter.
Интересно, что на пропускную способность некоторых реализаций (со штампом) влияет включенная опция «-XX:+UseLWPSynchronization». Тест производительности выполнялся на компьютере с Windows, и я полагаю, что он не повлияет на систему, отличную от Solaris. Однако результат тестирования показывает другое. Могу ли я узнать, кто-нибудь может помочь мне понять, что именно происходит?
Вот результат тестирования на моей машине в качестве справки:
С опцией: -server, -XX:+UnlockDiagnosticVMOptions, -XX:+UseNUMA
С опцией: -server, -XX:+UnlockDiagnosticVMOptions, -XX:+UseNUMA, -XX:+UseLWPSynchronization
Есть реализация StampLockLongArrayRateLimiter и SynchronizedLongArrayRateLImiter:
package one.williamwong.ratelimiter;
import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.locks.StampedLock;
public class StampLockLongArrayRateLimiter implements IRateLimiter {
private final long duration;
private final long[] records;
private final StampedLock lock;
private int pointer;
public StampLockLongArrayRateLimiter(int maxInvokes, Duration duration) {
this.duration = duration.toNanos();
this.records = new long[maxInvokes];
this.lock = new StampedLock();
this.pointer = 0;
}
@Override public void acquire() {
final long stamp = lock.writeLock();
try {
final long now = System.nanoTime();
if (records[pointer] != 0) {
final long awayFromHead = now - records[pointer];
if (awayFromHead < duration) {
handleExcessLimit(records.length, Duration.ofNanos(awayFromHead));
}
}
records[pointer] = now;
pointer = (pointer + 1) % records.length;
} finally {
lock.unlockWrite(stamp);
}
}
@Override public void reset() {
final long stamp = lock.writeLock();
try {
Arrays.fill(records, 0);
this.pointer = 0;
} finally {
lock.unlockWrite(stamp);
}
}
}
package one.williamwong.ratelimiter;
import java.time.Duration;
import java.util.Arrays;
public class SynchronizedLongArrayRateLimiter implements IRateLimiter {
private final long duration;
private final long[] records;
private final Object lock;
private int pointer;
public SynchronizedLongArrayRateLimiter(int maxInvokes, Duration duration) {
this.duration = duration.toNanos();
this.records = new long[maxInvokes];
this.lock = new Object();
this.pointer = 0;
}
@Override
public void acquire() {
synchronized (lock) {
final long now = System.nanoTime();
if (records[pointer] != 0) {
final long awayFromHead = now - records[pointer];
if (awayFromHead < duration) {
handleExcessLimit(records.length, Duration.ofNanos(awayFromHead));
}
}
records[pointer] = now;
pointer = (pointer + 1) % records.length;
}
}
@Override public void reset() {
synchronized (lock) {
Arrays.fill(records, 0);
this.pointer = 0;
}
}
}
-XX:+UseLWPSynchronization
действительно не влияет на Windows. И потому, что он даже не читается, и потому, что «истина» является значением флага по умолчанию.
Привет @apangin, спасибо за ваш комментарий. Я перезапущу тест, чтобы убедиться, что то, что я наблюдал, стабильно. Могу ли я узнать, как долго должно быть измерение? Любая рекомендация или ссылка? Большое спасибо.
Спасибо за ваш комментарий. Я перезапускаю тест с другими настройками. Если мы снова выполним JMH с 1 секундой для каждой итерации, я получу следующий результат:
С опцией: -server, -XX:+UnlockDiagnosticVMOptions, -XX:+UseNUMA, -XX:-UseLWPSynchronization
С опцией: -server, -XX:+UnlockDiagnosticVMOptions, -XX:+UseNUMA, -XX:+UseLWPSynchronization
Я не наблюдаю большой разницы в производительности, когда «UseLWPSynchronization» включена или нет. Проблема, которую я получил, связана с нестабильной настройкой JMH.