private final T[] items; /** items index for next take, poll or remove */ private int takeIndex = 0; /** items index for next put, offer, or add. */ private int putIndex = 0; /** Number of items in the queue */ private int count = 0; private final ReentrantLock lock = new ReentrantLock(); @SuppressWarnings( "unchecked" ) public ABQConditionFreeQueue( final int size ) { this.items = ( T[] ) new Object[size]; } private int inc( int i ) { return ( ++i == items.length ) ? 0 : i; } @Override public void enqueue( final T item ) throws InterruptedException { for ( int i = 0; ; i++ ) { if ( count < items.length && tryAcquireLock() ) { try { if ( count < items.length ) { items[putIndex] = item; putIndex = inc( putIndex ); ++count; return; } } finally { releaseLock(); } } backoff(); } } @Override public T dequeue() throws InterruptedException { for ( int i = 0; ; i++ ) { if ( count > 0 && tryAcquireLock() ) { try { if ( count > 0 ) { final T item = items[takeIndex]; items[takeIndex] = null; takeIndex = inc( takeIndex ); --count; return item; } } finally { releaseLock(); } } backoff(); } } private void backoff() throws InterruptedException { Thread.yield(); } private void releaseLock() { lock.unlock(); } private boolean tryAcquireLock() throws InterruptedException { return lock.tryLock(); }Большая часть кода взята из ABQ, если что. Причем без backoff() эта штука обходит ABQ вдвое, а с задержкой через Thread.yield() -- почти вчетверо. Простота залог успеха. А я в прошлый заход сразу полез в дебри CAS-free.
27 марта 2012 г.
...и вновь продолжается бой
Я тут в процессе подготовки к докладу -- прорабатываю тему Disruptor-а. И вот любопытные вещи обнаруживаются. Вот оказывается, что чтобы скакнуть почти вдвое от производительности ABQ нет необходимости реализовывать сразу хитрую очередь из Art of MP. Достаточно убрать из ABQ Condition-ы. Да, вполне можно оставить ReentrantLock, просто вместо ожидания на Condition notEmpty/notFull сделать spin-wait:
23 марта 2012 г.
Keep call stacks small
В последней статье Мартина Томпсона встретил интересную идею в списке техник оптимизации:
Keep call stacks reasonably small. Still more work to do here. If you are crazy enough to use Spring, then check out your call stacks to see what I mean! The garbage collector has to walk them finding reachable objects.
Это интересная мысль, стоит ее подумать и потестировать. Т.е. она выглядит логичной -- если у вас "сверху" над вашим рабочим стеком висит еще 15 вызовов Спринга, то GC и правда на каждой итерации (даже при сборке молодого поколения) придется сканировать все эти 15 фреймов, и все достижимые из них объекты. Вопрос лишь в том, каков порядок величины этого эффекта -- начиная с какого уровня заоптимизированности кода имеет смысл начинать думать об этом, как о возможном лимитирующем факторе.
Keep call stacks reasonably small. Still more work to do here. If you are crazy enough to use Spring, then check out your call stacks to see what I mean! The garbage collector has to walk them finding reachable objects.
Это интересная мысль, стоит ее подумать и потестировать. Т.е. она выглядит логичной -- если у вас "сверху" над вашим рабочим стеком висит еще 15 вызовов Спринга, то GC и правда на каждой итерации (даже при сборке молодого поколения) придется сканировать все эти 15 фреймов, и все достижимые из них объекты. Вопрос лишь в том, каков порядок величины этого эффекта -- начиная с какого уровня заоптимизированности кода имеет смысл начинать думать об этом, как о возможном лимитирующем факторе.
Подписаться на:
Сообщения (Atom)