Skip to content

Commit

Permalink
Use spaces (vs object padding) for ring buffers
Browse files Browse the repository at this point in the history
Padding is used to avoid false sharing of the cache lines. Previously
the bloat of an object (header, etc) was used as filler, which is a
little better on a laptop. On a server-class machine, spacing the value
out in a primitive array is more efficient. So favoring large machine
over small, but the difference is not significant to bother anyone in
practice.
  • Loading branch information
ben-manes committed Nov 9, 2015
1 parent 4b73c14 commit 8ceaf81
Showing 1 changed file with 20 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/
package com.github.benmanes.caffeine.cache;

import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;
import java.util.function.Consumer;

import com.github.benmanes.caffeine.base.UnsafeAccess;
Expand Down Expand Up @@ -47,40 +47,39 @@ final class BoundedBuffer<E> extends StripedBuffer<E> {
*/

/** The maximum number of elements per buffer. */
static final int BUFFER_SIZE = 32;
static final int BUFFER_SIZE = 16;

/** Mask value for indexing into the ring buffer. */
static final int BUFFER_MASK = BUFFER_SIZE - 1;
// Assume 4-byte references and 64-byte cache line (16 elements per line)
static final int SPACED_SIZE = BUFFER_SIZE << 4;
static final int SPACED_MASK = SPACED_SIZE - 1;
static final int OFFSET = 16;

@Override
protected Buffer<E> create(E e) {
return new RingBuffer<>(e);
}

static final class RingBuffer<E> extends BBHeader.ReadAndWriteCounterRef implements Buffer<E> {
final AtomicReference<E>[] buffer;
final AtomicReferenceArray<E> buffer;

@SuppressWarnings({"unchecked", "cast", "rawtypes"})
public RingBuffer(E e) {
super(1);
buffer = new AtomicReference[BUFFER_SIZE];
for (int i = 0; i < BUFFER_SIZE; i++) {
buffer[i] = new AtomicReference<>();
}
buffer[0].lazySet(e);
super(OFFSET);
buffer = new AtomicReferenceArray<>(SPACED_SIZE);
buffer.lazySet(0, e);
}

@Override
public int offer(E e) {
long head = readCounter;
long tail = relaxedWriteCounter();
long size = (tail - head);
if (size >= BUFFER_SIZE) {
if (size >= SPACED_SIZE) {
return Buffer.FULL;
}
if (casWriteCounter(tail, tail + 1)) {
int index = (int) (tail & BUFFER_MASK);
buffer[index].lazySet(e);
if (casWriteCounter(tail, tail + OFFSET)) {
int index = (int) (tail & SPACED_MASK);
buffer.lazySet(index, e);
return Buffer.SUCCESS;
}
return Buffer.FAILED;
Expand All @@ -95,28 +94,27 @@ public void drainTo(Consumer<E> consumer) {
return;
}
do {
int index = (int) (head & BUFFER_MASK);
AtomicReference<E> slot = buffer[index];
E e = slot.get();
int index = (int) (head & SPACED_MASK);
E e = buffer.get(index);
if (e == null) {
// not published yet
break;
}
slot.lazySet(null);
buffer.lazySet(index, null);
consumer.accept(e);
head++;
head += OFFSET;
} while (head != tail);
lazySetReadCounter(head);
}

@Override
public int reads() {
return (int) readCounter;
return (int) readCounter / OFFSET;
}

@Override
public int writes() {
return (int) writeCounter;
return (int) writeCounter / OFFSET;
}
}
}
Expand Down

0 comments on commit 8ceaf81

Please sign in to comment.