Загрузка CPU составляет 100% во время Thread.onSpinWait () - PullRequest
0 голосов
/ 19 апреля 2020

Я пишу тестировщик необработанных данных для моего бота по крипто-торговле, и у меня возникла странная проблема оптимизации.

У меня постоянно есть 30 исполняемых модулей в Executors.newCachedThreadPool (), выполняющих запросы get от API. Так как у API есть предел запроса 1200 в минуту, у меня есть этот бит кода в моем runnable:

    while (minuteRequests.get() >= 1170) {
        Thread.onSpinWait();
    }

Да, minuteRequests является AtomicInteger, поэтому я не сталкиваюсь с какими-либо проблемами там.

Все работает, проблема в том, что, хотя я использую рекомендованный метод onSpinWait с ожиданием занятости, я снимаю с 24% загрузки процессора или около того до 100%, когда инициируется ожидание. Для справки, я запускаю это на 3900X (24 потока).

Любые рекомендации о том, как лучше справиться с этой ситуацией?

1 Ответ

6 голосов
/ 19 апреля 2020

Я бы порекомендовал вообще не ждать .


javadocs для Thread.onSpinWait сказать следующее:

Указывает, что вызывающий в данный момент не может прогрессировать, пока не произойдет одно или несколько действий со стороны других действий. Вызывая этот метод в каждой итерации конструкции spin-wait l oop, вызывающий поток указывает среде выполнения, что он ожидает ожидания. Среда выполнения может принять меры для повышения производительности вызова конструкций с ожиданием вращения l oop .

Обратите внимание, что выделенный раздел использует слово , может чем будет . Это означает, что он также не может ничего не делать. «Повышение производительности» также не означает, что ваш код будет объективно эффективным.

Javado c также подразумевает, что улучшения могут зависеть от аппаратного обеспечения.

Короче говоря, это правильный способ использования onSpinwait ... но вы ожидаете слишком многого от него. Это не сделает ваш код ожидания занятости эффективным.


Так что я бы порекомендовал вам сделать на самом деле?

Я бы порекомендовал заменить AtomicInteger на Semaphore ( javado c). Этот конкретный l oop будет заменен следующим:

semaphore.acquire();

Этот блок 1 , пока 1 "разрешение" не будет доступно и не получит его. Обратитесь к классу javadocs для объяснения того, как работают семафоры.

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


1 - заблокированный поток «припаркован» до тех пор, пока какой-либо другой поток не освободит разрешение. Пока он припаркован, поток не запускается и не связан с ядром ЦП. Ядро либо не используется (обычно в состоянии низкого энергопотребления), либо оно назначено другому потоку. Обычно это обрабатывается планировщиком потоков операционной системы. Когда другой поток освобождает разрешение, метод Semaphore.release скажет ОС разархивировать один из потоков, заблокированных в acquire.

...