Проблема производительности Java с LinkedBlockingQueue - PullRequest
15 голосов
/ 15 апреля 2010

это мой первый пост на stackoverflow ... Я надеюсь, что кто-то может мне помочь

У меня большой спад производительности с Java 6 LinkedBlockingQueue. В первом потоке я генерирую несколько объектов, которые помещаю в очередь Во второй теме я вытаскиваю эти объекты. Падение производительности происходит при частом вызове метода take() из LinkedBlockingQueue. Я следил за всей программой, и метод take() потребовал больше всего времени. И пропускная способность идет от ~ 58 Мбит / с до 0,9 Мбит / с ...

методы pop и take очереди, вызываемые статическим методом из этого класса

public class C_myMessageQueue {

    private static final LinkedBlockingQueue<C_myMessageObject> x_queue = new LinkedBlockingQueue<C_myMessageObject>( 50000 );

    /**
     * @param message
     * @throws InterruptedException
     * @throws NullPointerException
     */
    public static void addMyMessage( C_myMessageObject message )
            throws InterruptedException, NullPointerException {
        x_queue.put( message );
    }

    /**
     * @return Die erste message der MesseageQueue
     * @throws InterruptedException
     */
    public static C_myMessageObject getMyMessage() throws InterruptedException {
        return x_queue.take();
    }
}

как я могу настроить метод take() для достижения как минимум 25 Мбит / с, или есть другой класс, который я могу использовать, который будет блокировать, когда "очередь" заполнена или пуста.

С уважением

Bart

П.С .: простите за мой плохой английский, я из Германии;)

Ответы [ 8 ]

16 голосов
/ 15 апреля 2010

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

Обобщая мой первоначальный ответ, теперь мы в основном имеем полную картину:

  • Вы достигнете предельного значения пропускной способности LinkedBlockingQueue (у каждой очереди есть), выполнив чрезвычайно быстрые put() с, где даже непрерывный take()s с нулевой дальнейшей обработкой не может выдержать. (Между прочим, это показывает, что в этой структуре, на вашей JVM и машине, так или иначе, put () как минимум немного дороже, чем чтение).
  • Поскольку существует особая блокировка, которую блокируют потребители, размещение большего количества потоков потребителей не могло бы помочь (если ваш потребитель фактически выполнял некоторую обработку и ограничивал пропускную способность, то добавление большего числа потребителей помогло бы. Существуют лучшие реализации очереди для Сценарий с более чем одним потребителем (или производителем), вы можете попробовать SynchronousQueue, ConcurrentLinkedQueue и предстоящий TransferQueue из jsr166y).

Некоторые предложения:

  • Старайтесь создавать более крупнозернистые объекты, чтобы издержки каждого из них были сбалансированы с фактической работой, которая выгружается из производящего потока (в вашем случае кажется, что вы создаете много накладных расходов на связь для объектов, которые представляют незначительное количество работы)
  • Вы также можете попросить производителя помочь потребителю, разгрузив некоторую трудоемкую работу (не так много смысла в ожидании ожидания, когда есть работа, которую нужно сделать).

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

3 голосов
/ 15 апреля 2010

Вот пара вещей, которые нужно попробовать:

Заменить LinkedBlockingQueue на ArrayBlockingQueue. Он не имеет висячих ссылок и поэтому лучше ведет себя, когда очередь заполняется. В частности, учитывая реализацию LinkedBlockingQueue 1.6, полный GC элементов не произойдет, пока очередь не станет фактически пустой.

Если сторона производителя последовательно не справляется с работой на стороне потребителя, рассмотрите возможность использования drain или drainTo для выполнения операции массового взятия.

В качестве альтернативы, пусть очередь принимает массивы или списки объектов сообщений. Производитель заполняет список или массив объектами сообщений, и каждый ввод или вывод перемещает несколько сообщений с одинаковыми издержками блокировки. Думайте об этом как о секретаре, который вручает вам стопку сообщений «Пока вас нет», а не вручает их по одному.

3 голосов
/ 15 апреля 2010

Я бы вообще рекомендовал не использовать LinkedBlockingQueue в чувствительной к производительности области кода, используйте ArrayBlockingQueue. Это даст гораздо более приятный профиль сборки мусора и более дружественен к кешу, чем LinkedBlockingQueue.

Попробуйте ArrayBlockingQueue и измерьте производительность.

Единственное преимущество LinkedBlockingQueue заключается в том, что он может быть неограниченным, однако это редко то, что вы на самом деле хотите. Если у вас есть случай, когда потребитель отказывает, и очереди начинают выполнять резервное копирование, наличие ограниченных очередей позволяет системе изящно ухудшаться, что может привести к возникновению OutOfMemoryErrors, которые могут возникнуть, если очереди не ограничены.

1 голос
/ 15 апреля 2010

Найдено этот интересный пост о проблемах производительности из-за размера очереди и сборки мусора.

1 голос
/ 15 апреля 2010

Трудно сказать, что происходит, ничего не зная о процессе заполнения.

Если addMyMessage вызывается реже - возможно, из-за проблем с производительностью в совершенно другой части вашего приложения - метод take должен ждать.

Таким образом, похоже, take является виновником, но на самом деле это заполняющая часть вашего приложения.

0 голосов
/ 02 июля 2017

Если ваши узкие места связаны с необработанными накладными расходами на производительность при помещении и извлечении объектов из блоков (а не с проблемой медленного производителя / потребителя ), вы можете добиться значительных улучшений производительности с помощью пакетирования объектов: например вместо того, чтобы класть или брать мелкозернистые объекты, вы кладете или берете грубые списки объектов. Вот фрагмент кода:

ArrayBlockingQueue<List<Object>> Q = new ArrayBlockingQueue<List<Object>>();

// producer side
List<Object> l = new ArrayList<Object>();
for (int i=0; i<100; i++) {
    l.add(i); // your initialization here
}
Q.put(l);

// consumer side
List<Object> l2 = Q.take();
// do something 

Пакетирование может повысить вашу производительность на порядок.

0 голосов
/ 15 апреля 2010

Ничего не могу сказать наверняка. Но вы можете попытаться изменить реализацию BlockingQueue (просто в качестве эксперимента).

Вы устанавливаете начальную емкость 50 КБ и используете LinkedBlockingQueue. Попробуйте ArrayBlockingQueue с той же емкостью, вы также можете играть с параметром fair.

0 голосов
/ 15 апреля 2010

Вполне возможно, что на ваше приложение влияют изменения, связанные с блокировкой в ​​Java 6, особенно функция "смещенной блокировки".

Попробуйте отключить его с помощью переключателя -XX:-UseBiasedLocking и посмотрите, имеет ли это значение.

См. Это для получения дополнительной информации: http://java.sun.com/performance/reference/whitepapers/6_performance.html

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...