├── .gitignore ├── README.md └── src └── java ├── psy └── lob │ └── saw │ ├── ipc │ └── IpcPerfTest.java │ └── queues │ ├── P1C1OffHeapQueue.java │ ├── P1C1Queue2CacheLinesHeapBuffer.java │ ├── P1C1Queue4CacheLinesHeapBuffer.java │ ├── P1C1Queue4CacheLinesHeapBufferUnsafe.java │ ├── UnsafeAccess.java │ └── UnsafeDirectByteBuffer.java └── uk └── co └── real_logic └── queues ├── P1C1QueueOriginal1.java ├── P1C1QueueOriginal12.java ├── P1C1QueueOriginal2.java ├── P1C1QueueOriginal21.java ├── P1C1QueueOriginal22.java ├── P1C1QueueOriginal23.java ├── P1C1QueueOriginal3.java ├── P1C1QueueOriginalPrimitive.java ├── PaddedAtomicLong.java └── QueuePerfTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | examples.iml 2 | .idea 3 | target 4 | /bin 5 | /.classpath 6 | /.project 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | examples 2 | ======== 3 | Forking Martin's code to compare off-heap implementation of single producer/consumer 4 | Q with original. Taking the off heap implementation and using it to implement and IPC 5 | queue. 6 | P1C1OffHeapQueue is the end result for an integer IPC queue. To run the perf test 7 | launch a producer process(first) and a consumer process: 8 | java -cp bin psy.lob.saw.ipc.IpcPerfTest p 9 | java -cp bin psy.lob.saw.ipc.IpcPerfTest c -------------------------------------------------------------------------------- /src/java/psy/lob/saw/ipc/IpcPerfTest.java: -------------------------------------------------------------------------------- 1 | package psy.lob.saw.ipc; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | import java.io.IOException; 6 | import java.io.RandomAccessFile; 7 | import java.nio.ByteBuffer; 8 | import java.nio.MappedByteBuffer; 9 | import java.nio.channels.FileChannel; 10 | import java.nio.channels.FileChannel.MapMode; 11 | import java.util.Queue; 12 | 13 | import psy.lob.saw.queues.P1C1OffHeapQueue; 14 | import psy.lob.saw.queues.UnsafeAccess; 15 | import psy.lob.saw.queues.UnsafeDirectByteBuffer; 16 | 17 | 18 | /** 19 | * @author Nitsan 20 | * 21 | */ 22 | public class IpcPerfTest { 23 | public static final int QUEUE_CAPACITY = 1 << Integer.getInteger("scale", 16) ; 24 | public static final int REPETITIONS = Integer.getInteger("reps", 50) * 1000 * 1000; 25 | public static final Integer TEST_VALUE = Integer.valueOf(777); 26 | public static ByteBuffer syncArea; 27 | public static void main(final String[] args) throws Exception { 28 | byte type= P1C1OffHeapQueue.CONSUMER; 29 | if (args.length > 1 30 | || (args.length == 1 && !args[0].equalsIgnoreCase("p") && !args[0] 31 | .equalsIgnoreCase("c"))) { 32 | throw new IllegalArgumentException( 33 | "Expecting 0 or 1 arguments: p for producer, c for consumer"); 34 | } 35 | else if(args.length == 1 && args[0].equalsIgnoreCase("p")){ 36 | type= P1C1OffHeapQueue.PRODUCER; 37 | } 38 | MappedByteBuffer ipcBuffer; 39 | if(type != P1C1OffHeapQueue.CONSUMER){ 40 | ipcBuffer = createAndLoadMappedFile(); 41 | } else { 42 | ipcBuffer = waitForFileCreateMappedBuffer(); 43 | } 44 | final Queue queue = new P1C1OffHeapQueue( 45 | ipcBuffer, QUEUE_CAPACITY, type); 46 | for (int i = 0; i < 20; i++) { 47 | System.gc(); 48 | if(type == P1C1OffHeapQueue.CONSUMER){ 49 | consumerRun(i, queue); 50 | } 51 | else { 52 | producerRun(i, queue); 53 | } 54 | } 55 | } 56 | 57 | private static MappedByteBuffer createAndLoadMappedFile() 58 | throws FileNotFoundException, IOException { 59 | File ipcFile = new File("queue.ipc"); 60 | if (ipcFile.exists()) { 61 | ipcFile.delete(); 62 | } 63 | ipcFile.deleteOnExit(); 64 | FileChannel channel = new RandomAccessFile(ipcFile, "rw").getChannel(); 65 | int minimumBuffSize = QUEUE_CAPACITY * 4 + 64 * 6; 66 | if (channel.size() < minimumBuffSize) { 67 | ByteBuffer temp = ByteBuffer.allocateDirect(64 * 1024); 68 | while (channel.size() < minimumBuffSize) { 69 | channel.write(temp); 70 | temp.clear(); 71 | } 72 | } 73 | MappedByteBuffer ipcBuffer = channel.map(MapMode.READ_WRITE, 64, 74 | minimumBuffSize-64); 75 | syncArea = channel.map(MapMode.READ_WRITE, 0, 64); 76 | syncArea.putInt(-1); 77 | ipcBuffer.load(); 78 | return ipcBuffer; 79 | } 80 | 81 | private static MappedByteBuffer waitForFileCreateMappedBuffer() 82 | throws InterruptedException, FileNotFoundException, IOException { 83 | File ipcFile = new File("queue.ipc"); 84 | while (!ipcFile.exists()) { 85 | Thread.sleep(1000); 86 | } 87 | 88 | FileChannel channel = new RandomAccessFile(ipcFile, "rw").getChannel(); 89 | int minimumBuffSize = QUEUE_CAPACITY * 4 + 64 * 6; 90 | while (channel.size() < minimumBuffSize) { 91 | Thread.sleep(1000); 92 | } 93 | MappedByteBuffer ipcBuffer = channel.map(MapMode.READ_WRITE, 64, 94 | minimumBuffSize-64); 95 | ipcBuffer.load(); 96 | syncArea = channel.map(MapMode.READ_WRITE, 0, 64); 97 | return ipcBuffer; 98 | } 99 | private static void producerRun(final int runNumber, 100 | final Queue queue) throws Exception { 101 | long syncAddress = UnsafeDirectByteBuffer.getAddress(syncArea); 102 | while(!UnsafeAccess.unsafe.compareAndSwapInt(null, syncAddress, 3*runNumber, 3*runNumber)) 103 | Thread.yield(); 104 | final long start = System.nanoTime(); 105 | int i = REPETITIONS; 106 | do { 107 | while (!queue.offer(TEST_VALUE)) { 108 | // Thread.yield(); 109 | } 110 | } while (0 != --i); 111 | while(!UnsafeAccess.unsafe.compareAndSwapInt(null, syncAddress, 3*runNumber+1, 3*runNumber+2)) 112 | Thread.yield(); 113 | final long duration = System.nanoTime() - start; 114 | final long ops = (REPETITIONS * 1000L * 1000L * 1000L) / duration; 115 | System.out.format("%d - ops/sec=%,d - %s result=%d\n", Integer 116 | .valueOf(runNumber), Long.valueOf(ops), queue.getClass() 117 | .getSimpleName(), queue.size()); 118 | } 119 | 120 | private static void consumerRun(final int runNumber, 121 | final Queue queue) throws Exception { 122 | long syncAddress = UnsafeDirectByteBuffer.getAddress(syncArea); 123 | while(!UnsafeAccess.unsafe.compareAndSwapInt(null, syncAddress, 3*runNumber-1, 3*runNumber)) 124 | Thread.yield(); 125 | Integer result; 126 | int i = REPETITIONS; 127 | do { 128 | while (null == (result = queue.poll())) { 129 | // Thread.yield(); 130 | } 131 | } while (0 != --i); 132 | while(!UnsafeAccess.unsafe.compareAndSwapInt(null, syncAddress, 3*runNumber, 3*runNumber+1)) 133 | Thread.yield(); 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /src/java/psy/lob/saw/queues/P1C1OffHeapQueue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package psy.lob.saw.queues; 17 | 18 | import static psy.lob.saw.queues.UnsafeDirectByteBuffer.CACHE_LINE_SIZE; 19 | import static psy.lob.saw.queues.UnsafeDirectByteBuffer.alignedSlice; 20 | import static psy.lob.saw.queues.UnsafeDirectByteBuffer.allocateAlignedByteBuffer; 21 | 22 | import java.nio.ByteBuffer; 23 | import java.util.Collection; 24 | import java.util.Iterator; 25 | import java.util.NoSuchElementException; 26 | import java.util.Queue; 27 | 28 | public final class P1C1OffHeapQueue implements Queue { 29 | public final static byte PRODUCER = 1; 30 | public final static byte CONSUMER = 2; 31 | // 24b,8b,32b | 24b,8b,32b | 24b,8b,32b | 24b,8b,32b 32 | private final ByteBuffer buffy; 33 | private final long headAddress; 34 | private final long tailCacheAddress; 35 | private final long tailAddress; 36 | private final long headCacheAddress; 37 | 38 | private final int capacity; 39 | private final int mask; 40 | private final long arrayBase; 41 | private static final int INT_ELEMENT_SCALE = 2; 42 | 43 | public P1C1OffHeapQueue(final int capacity) { 44 | this(allocateAlignedByteBuffer( 45 | 4 * CACHE_LINE_SIZE + findNextPositivePowerOfTwo(capacity)<= getTailCache()) { 119 | setTailCache(getTail()); 120 | if (currentHead >= getTailCache()) { 121 | return null; 122 | } 123 | } 124 | 125 | final long offset = arrayBase + ((currentHead & mask) << INT_ELEMENT_SCALE); 126 | final int e = UnsafeAccess.unsafe.getInt(offset); 127 | // UnsafeAccess.unsafe.putInt(null, offset, 0); 128 | setHead(currentHead + 1); 129 | 130 | return e; 131 | } 132 | 133 | public Integer remove() { 134 | final Integer e = poll(); 135 | if (null == e) { 136 | throw new NoSuchElementException("Queue is empty"); 137 | } 138 | 139 | return e; 140 | } 141 | 142 | public Integer element() { 143 | final Integer e = peek(); 144 | if (null == e) { 145 | throw new NoSuchElementException("Queue is empty"); 146 | } 147 | 148 | return e; 149 | } 150 | 151 | public Integer peek() { 152 | return null; 153 | } 154 | 155 | public int size() { 156 | return (int) (getTail() - getHead()); 157 | } 158 | 159 | public boolean isEmpty() { 160 | return getTail() == getHead(); 161 | } 162 | 163 | public boolean contains(final Object o) { 164 | return false; 165 | } 166 | 167 | public Iterator iterator() { 168 | throw new UnsupportedOperationException(); 169 | } 170 | 171 | public Object[] toArray() { 172 | throw new UnsupportedOperationException(); 173 | } 174 | 175 | public T[] toArray(final T[] a) { 176 | throw new UnsupportedOperationException(); 177 | } 178 | 179 | public boolean remove(final Object o) { 180 | throw new UnsupportedOperationException(); 181 | } 182 | 183 | public boolean containsAll(final Collection c) { 184 | for (final Object o : c) { 185 | if (!contains(o)) { 186 | return false; 187 | } 188 | } 189 | 190 | return true; 191 | } 192 | 193 | public boolean addAll(final Collection c) { 194 | for (final Integer e : c) { 195 | add(e); 196 | } 197 | 198 | return true; 199 | } 200 | 201 | public boolean removeAll(final Collection c) { 202 | throw new UnsupportedOperationException(); 203 | } 204 | 205 | public boolean retainAll(final Collection c) { 206 | throw new UnsupportedOperationException(); 207 | } 208 | 209 | public void clear() { 210 | Object value; 211 | do { 212 | value = poll(); 213 | } while (null != value); 214 | } 215 | 216 | private long getHead() { 217 | return UnsafeAccess.unsafe.getLongVolatile(null, headAddress); 218 | } 219 | 220 | private void setHead(final long value) { 221 | UnsafeAccess.unsafe.putOrderedLong(null, headAddress, value); 222 | } 223 | 224 | private long getTail() { 225 | return UnsafeAccess.unsafe.getLongVolatile(null, tailAddress); 226 | } 227 | 228 | private void setTail(final long value) { 229 | UnsafeAccess.unsafe.putOrderedLong(null, tailAddress, value); 230 | } 231 | 232 | private long getHeadCache() { 233 | return UnsafeAccess.unsafe.getLong(null, headCacheAddress); 234 | } 235 | 236 | private void setHeadCache(final long value) { 237 | UnsafeAccess.unsafe.putLong(headCacheAddress, value); 238 | } 239 | 240 | private long getTailCache() { 241 | return UnsafeAccess.unsafe.getLong(null, tailCacheAddress); 242 | } 243 | 244 | private void setTailCache(final long value) { 245 | UnsafeAccess.unsafe.putLong(tailCacheAddress, value); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /src/java/psy/lob/saw/queues/P1C1Queue2CacheLinesHeapBuffer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package psy.lob.saw.queues; 17 | 18 | import static psy.lob.saw.queues.UnsafeDirectByteBuffer.CACHE_LINE_SIZE; 19 | import static psy.lob.saw.queues.UnsafeDirectByteBuffer.allocateAlignedByteBuffer; 20 | 21 | import java.nio.ByteBuffer; 22 | import java.util.Collection; 23 | import java.util.Iterator; 24 | import java.util.NoSuchElementException; 25 | import java.util.Queue; 26 | 27 | public final class P1C1Queue2CacheLinesHeapBuffer implements Queue { 28 | // 24b,8b, 8b, 24b | ,24b,8b, 8b, 24b 29 | // PAD,head,tailCache,PAD,PAD,tail,headCache,PAD 30 | private final ByteBuffer buffy = allocateAlignedByteBuffer( 31 | 2 * CACHE_LINE_SIZE, CACHE_LINE_SIZE); 32 | private final long headAddress; 33 | private final long tailCacheAddress; 34 | private final long tailAddress; 35 | private final long headCacheAddress; 36 | 37 | private final int capacity; 38 | private final int mask; 39 | private final E[] buffer; 40 | 41 | @SuppressWarnings("unchecked") 42 | public P1C1Queue2CacheLinesHeapBuffer(final int capacity) { 43 | long alignedAddress = UnsafeDirectByteBuffer.getAddress(buffy); 44 | 45 | headAddress = alignedAddress + (CACHE_LINE_SIZE / 2 - 8); 46 | tailCacheAddress = headAddress + 8; 47 | tailAddress = headAddress + CACHE_LINE_SIZE; 48 | headCacheAddress = tailAddress + 8; 49 | 50 | this.capacity = findNextPositivePowerOfTwo(capacity); 51 | mask = this.capacity - 1; 52 | buffer = (E[]) new Object[this.capacity]; 53 | } 54 | 55 | public static int findNextPositivePowerOfTwo(final int value) { 56 | return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); 57 | } 58 | 59 | public boolean add(final E e) { 60 | if (offer(e)) { 61 | return true; 62 | } 63 | 64 | throw new IllegalStateException("Queue is full"); 65 | } 66 | 67 | public boolean offer(final E e) { 68 | if (null == e) { 69 | throw new NullPointerException("Null is not a valid element"); 70 | } 71 | 72 | final long currentTail = getTail(); 73 | final long wrapPoint = currentTail - capacity; 74 | if (getHeadCache() <= wrapPoint) { 75 | setHeadCache(getHead()); 76 | if (getHeadCache() <= wrapPoint) { 77 | return false; 78 | } 79 | } 80 | 81 | buffer[(int) currentTail & mask] = e; 82 | setTail(currentTail + 1); 83 | 84 | return true; 85 | } 86 | 87 | public E poll() { 88 | final long currentHead = getHead(); 89 | if (currentHead >= getTailCache()) { 90 | setTailCache(getTail()); 91 | if (currentHead >= getTailCache()) { 92 | return null; 93 | } 94 | } 95 | 96 | final int index = (int) currentHead & mask; 97 | final E e = buffer[index]; 98 | buffer[index] = null; 99 | setHead(currentHead + 1); 100 | 101 | return e; 102 | } 103 | 104 | public E remove() { 105 | final E e = poll(); 106 | if (null == e) { 107 | throw new NoSuchElementException("Queue is empty"); 108 | } 109 | 110 | return e; 111 | } 112 | 113 | public E element() { 114 | final E e = peek(); 115 | if (null == e) { 116 | throw new NoSuchElementException("Queue is empty"); 117 | } 118 | 119 | return e; 120 | } 121 | 122 | public E peek() { 123 | return buffer[(int) getHead() & mask]; 124 | } 125 | 126 | public int size() { 127 | return (int) (getTail() - getHead()); 128 | } 129 | 130 | public boolean isEmpty() { 131 | return getTail() == getHead(); 132 | } 133 | 134 | public boolean contains(final Object o) { 135 | if (null == o) { 136 | return false; 137 | } 138 | 139 | for (long i = getHead(), limit = getTail(); i < limit; i++) { 140 | final E e = buffer[(int) i & mask]; 141 | if (o.equals(e)) { 142 | return true; 143 | } 144 | } 145 | 146 | return false; 147 | } 148 | 149 | public Iterator iterator() { 150 | throw new UnsupportedOperationException(); 151 | } 152 | 153 | public Object[] toArray() { 154 | throw new UnsupportedOperationException(); 155 | } 156 | 157 | public T[] toArray(final T[] a) { 158 | throw new UnsupportedOperationException(); 159 | } 160 | 161 | public boolean remove(final Object o) { 162 | throw new UnsupportedOperationException(); 163 | } 164 | 165 | public boolean containsAll(final Collection c) { 166 | for (final Object o : c) { 167 | if (!contains(o)) { 168 | return false; 169 | } 170 | } 171 | 172 | return true; 173 | } 174 | 175 | public boolean addAll(final Collection c) { 176 | for (final E e : c) { 177 | add(e); 178 | } 179 | 180 | return true; 181 | } 182 | 183 | public boolean removeAll(final Collection c) { 184 | throw new UnsupportedOperationException(); 185 | } 186 | 187 | public boolean retainAll(final Collection c) { 188 | throw new UnsupportedOperationException(); 189 | } 190 | 191 | public void clear() { 192 | Object value; 193 | do { 194 | value = poll(); 195 | } while (null != value); 196 | } 197 | 198 | private long getHead() { 199 | return UnsafeAccess.unsafe.getLongVolatile(null, headAddress); 200 | } 201 | 202 | private void setHead(final long value) { 203 | UnsafeAccess.unsafe.putOrderedLong(null, headAddress, value); 204 | } 205 | 206 | private long getTail() { 207 | return UnsafeAccess.unsafe.getLongVolatile(null, tailAddress); 208 | } 209 | 210 | private void setTail(final long value) { 211 | UnsafeAccess.unsafe.putOrderedLong(null, tailAddress, value); 212 | } 213 | 214 | private long getHeadCache() { 215 | return UnsafeAccess.unsafe.getLong(null, headCacheAddress); 216 | } 217 | 218 | private void setHeadCache(final long value) { 219 | UnsafeAccess.unsafe.putLong(headCacheAddress, value); 220 | } 221 | 222 | private long getTailCache() { 223 | return UnsafeAccess.unsafe.getLong(null, tailCacheAddress); 224 | } 225 | 226 | private void setTailCache(final long value) { 227 | UnsafeAccess.unsafe.putLong(tailCacheAddress, value); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/java/psy/lob/saw/queues/P1C1Queue4CacheLinesHeapBuffer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package psy.lob.saw.queues; 17 | 18 | import static psy.lob.saw.queues.UnsafeDirectByteBuffer.*; 19 | 20 | import java.nio.ByteBuffer; 21 | import java.util.Collection; 22 | import java.util.Iterator; 23 | import java.util.NoSuchElementException; 24 | import java.util.Queue; 25 | 26 | public final class P1C1Queue4CacheLinesHeapBuffer implements Queue { 27 | // 24b,8b,32b | 24b,8b,32b | 24b,8b,32b | 24b,8b,32b 28 | private final ByteBuffer buffy = allocateAlignedByteBuffer( 29 | 4 * CACHE_LINE_SIZE, CACHE_LINE_SIZE); 30 | private final long headAddress; 31 | private final long tailCacheAddress; 32 | private final long tailAddress; 33 | private final long headCacheAddress; 34 | 35 | private final int capacity; 36 | private final int mask; 37 | private final E[] buffer; 38 | 39 | @SuppressWarnings("unchecked") 40 | public P1C1Queue4CacheLinesHeapBuffer(final int capacity) { 41 | long alignedAddress = UnsafeDirectByteBuffer.getAddress(buffy); 42 | 43 | headAddress = alignedAddress + (CACHE_LINE_SIZE / 2 - 8); 44 | tailCacheAddress = headAddress + CACHE_LINE_SIZE; 45 | tailAddress = tailCacheAddress + CACHE_LINE_SIZE; 46 | headCacheAddress = tailAddress + CACHE_LINE_SIZE; 47 | 48 | this.capacity = findNextPositivePowerOfTwo(capacity); 49 | mask = this.capacity - 1; 50 | buffer = (E[]) new Object[this.capacity]; 51 | } 52 | 53 | public static int findNextPositivePowerOfTwo(final int value) { 54 | return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); 55 | } 56 | 57 | public boolean add(final E e) { 58 | if (offer(e)) { 59 | return true; 60 | } 61 | 62 | throw new IllegalStateException("Queue is full"); 63 | } 64 | 65 | public boolean offer(final E e) { 66 | if (null == e) { 67 | throw new NullPointerException("Null is not a valid element"); 68 | } 69 | 70 | final long currentTail = getTail(); 71 | final long wrapPoint = currentTail - capacity; 72 | if (getHeadCache() <= wrapPoint) { 73 | setHeadCache(getHead()); 74 | if (getHeadCache() <= wrapPoint) { 75 | return false; 76 | } 77 | } 78 | 79 | buffer[(int) currentTail & mask] = e; 80 | setTail(currentTail + 1); 81 | 82 | return true; 83 | } 84 | 85 | public E poll() { 86 | final long currentHead = getHead(); 87 | if (currentHead >= getTailCache()) { 88 | setTailCache(getTail()); 89 | if (currentHead >= getTailCache()) { 90 | return null; 91 | } 92 | } 93 | 94 | final int index = (int) currentHead & mask; 95 | final E e = buffer[index]; 96 | buffer[index] = null; 97 | setHead(currentHead + 1); 98 | 99 | return e; 100 | } 101 | 102 | public E remove() { 103 | final E e = poll(); 104 | if (null == e) { 105 | throw new NoSuchElementException("Queue is empty"); 106 | } 107 | 108 | return e; 109 | } 110 | 111 | public E element() { 112 | final E e = peek(); 113 | if (null == e) { 114 | throw new NoSuchElementException("Queue is empty"); 115 | } 116 | 117 | return e; 118 | } 119 | 120 | public E peek() { 121 | return buffer[(int) getHead() & mask]; 122 | } 123 | 124 | public int size() { 125 | return (int) (getTail() - getHead()); 126 | } 127 | 128 | public boolean isEmpty() { 129 | return getTail() == getHead(); 130 | } 131 | 132 | public boolean contains(final Object o) { 133 | if (null == o) { 134 | return false; 135 | } 136 | 137 | for (long i = getHead(), limit = getTail(); i < limit; i++) { 138 | final E e = buffer[(int) i & mask]; 139 | if (o.equals(e)) { 140 | return true; 141 | } 142 | } 143 | 144 | return false; 145 | } 146 | 147 | public Iterator iterator() { 148 | throw new UnsupportedOperationException(); 149 | } 150 | 151 | public Object[] toArray() { 152 | throw new UnsupportedOperationException(); 153 | } 154 | 155 | public T[] toArray(final T[] a) { 156 | throw new UnsupportedOperationException(); 157 | } 158 | 159 | public boolean remove(final Object o) { 160 | throw new UnsupportedOperationException(); 161 | } 162 | 163 | public boolean containsAll(final Collection c) { 164 | for (final Object o : c) { 165 | if (!contains(o)) { 166 | return false; 167 | } 168 | } 169 | 170 | return true; 171 | } 172 | 173 | public boolean addAll(final Collection c) { 174 | for (final E e : c) { 175 | add(e); 176 | } 177 | 178 | return true; 179 | } 180 | 181 | public boolean removeAll(final Collection c) { 182 | throw new UnsupportedOperationException(); 183 | } 184 | 185 | public boolean retainAll(final Collection c) { 186 | throw new UnsupportedOperationException(); 187 | } 188 | 189 | public void clear() { 190 | Object value; 191 | do { 192 | value = poll(); 193 | } while (null != value); 194 | } 195 | 196 | private long getHead() { 197 | return UnsafeAccess.unsafe.getLongVolatile(null, headAddress); 198 | } 199 | 200 | private void setHead(final long value) { 201 | UnsafeAccess.unsafe.putOrderedLong(null, headAddress, value); 202 | } 203 | 204 | private long getTail() { 205 | return UnsafeAccess.unsafe.getLongVolatile(null, tailAddress); 206 | } 207 | 208 | private void setTail(final long value) { 209 | UnsafeAccess.unsafe.putOrderedLong(null, tailAddress, value); 210 | } 211 | 212 | private long getHeadCache() { 213 | return UnsafeAccess.unsafe.getLong(null, headCacheAddress); 214 | } 215 | 216 | private void setHeadCache(final long value) { 217 | UnsafeAccess.unsafe.putLong(headCacheAddress, value); 218 | } 219 | 220 | private long getTailCache() { 221 | return UnsafeAccess.unsafe.getLong(null, tailCacheAddress); 222 | } 223 | 224 | private void setTailCache(final long value) { 225 | UnsafeAccess.unsafe.putLong(tailCacheAddress, value); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/java/psy/lob/saw/queues/P1C1Queue4CacheLinesHeapBufferUnsafe.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package psy.lob.saw.queues; 17 | 18 | import static psy.lob.saw.queues.UnsafeDirectByteBuffer.*; 19 | 20 | import java.nio.ByteBuffer; 21 | import java.util.Collection; 22 | import java.util.Iterator; 23 | import java.util.NoSuchElementException; 24 | import java.util.Queue; 25 | 26 | public final class P1C1Queue4CacheLinesHeapBufferUnsafe implements Queue { 27 | // 24b,8b,32b | 24b,8b,32b | 24b,8b,32b | 24b,8b,32b 28 | private final ByteBuffer buffy = allocateAlignedByteBuffer( 29 | 4 * CACHE_LINE_SIZE, CACHE_LINE_SIZE); 30 | private final long headAddress; 31 | private final long tailCacheAddress; 32 | private final long tailAddress; 33 | private final long headCacheAddress; 34 | 35 | private final int capacity; 36 | private final int mask; 37 | private final E[] buffer; 38 | private static final long arrayBase; 39 | private static final int arrayScale; 40 | 41 | static { 42 | try { 43 | arrayBase = UnsafeAccess.unsafe.arrayBaseOffset(Object[].class); 44 | final int scale = UnsafeAccess.unsafe 45 | .arrayIndexScale(Object[].class); 46 | 47 | if (4 == scale) { 48 | arrayScale = 2; 49 | } else if (8 == scale) { 50 | arrayScale = 3; 51 | } else { 52 | throw new IllegalStateException("Unknown pointer size"); 53 | } 54 | } catch (Exception e) { 55 | throw new RuntimeException(e); 56 | } 57 | } 58 | 59 | @SuppressWarnings("unchecked") 60 | public P1C1Queue4CacheLinesHeapBufferUnsafe(final int capacity) { 61 | long alignedAddress = UnsafeDirectByteBuffer.getAddress(buffy); 62 | 63 | headAddress = alignedAddress + (CACHE_LINE_SIZE / 2 - 8); 64 | tailCacheAddress = headAddress + CACHE_LINE_SIZE; 65 | tailAddress = tailCacheAddress + CACHE_LINE_SIZE; 66 | headCacheAddress = tailAddress + CACHE_LINE_SIZE; 67 | 68 | this.capacity = findNextPositivePowerOfTwo(capacity); 69 | mask = this.capacity - 1; 70 | buffer = (E[]) new Object[this.capacity]; 71 | } 72 | 73 | public static int findNextPositivePowerOfTwo(final int value) { 74 | return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); 75 | } 76 | 77 | public boolean add(final E e) { 78 | if (offer(e)) { 79 | return true; 80 | } 81 | 82 | throw new IllegalStateException("Queue is full"); 83 | } 84 | 85 | public boolean offer(final E e) { 86 | if (null == e) { 87 | throw new NullPointerException("Null is not a valid element"); 88 | } 89 | 90 | final long currentTail = getTail(); 91 | final long wrapPoint = currentTail - capacity; 92 | if (getHeadCache() <= wrapPoint) { 93 | setHeadCache(getHead()); 94 | if (getHeadCache() <= wrapPoint) { 95 | return false; 96 | } 97 | } 98 | 99 | UnsafeAccess.unsafe.putObject(buffer, arrayBase 100 | + ((currentTail & mask) << arrayScale), e); 101 | 102 | setTail(currentTail + 1); 103 | 104 | return true; 105 | } 106 | 107 | public E poll() { 108 | final long currentHead = getHead(); 109 | if (currentHead >= getTailCache()) { 110 | setTailCache(getTail()); 111 | if (currentHead >= getTailCache()) { 112 | return null; 113 | } 114 | } 115 | 116 | final long offset = arrayBase + ((currentHead & mask) << arrayScale); 117 | final E e = (E) UnsafeAccess.unsafe.getObject(buffer, offset); 118 | UnsafeAccess.unsafe.putObject(buffer, offset, null); 119 | setHead(currentHead + 1); 120 | 121 | return e; 122 | } 123 | 124 | public E remove() { 125 | final E e = poll(); 126 | if (null == e) { 127 | throw new NoSuchElementException("Queue is empty"); 128 | } 129 | 130 | return e; 131 | } 132 | 133 | public E element() { 134 | final E e = peek(); 135 | if (null == e) { 136 | throw new NoSuchElementException("Queue is empty"); 137 | } 138 | 139 | return e; 140 | } 141 | 142 | public E peek() { 143 | return buffer[(int) getHead() & mask]; 144 | } 145 | 146 | public int size() { 147 | return (int) (getTail() - getHead()); 148 | } 149 | 150 | public boolean isEmpty() { 151 | return getTail() == getHead(); 152 | } 153 | 154 | public boolean contains(final Object o) { 155 | if (null == o) { 156 | return false; 157 | } 158 | 159 | for (long i = getHead(), limit = getTail(); i < limit; i++) { 160 | final E e = buffer[(int) i & mask]; 161 | if (o.equals(e)) { 162 | return true; 163 | } 164 | } 165 | 166 | return false; 167 | } 168 | 169 | public Iterator iterator() { 170 | throw new UnsupportedOperationException(); 171 | } 172 | 173 | public Object[] toArray() { 174 | throw new UnsupportedOperationException(); 175 | } 176 | 177 | public T[] toArray(final T[] a) { 178 | throw new UnsupportedOperationException(); 179 | } 180 | 181 | public boolean remove(final Object o) { 182 | throw new UnsupportedOperationException(); 183 | } 184 | 185 | public boolean containsAll(final Collection c) { 186 | for (final Object o : c) { 187 | if (!contains(o)) { 188 | return false; 189 | } 190 | } 191 | 192 | return true; 193 | } 194 | 195 | public boolean addAll(final Collection c) { 196 | for (final E e : c) { 197 | add(e); 198 | } 199 | 200 | return true; 201 | } 202 | 203 | public boolean removeAll(final Collection c) { 204 | throw new UnsupportedOperationException(); 205 | } 206 | 207 | public boolean retainAll(final Collection c) { 208 | throw new UnsupportedOperationException(); 209 | } 210 | 211 | public void clear() { 212 | Object value; 213 | do { 214 | value = poll(); 215 | } while (null != value); 216 | } 217 | 218 | private long getHead() { 219 | return UnsafeAccess.unsafe.getLongVolatile(null, headAddress); 220 | } 221 | 222 | private void setHead(final long value) { 223 | UnsafeAccess.unsafe.putOrderedLong(null, headAddress, value); 224 | } 225 | 226 | private long getTail() { 227 | return UnsafeAccess.unsafe.getLongVolatile(null, tailAddress); 228 | } 229 | 230 | private void setTail(final long value) { 231 | UnsafeAccess.unsafe.putOrderedLong(null, tailAddress, value); 232 | } 233 | 234 | private long getHeadCache() { 235 | return UnsafeAccess.unsafe.getLong(null, headCacheAddress); 236 | } 237 | 238 | private void setHeadCache(final long value) { 239 | UnsafeAccess.unsafe.putLong(headCacheAddress, value); 240 | } 241 | 242 | private long getTailCache() { 243 | return UnsafeAccess.unsafe.getLong(null, tailCacheAddress); 244 | } 245 | 246 | private void setTailCache(final long value) { 247 | UnsafeAccess.unsafe.putLong(tailCacheAddress, value); 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/java/psy/lob/saw/queues/UnsafeAccess.java: -------------------------------------------------------------------------------- 1 | package psy.lob.saw.queues; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | import sun.misc.Unsafe; 6 | 7 | public class UnsafeAccess { 8 | public static final Unsafe unsafe; 9 | static { 10 | try { 11 | // This is a bit of voodoo to force the unsafe object into 12 | // visibility and acquire it. 13 | // This is not playing nice, but as an established back door it is 14 | // not likely to be 15 | // taken away. 16 | Field field = Unsafe.class.getDeclaredField("theUnsafe"); 17 | field.setAccessible(true); 18 | unsafe = (Unsafe) field.get(null); 19 | } catch (Exception e) { 20 | throw new RuntimeException(e); 21 | } 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/java/psy/lob/saw/queues/UnsafeDirectByteBuffer.java: -------------------------------------------------------------------------------- 1 | package psy.lob.saw.queues; 2 | 3 | import java.nio.Buffer; 4 | import java.nio.ByteBuffer; 5 | import java.nio.ByteOrder; 6 | 7 | public class UnsafeDirectByteBuffer { 8 | private static final long addressOffset; 9 | public static final int CACHE_LINE_SIZE = 64; 10 | public static final int PAGE_SIZE = UnsafeAccess.unsafe.pageSize(); 11 | static { 12 | try { 13 | addressOffset = UnsafeAccess.unsafe.objectFieldOffset(Buffer.class 14 | .getDeclaredField("address")); 15 | } catch (Exception e) { 16 | throw new RuntimeException(e); 17 | } 18 | } 19 | 20 | public static long getAddress(ByteBuffer buffy) { 21 | return UnsafeAccess.unsafe.getLong(buffy, addressOffset); 22 | } 23 | 24 | /** 25 | * put byte and skip position update and boundary checks 26 | * 27 | * @param buffy 28 | * @param b 29 | */ 30 | public static void putByte(long address, int position, byte b) { 31 | UnsafeAccess.unsafe.putByte(address + (position << 0), b); 32 | } 33 | 34 | public static void putByte(long address, byte b) { 35 | UnsafeAccess.unsafe.putByte(address, b); 36 | } 37 | 38 | public static ByteBuffer allocateAlignedByteBuffer(int capacity, long align) { 39 | if (Long.bitCount(align) != 1) { 40 | throw new IllegalArgumentException("Alignment must be a power of 2"); 41 | } 42 | ByteBuffer buffy = ByteBuffer.allocateDirect((int) (capacity + align)); 43 | return alignedSlice(capacity, align, buffy); 44 | } 45 | 46 | public static ByteBuffer alignedSlice(int capacity, long align, 47 | ByteBuffer buffy) { 48 | long address = getAddress(buffy); 49 | if ((address & (align - 1)) == 0) { 50 | return buffy; 51 | } else { 52 | int newPosition = (int) (align - (address & (align - 1))); 53 | if (newPosition + capacity > buffy.capacity()) { 54 | throw new IllegalArgumentException("it's impossible!"); 55 | } 56 | int oldPosition = buffy.position(); 57 | buffy.position(newPosition); 58 | int newLimit = newPosition + capacity; 59 | buffy.limit(newLimit); 60 | ByteBuffer slice = buffy.slice(); 61 | buffy.position(oldPosition); 62 | return slice; 63 | } 64 | } 65 | 66 | public static boolean isPageAligned(ByteBuffer buffy) { 67 | return isPageAligned(getAddress(buffy)); 68 | } 69 | 70 | /** 71 | * This assumes cache line is 64b 72 | */ 73 | public static boolean isCacheAligned(ByteBuffer buffy) { 74 | return isCacheAligned(getAddress(buffy)); 75 | } 76 | 77 | public static boolean isPageAligned(long address) { 78 | return (address & (PAGE_SIZE - 1)) == 0; 79 | } 80 | 81 | /** 82 | * This assumes cache line is 64b 83 | */ 84 | public static boolean isCacheAligned(long address) { 85 | return (address & (CACHE_LINE_SIZE - 1)) == 0; 86 | } 87 | 88 | public static boolean isAligned(long address, long align) { 89 | if (Long.bitCount(align) != 1) { 90 | throw new IllegalArgumentException("Alignment must be a power of 2"); 91 | } 92 | return (address & (align - 1)) == 0; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/java/uk/co/real_logic/queues/P1C1QueueOriginal1.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package uk.co.real_logic.queues; 17 | 18 | import java.util.Collection; 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | import java.util.Queue; 22 | 23 | /** 24 | *
    25 | *
  • Lock free, observing single writer principal. 26 | *
27 | */ 28 | public final class P1C1QueueOriginal1 implements Queue { 29 | private final E[] buffer; 30 | 31 | private volatile long tail = 0; 32 | private volatile long head = 0; 33 | 34 | @SuppressWarnings("unchecked") 35 | public P1C1QueueOriginal1(final int capacity) { 36 | buffer = (E[]) new Object[capacity]; 37 | } 38 | 39 | public boolean add(final E e) { 40 | if (offer(e)) { 41 | return true; 42 | } 43 | 44 | throw new IllegalStateException("Queue is full"); 45 | } 46 | 47 | public boolean offer(final E e) { 48 | if (null == e) { 49 | throw new NullPointerException("Null is not a valid element"); 50 | } 51 | 52 | final long currentTail = tail; 53 | final long wrapPoint = currentTail - buffer.length; 54 | if (head <= wrapPoint) { 55 | return false; 56 | } 57 | 58 | buffer[(int) (currentTail % buffer.length)] = e; 59 | tail = currentTail + 1; 60 | 61 | return true; 62 | } 63 | 64 | public E poll() { 65 | final long currentHead = head; 66 | if (currentHead >= tail) { 67 | return null; 68 | } 69 | 70 | final int index = (int) (currentHead % buffer.length); 71 | final E e = buffer[index]; 72 | buffer[index] = null; 73 | head = currentHead + 1; 74 | 75 | return e; 76 | } 77 | 78 | public E remove() { 79 | final E e = poll(); 80 | if (null == e) { 81 | throw new NoSuchElementException("Queue is empty"); 82 | } 83 | 84 | return e; 85 | } 86 | 87 | public E element() { 88 | final E e = peek(); 89 | if (null == e) { 90 | throw new NoSuchElementException("Queue is empty"); 91 | } 92 | 93 | return e; 94 | } 95 | 96 | public E peek() { 97 | return buffer[(int) (head % buffer.length)]; 98 | } 99 | 100 | public int size() { 101 | return (int) (tail - head); 102 | } 103 | 104 | public boolean isEmpty() { 105 | return tail == head; 106 | } 107 | 108 | public boolean contains(final Object o) { 109 | if (null == o) { 110 | return false; 111 | } 112 | 113 | for (long i = head, limit = tail; i < limit; i++) { 114 | final E e = buffer[(int) (i % buffer.length)]; 115 | if (o.equals(e)) { 116 | return true; 117 | } 118 | } 119 | 120 | return false; 121 | } 122 | 123 | public Iterator iterator() { 124 | throw new UnsupportedOperationException(); 125 | } 126 | 127 | public Object[] toArray() { 128 | throw new UnsupportedOperationException(); 129 | } 130 | 131 | public T[] toArray(final T[] a) { 132 | throw new UnsupportedOperationException(); 133 | } 134 | 135 | public boolean remove(final Object o) { 136 | throw new UnsupportedOperationException(); 137 | } 138 | 139 | public boolean containsAll(final Collection c) { 140 | for (final Object o : c) { 141 | if (!contains(o)) { 142 | return false; 143 | } 144 | } 145 | 146 | return true; 147 | } 148 | 149 | public boolean addAll(final Collection c) { 150 | for (final E e : c) { 151 | add(e); 152 | } 153 | 154 | return true; 155 | } 156 | 157 | public boolean removeAll(final Collection c) { 158 | throw new UnsupportedOperationException(); 159 | } 160 | 161 | public boolean retainAll(final Collection c) { 162 | throw new UnsupportedOperationException(); 163 | } 164 | 165 | public void clear() { 166 | Object value; 167 | do { 168 | value = poll(); 169 | } while (null != value); 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/java/uk/co/real_logic/queues/P1C1QueueOriginal12.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package uk.co.real_logic.queues; 17 | 18 | import java.util.Collection; 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | import java.util.Queue; 22 | import java.util.concurrent.atomic.AtomicLong; 23 | 24 | /** 25 | *
    26 | *
  • Lock free, observing single writer principal. 27 | *
  • Replacing the long fields with AtomicLong and using lazySet instead of 28 | * volatile assignment. 29 | *
30 | */ 31 | public final class P1C1QueueOriginal12 implements Queue { 32 | private final E[] buffer; 33 | 34 | private final AtomicLong tail = new AtomicLong(0); 35 | private final AtomicLong head = new AtomicLong(0); 36 | 37 | @SuppressWarnings("unchecked") 38 | public P1C1QueueOriginal12(int capacity) { 39 | buffer = (E[]) new Object[capacity]; 40 | } 41 | 42 | public static int findNextPositivePowerOfTwo(final int value) { 43 | return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); 44 | } 45 | 46 | public boolean add(final E e) { 47 | if (offer(e)) { 48 | return true; 49 | } 50 | 51 | throw new IllegalStateException("Queue is full"); 52 | } 53 | 54 | public boolean offer(final E e) { 55 | if (null == e) { 56 | throw new NullPointerException("Null is not a valid element"); 57 | } 58 | 59 | final long currentTail = tail.get(); 60 | final long wrapPoint = currentTail - buffer.length; 61 | if (head.get() <= wrapPoint) { 62 | return false; 63 | } 64 | 65 | buffer[(int) currentTail % buffer.length] = e; 66 | tail.lazySet(currentTail + 1); 67 | 68 | return true; 69 | } 70 | 71 | public E poll() { 72 | final long currentHead = head.get(); 73 | if (currentHead >= tail.get()) { 74 | return null; 75 | } 76 | 77 | final int index = (int) currentHead % buffer.length; 78 | final E e = buffer[index]; 79 | buffer[index] = null; 80 | head.lazySet(currentHead + 1); 81 | 82 | return e; 83 | } 84 | 85 | public E remove() { 86 | final E e = poll(); 87 | if (null == e) { 88 | throw new NoSuchElementException("Queue is empty"); 89 | } 90 | 91 | return e; 92 | } 93 | 94 | public E element() { 95 | final E e = peek(); 96 | if (null == e) { 97 | throw new NoSuchElementException("Queue is empty"); 98 | } 99 | 100 | return e; 101 | } 102 | 103 | public E peek() { 104 | return buffer[(int) head.get() % buffer.length]; 105 | } 106 | 107 | public int size() { 108 | return (int) (tail.get() - head.get()); 109 | } 110 | 111 | public boolean isEmpty() { 112 | return tail.get() == head.get(); 113 | } 114 | 115 | public boolean contains(final Object o) { 116 | if (null == o) { 117 | return false; 118 | } 119 | 120 | for (long i = head.get(), limit = tail.get(); i < limit; i++) { 121 | final E e = buffer[(int) i % buffer.length]; 122 | if (o.equals(e)) { 123 | return true; 124 | } 125 | } 126 | 127 | return false; 128 | } 129 | 130 | public Iterator iterator() { 131 | throw new UnsupportedOperationException(); 132 | } 133 | 134 | public Object[] toArray() { 135 | throw new UnsupportedOperationException(); 136 | } 137 | 138 | public T[] toArray(final T[] a) { 139 | throw new UnsupportedOperationException(); 140 | } 141 | 142 | public boolean remove(final Object o) { 143 | throw new UnsupportedOperationException(); 144 | } 145 | 146 | public boolean containsAll(final Collection c) { 147 | for (final Object o : c) { 148 | if (!contains(o)) { 149 | return false; 150 | } 151 | } 152 | 153 | return true; 154 | } 155 | 156 | public boolean addAll(final Collection c) { 157 | for (final E e : c) { 158 | add(e); 159 | } 160 | 161 | return true; 162 | } 163 | 164 | public boolean removeAll(final Collection c) { 165 | throw new UnsupportedOperationException(); 166 | } 167 | 168 | public boolean retainAll(final Collection c) { 169 | throw new UnsupportedOperationException(); 170 | } 171 | 172 | public void clear() { 173 | Object value; 174 | do { 175 | value = poll(); 176 | } while (null != value); 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /src/java/uk/co/real_logic/queues/P1C1QueueOriginal2.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package uk.co.real_logic.queues; 17 | 18 | import java.util.Collection; 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | import java.util.Queue; 22 | import java.util.concurrent.atomic.AtomicLong; 23 | /** 24 | *
    25 | *
  • Lock free, observing single writer principal. 26 | *
  • Replacing the long fields with AtomicLong and using lazySet instead of 27 | * volatile assignment. 28 | *
  • Using the power of 2 mask, forcing the capacity to next power of 2. 29 | *
30 | */ 31 | public final class P1C1QueueOriginal2 implements Queue { 32 | private final int mask; 33 | private final E[] buffer; 34 | 35 | private final AtomicLong tail = new AtomicLong(0); 36 | private final AtomicLong head = new AtomicLong(0); 37 | 38 | @SuppressWarnings("unchecked") 39 | public P1C1QueueOriginal2(int capacity) { 40 | capacity = findNextPositivePowerOfTwo(capacity); 41 | mask = capacity - 1; 42 | buffer = (E[]) new Object[capacity]; 43 | } 44 | 45 | public static int findNextPositivePowerOfTwo(final int value) { 46 | return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); 47 | } 48 | 49 | public boolean add(final E e) { 50 | if (offer(e)) { 51 | return true; 52 | } 53 | 54 | throw new IllegalStateException("Queue is full"); 55 | } 56 | 57 | public boolean offer(final E e) { 58 | if (null == e) { 59 | throw new NullPointerException("Null is not a valid element"); 60 | } 61 | 62 | final long currentTail = tail.get(); 63 | final long wrapPoint = currentTail - buffer.length; 64 | if (head.get() <= wrapPoint) { 65 | return false; 66 | } 67 | 68 | buffer[(int) currentTail & mask] = e; 69 | tail.lazySet(currentTail + 1); 70 | 71 | return true; 72 | } 73 | 74 | public E poll() { 75 | final long currentHead = head.get(); 76 | if (currentHead >= tail.get()) { 77 | return null; 78 | } 79 | 80 | final int index = (int) currentHead & mask; 81 | final E e = buffer[index]; 82 | buffer[index] = null; 83 | head.lazySet(currentHead + 1); 84 | 85 | return e; 86 | } 87 | 88 | public E remove() { 89 | final E e = poll(); 90 | if (null == e) { 91 | throw new NoSuchElementException("Queue is empty"); 92 | } 93 | 94 | return e; 95 | } 96 | 97 | public E element() { 98 | final E e = peek(); 99 | if (null == e) { 100 | throw new NoSuchElementException("Queue is empty"); 101 | } 102 | 103 | return e; 104 | } 105 | 106 | public E peek() { 107 | return buffer[(int) head.get() & mask]; 108 | } 109 | 110 | public int size() { 111 | return (int) (tail.get() - head.get()); 112 | } 113 | 114 | public boolean isEmpty() { 115 | return tail.get() == head.get(); 116 | } 117 | 118 | public boolean contains(final Object o) { 119 | if (null == o) { 120 | return false; 121 | } 122 | 123 | for (long i = head.get(), limit = tail.get(); i < limit; i++) { 124 | final E e = buffer[(int) i & mask]; 125 | if (o.equals(e)) { 126 | return true; 127 | } 128 | } 129 | 130 | return false; 131 | } 132 | 133 | public Iterator iterator() { 134 | throw new UnsupportedOperationException(); 135 | } 136 | 137 | public Object[] toArray() { 138 | throw new UnsupportedOperationException(); 139 | } 140 | 141 | public T[] toArray(final T[] a) { 142 | throw new UnsupportedOperationException(); 143 | } 144 | 145 | public boolean remove(final Object o) { 146 | throw new UnsupportedOperationException(); 147 | } 148 | 149 | public boolean containsAll(final Collection c) { 150 | for (final Object o : c) { 151 | if (!contains(o)) { 152 | return false; 153 | } 154 | } 155 | 156 | return true; 157 | } 158 | 159 | public boolean addAll(final Collection c) { 160 | for (final E e : c) { 161 | add(e); 162 | } 163 | 164 | return true; 165 | } 166 | 167 | public boolean removeAll(final Collection c) { 168 | throw new UnsupportedOperationException(); 169 | } 170 | 171 | public boolean retainAll(final Collection c) { 172 | throw new UnsupportedOperationException(); 173 | } 174 | 175 | public void clear() { 176 | Object value; 177 | do { 178 | value = poll(); 179 | } while (null != value); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/java/uk/co/real_logic/queues/P1C1QueueOriginal21.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package uk.co.real_logic.queues; 17 | 18 | import java.util.Collection; 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | import java.util.Queue; 22 | import java.util.concurrent.atomic.AtomicLong; 23 | 24 | /** 25 | *
    26 | *
  • Lock free, observing single writer principal. 27 | *
  • Replacing the long fields with AtomicLong and using lazySet instead of 28 | * volatile assignment. 29 | *
  • Using the power of 2 mask, forcing the capacity to next power of 2. 30 | *
  • Padding the head/tail fields to avoid false sharing. 31 | *
32 | */ 33 | public final class P1C1QueueOriginal21 implements Queue { 34 | private final int capacity; 35 | private final int mask; 36 | private final E[] buffer; 37 | 38 | private final AtomicLong tail = new PaddedAtomicLong(0); 39 | private final AtomicLong head = new PaddedAtomicLong(0); 40 | 41 | @SuppressWarnings("unchecked") 42 | public P1C1QueueOriginal21(final int capacity) { 43 | this.capacity = findNextPositivePowerOfTwo(capacity); 44 | mask = this.capacity - 1; 45 | buffer = (E[]) new Object[this.capacity]; 46 | } 47 | 48 | public static int findNextPositivePowerOfTwo(final int value) { 49 | return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); 50 | } 51 | 52 | public boolean add(final E e) { 53 | if (offer(e)) { 54 | return true; 55 | } 56 | 57 | throw new IllegalStateException("Queue is full"); 58 | } 59 | 60 | public boolean offer(final E e) { 61 | if (null == e) { 62 | throw new NullPointerException("Null is not a valid element"); 63 | } 64 | 65 | final long currentTail = tail.get(); 66 | final long wrapPoint = currentTail - buffer.length; 67 | if (head.get() <= wrapPoint) { 68 | return false; 69 | } 70 | 71 | buffer[(int) currentTail & mask] = e; 72 | tail.lazySet(currentTail + 1); 73 | 74 | return true; 75 | } 76 | 77 | public E poll() { 78 | final long currentHead = head.get(); 79 | if (currentHead >= tail.get()) { 80 | return null; 81 | } 82 | 83 | final int index = (int) currentHead & mask; 84 | final E e = buffer[index]; 85 | buffer[index] = null; 86 | head.lazySet(currentHead + 1); 87 | 88 | return e; 89 | } 90 | 91 | public E remove() { 92 | final E e = poll(); 93 | if (null == e) { 94 | throw new NoSuchElementException("Queue is empty"); 95 | } 96 | 97 | return e; 98 | } 99 | 100 | public E element() { 101 | final E e = peek(); 102 | if (null == e) { 103 | throw new NoSuchElementException("Queue is empty"); 104 | } 105 | 106 | return e; 107 | } 108 | 109 | public E peek() { 110 | return buffer[(int) head.get() & mask]; 111 | } 112 | 113 | public int size() { 114 | return (int) (tail.get() - head.get()); 115 | } 116 | 117 | public boolean isEmpty() { 118 | return tail.get() == head.get(); 119 | } 120 | 121 | public boolean contains(final Object o) { 122 | if (null == o) { 123 | return false; 124 | } 125 | 126 | for (long i = head.get(), limit = tail.get(); i < limit; i++) { 127 | final E e = buffer[(int) i & mask]; 128 | if (o.equals(e)) { 129 | return true; 130 | } 131 | } 132 | 133 | return false; 134 | } 135 | 136 | public Iterator iterator() { 137 | throw new UnsupportedOperationException(); 138 | } 139 | 140 | public Object[] toArray() { 141 | throw new UnsupportedOperationException(); 142 | } 143 | 144 | public T[] toArray(final T[] a) { 145 | throw new UnsupportedOperationException(); 146 | } 147 | 148 | public boolean remove(final Object o) { 149 | throw new UnsupportedOperationException(); 150 | } 151 | 152 | public boolean containsAll(final Collection c) { 153 | for (final Object o : c) { 154 | if (!contains(o)) { 155 | return false; 156 | } 157 | } 158 | 159 | return true; 160 | } 161 | 162 | public boolean addAll(final Collection c) { 163 | for (final E e : c) { 164 | add(e); 165 | } 166 | 167 | return true; 168 | } 169 | 170 | public boolean removeAll(final Collection c) { 171 | throw new UnsupportedOperationException(); 172 | } 173 | 174 | public boolean retainAll(final Collection c) { 175 | throw new UnsupportedOperationException(); 176 | } 177 | 178 | public void clear() { 179 | Object value; 180 | do { 181 | value = poll(); 182 | } while (null != value); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/java/uk/co/real_logic/queues/P1C1QueueOriginal22.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package uk.co.real_logic.queues; 17 | 18 | import java.util.Collection; 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | import java.util.Queue; 22 | import java.util.concurrent.atomic.AtomicLong; 23 | 24 | 25 | /** 26 | *
    27 | *
  • Lock free, observing single writer principal. 28 | *
  • Replacing the long fields with AtomicLong and using lazySet instead of 29 | * volatile assignment. 30 | *
  • Using the power of 2 mask, forcing the capacity to next power of 2. 31 | *
  • Adding head and tail cache fields. Avoiding redundant volatile reads. 32 | *
33 | */ 34 | public final class P1C1QueueOriginal22 implements Queue { 35 | private final int capacity; 36 | private final int mask; 37 | private final E[] buffer; 38 | 39 | private final AtomicLong tail = new AtomicLong(0); 40 | private final AtomicLong head = new AtomicLong(0); 41 | 42 | private long tailCache = 0; 43 | private long headCache = 0; 44 | 45 | @SuppressWarnings("unchecked") 46 | public P1C1QueueOriginal22(final int capacity) { 47 | this.capacity = findNextPositivePowerOfTwo(capacity); 48 | mask = this.capacity - 1; 49 | buffer = (E[]) new Object[this.capacity]; 50 | } 51 | 52 | public static int findNextPositivePowerOfTwo(final int value) { 53 | return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); 54 | } 55 | 56 | public boolean add(final E e) { 57 | if (offer(e)) { 58 | return true; 59 | } 60 | 61 | throw new IllegalStateException("Queue is full"); 62 | } 63 | 64 | public boolean offer(final E e) { 65 | if (null == e) { 66 | throw new NullPointerException("Null is not a valid element"); 67 | } 68 | 69 | final long currentTail = tail.get(); 70 | final long wrapPoint = currentTail - capacity; 71 | if (headCache <= wrapPoint) { 72 | headCache = head.get(); 73 | if (headCache <= wrapPoint) { 74 | return false; 75 | } 76 | } 77 | 78 | buffer[(int) currentTail & mask] = e; 79 | tail.lazySet(currentTail + 1); 80 | 81 | return true; 82 | } 83 | 84 | public E poll() { 85 | final long currentHead = head.get(); 86 | if (currentHead >= tailCache) { 87 | tailCache = tail.get(); 88 | if (currentHead >= tailCache) { 89 | return null; 90 | } 91 | } 92 | 93 | final int index = (int) currentHead & mask; 94 | final E e = buffer[index]; 95 | buffer[index] = null; 96 | head.lazySet(currentHead + 1); 97 | 98 | return e; 99 | } 100 | 101 | public E remove() { 102 | final E e = poll(); 103 | if (null == e) { 104 | throw new NoSuchElementException("Queue is empty"); 105 | } 106 | 107 | return e; 108 | } 109 | 110 | public E element() { 111 | final E e = peek(); 112 | if (null == e) { 113 | throw new NoSuchElementException("Queue is empty"); 114 | } 115 | 116 | return e; 117 | } 118 | 119 | public E peek() { 120 | return buffer[(int) head.get() & mask]; 121 | } 122 | 123 | public int size() { 124 | return (int) (tail.get() - head.get()); 125 | } 126 | 127 | public boolean isEmpty() { 128 | return tail.get() == head.get(); 129 | } 130 | 131 | public boolean contains(final Object o) { 132 | if (null == o) { 133 | return false; 134 | } 135 | 136 | for (long i = head.get(), limit = tail.get(); i < limit; i++) { 137 | final E e = buffer[(int) i & mask]; 138 | if (o.equals(e)) { 139 | return true; 140 | } 141 | } 142 | 143 | return false; 144 | } 145 | 146 | public Iterator iterator() { 147 | throw new UnsupportedOperationException(); 148 | } 149 | 150 | public Object[] toArray() { 151 | throw new UnsupportedOperationException(); 152 | } 153 | 154 | public T[] toArray(final T[] a) { 155 | throw new UnsupportedOperationException(); 156 | } 157 | 158 | public boolean remove(final Object o) { 159 | throw new UnsupportedOperationException(); 160 | } 161 | 162 | public boolean containsAll(final Collection c) { 163 | for (final Object o : c) { 164 | if (!contains(o)) { 165 | return false; 166 | } 167 | } 168 | 169 | return true; 170 | } 171 | 172 | public boolean addAll(final Collection c) { 173 | for (final E e : c) { 174 | add(e); 175 | } 176 | 177 | return true; 178 | } 179 | 180 | public boolean removeAll(final Collection c) { 181 | throw new UnsupportedOperationException(); 182 | } 183 | 184 | public boolean retainAll(final Collection c) { 185 | throw new UnsupportedOperationException(); 186 | } 187 | 188 | public void clear() { 189 | Object value; 190 | do { 191 | value = poll(); 192 | } while (null != value); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/java/uk/co/real_logic/queues/P1C1QueueOriginal23.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package uk.co.real_logic.queues; 17 | 18 | import java.util.Collection; 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | import java.util.Queue; 22 | import java.util.concurrent.atomic.AtomicLong; 23 | /** 24 | *
    25 | *
  • Lock free, observing single writer principal. 26 | *
  • Replacing the long fields with AtomicLong and using lazySet instead of 27 | * volatile assignment. 28 | *
  • Using the power of 2 mask, forcing the capacity to next power of 2. 29 | *
  • Adding head and tail cache fields. Avoiding redundant volatile reads. 30 | *
  • Padding head/tail cache fields. Avoiding false sharing. 31 | *
32 | */ 33 | public final class P1C1QueueOriginal23 implements Queue { 34 | private final int capacity; 35 | private final int mask; 36 | private final E[] buffer; 37 | 38 | private final AtomicLong tail = new AtomicLong(0); 39 | private final AtomicLong head = new AtomicLong(0); 40 | 41 | public static class PaddedLong { 42 | public long value = 0, p1, p2, p3, p4, p5, p6; 43 | } 44 | 45 | private final PaddedLong tailCache = new PaddedLong(); 46 | private final PaddedLong headCache = new PaddedLong(); 47 | 48 | @SuppressWarnings("unchecked") 49 | public P1C1QueueOriginal23(final int capacity) { 50 | this.capacity = findNextPositivePowerOfTwo(capacity); 51 | mask = this.capacity - 1; 52 | buffer = (E[]) new Object[this.capacity]; 53 | } 54 | 55 | public static int findNextPositivePowerOfTwo(final int value) { 56 | return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); 57 | } 58 | 59 | public boolean add(final E e) { 60 | if (offer(e)) { 61 | return true; 62 | } 63 | 64 | throw new IllegalStateException("Queue is full"); 65 | } 66 | 67 | public boolean offer(final E e) { 68 | if (null == e) { 69 | throw new NullPointerException("Null is not a valid element"); 70 | } 71 | 72 | final long currentTail = tail.get(); 73 | final long wrapPoint = currentTail - capacity; 74 | if (headCache.value <= wrapPoint) { 75 | headCache.value = head.get(); 76 | if (headCache.value <= wrapPoint) { 77 | return false; 78 | } 79 | } 80 | 81 | buffer[(int) currentTail & mask] = e; 82 | tail.lazySet(currentTail + 1); 83 | 84 | return true; 85 | } 86 | 87 | public E poll() { 88 | final long currentHead = head.get(); 89 | if (currentHead >= tailCache.value) { 90 | tailCache.value = tail.get(); 91 | if (currentHead >= tailCache.value) { 92 | return null; 93 | } 94 | } 95 | 96 | final int index = (int) currentHead & mask; 97 | final E e = buffer[index]; 98 | buffer[index] = null; 99 | head.lazySet(currentHead + 1); 100 | 101 | return e; 102 | } 103 | 104 | public E remove() { 105 | final E e = poll(); 106 | if (null == e) { 107 | throw new NoSuchElementException("Queue is empty"); 108 | } 109 | 110 | return e; 111 | } 112 | 113 | public E element() { 114 | final E e = peek(); 115 | if (null == e) { 116 | throw new NoSuchElementException("Queue is empty"); 117 | } 118 | 119 | return e; 120 | } 121 | 122 | public E peek() { 123 | return buffer[(int) head.get() & mask]; 124 | } 125 | 126 | public int size() { 127 | return (int) (tail.get() - head.get()); 128 | } 129 | 130 | public boolean isEmpty() { 131 | return tail.get() == head.get(); 132 | } 133 | 134 | public boolean contains(final Object o) { 135 | if (null == o) { 136 | return false; 137 | } 138 | 139 | for (long i = head.get(), limit = tail.get(); i < limit; i++) { 140 | final E e = buffer[(int) i & mask]; 141 | if (o.equals(e)) { 142 | return true; 143 | } 144 | } 145 | 146 | return false; 147 | } 148 | 149 | public Iterator iterator() { 150 | throw new UnsupportedOperationException(); 151 | } 152 | 153 | public Object[] toArray() { 154 | throw new UnsupportedOperationException(); 155 | } 156 | 157 | public T[] toArray(final T[] a) { 158 | throw new UnsupportedOperationException(); 159 | } 160 | 161 | public boolean remove(final Object o) { 162 | throw new UnsupportedOperationException(); 163 | } 164 | 165 | public boolean containsAll(final Collection c) { 166 | for (final Object o : c) { 167 | if (!contains(o)) { 168 | return false; 169 | } 170 | } 171 | 172 | return true; 173 | } 174 | 175 | public boolean addAll(final Collection c) { 176 | for (final E e : c) { 177 | add(e); 178 | } 179 | 180 | return true; 181 | } 182 | 183 | public boolean removeAll(final Collection c) { 184 | throw new UnsupportedOperationException(); 185 | } 186 | 187 | public boolean retainAll(final Collection c) { 188 | throw new UnsupportedOperationException(); 189 | } 190 | 191 | public void clear() { 192 | Object value; 193 | do { 194 | value = poll(); 195 | } while (null != value); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/java/uk/co/real_logic/queues/P1C1QueueOriginal3.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package uk.co.real_logic.queues; 17 | 18 | import java.util.Collection; 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | import java.util.Queue; 22 | import java.util.concurrent.atomic.AtomicLong; 23 | 24 | /** 25 | *
    26 | *
  • Lock free, observing single writer principal. 27 | *
  • Replacing the long fields with AtomicLong and using lazySet instead of 28 | * volatile assignment. 29 | *
  • Using the power of 2 mask, forcing the capacity to next power of 2. 30 | *
  • Adding head and tail cache fields. Avoiding redundant volatile reads. 31 | *
  • Padding head/tail AtomicLong fields. Avoiding false sharing. 32 | *
  • Padding head/tail cache fields. Avoiding false sharing. 33 | *
34 | */ 35 | public final class P1C1QueueOriginal3 implements Queue { 36 | private final int capacity; 37 | private final int mask; 38 | private final E[] buffer; 39 | 40 | private final AtomicLong tail = new PaddedAtomicLong(0); 41 | private final AtomicLong head = new PaddedAtomicLong(0); 42 | 43 | public static class PaddedLong { 44 | public long value = 0, p1, p2, p3, p4, p5, p6; 45 | } 46 | 47 | private final PaddedLong tailCache = new PaddedLong(); 48 | private final PaddedLong headCache = new PaddedLong(); 49 | 50 | @SuppressWarnings("unchecked") 51 | public P1C1QueueOriginal3(final int capacity) { 52 | this.capacity = findNextPositivePowerOfTwo(capacity); 53 | mask = this.capacity - 1; 54 | buffer = (E[]) new Object[this.capacity]; 55 | } 56 | 57 | public static int findNextPositivePowerOfTwo(final int value) { 58 | return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); 59 | } 60 | 61 | public boolean add(final E e) { 62 | if (offer(e)) { 63 | return true; 64 | } 65 | 66 | throw new IllegalStateException("Queue is full"); 67 | } 68 | 69 | public boolean offer(final E e) { 70 | if (null == e) { 71 | throw new NullPointerException("Null is not a valid element"); 72 | } 73 | 74 | final long currentTail = tail.get(); 75 | final long wrapPoint = currentTail - capacity; 76 | if (headCache.value <= wrapPoint) { 77 | headCache.value = head.get(); 78 | if (headCache.value <= wrapPoint) { 79 | return false; 80 | } 81 | } 82 | 83 | buffer[(int) currentTail & mask] = e; 84 | tail.lazySet(currentTail + 1); 85 | 86 | return true; 87 | } 88 | 89 | public E poll() { 90 | final long currentHead = head.get(); 91 | if (currentHead >= tailCache.value) { 92 | tailCache.value = tail.get(); 93 | if (currentHead >= tailCache.value) { 94 | return null; 95 | } 96 | } 97 | 98 | final int index = (int) currentHead & mask; 99 | final E e = buffer[index]; 100 | buffer[index] = null; 101 | head.lazySet(currentHead + 1); 102 | 103 | return e; 104 | } 105 | 106 | public E remove() { 107 | final E e = poll(); 108 | if (null == e) { 109 | throw new NoSuchElementException("Queue is empty"); 110 | } 111 | 112 | return e; 113 | } 114 | 115 | public E element() { 116 | final E e = peek(); 117 | if (null == e) { 118 | throw new NoSuchElementException("Queue is empty"); 119 | } 120 | 121 | return e; 122 | } 123 | 124 | public E peek() { 125 | return buffer[(int) head.get() & mask]; 126 | } 127 | 128 | public int size() { 129 | return (int) (tail.get() - head.get()); 130 | } 131 | 132 | public boolean isEmpty() { 133 | return tail.get() == head.get(); 134 | } 135 | 136 | public boolean contains(final Object o) { 137 | if (null == o) { 138 | return false; 139 | } 140 | 141 | for (long i = head.get(), limit = tail.get(); i < limit; i++) { 142 | final E e = buffer[(int) i & mask]; 143 | if (o.equals(e)) { 144 | return true; 145 | } 146 | } 147 | 148 | return false; 149 | } 150 | 151 | public Iterator iterator() { 152 | throw new UnsupportedOperationException(); 153 | } 154 | 155 | public Object[] toArray() { 156 | throw new UnsupportedOperationException(); 157 | } 158 | 159 | public T[] toArray(final T[] a) { 160 | throw new UnsupportedOperationException(); 161 | } 162 | 163 | public boolean remove(final Object o) { 164 | throw new UnsupportedOperationException(); 165 | } 166 | 167 | public boolean containsAll(final Collection c) { 168 | for (final Object o : c) { 169 | if (!contains(o)) { 170 | return false; 171 | } 172 | } 173 | 174 | return true; 175 | } 176 | 177 | public boolean addAll(final Collection c) { 178 | for (final E e : c) { 179 | add(e); 180 | } 181 | 182 | return true; 183 | } 184 | 185 | public boolean removeAll(final Collection c) { 186 | throw new UnsupportedOperationException(); 187 | } 188 | 189 | public boolean retainAll(final Collection c) { 190 | throw new UnsupportedOperationException(); 191 | } 192 | 193 | public void clear() { 194 | Object value; 195 | do { 196 | value = poll(); 197 | } while (null != value); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/java/uk/co/real_logic/queues/P1C1QueueOriginalPrimitive.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package uk.co.real_logic.queues; 17 | 18 | import java.util.Collection; 19 | import java.util.Iterator; 20 | import java.util.NoSuchElementException; 21 | import java.util.Queue; 22 | import java.util.concurrent.atomic.AtomicLong; 23 | 24 | /** 25 | *
    26 | *
  • Lock free, observing single writer principal. 27 | *
  • Replacing the long fields with AtomicLong and using lazySet instead of 28 | * volatile assignment. 29 | *
  • Using the power of 2 mask, forcing the capacity to next power of 2. 30 | *
  • Adding head and tail cache fields. Avoiding redundant volatile reads. 31 | *
  • Padding head/tail AtomicLong fields. Avoiding false sharing. 32 | *
  • Padding head/tail cache fields. Avoiding false sharing. 33 | *
  • Inline integer value rather than use an object. 34 | *
35 | */ 36 | public final class P1C1QueueOriginalPrimitive implements Queue { 37 | private final int capacity; 38 | private final int mask; 39 | private final int[] buffer; 40 | 41 | private final AtomicLong tail = new PaddedAtomicLong(0); 42 | private final AtomicLong head = new PaddedAtomicLong(0); 43 | 44 | public static class PaddedLong { 45 | public long value = 0, p1, p2, p3, p4, p5, p6; 46 | } 47 | 48 | private final PaddedLong tailCache = new PaddedLong(); 49 | private final PaddedLong headCache = new PaddedLong(); 50 | 51 | @SuppressWarnings("unchecked") 52 | public P1C1QueueOriginalPrimitive(final int capacity) { 53 | this.capacity = findNextPositivePowerOfTwo(capacity); 54 | mask = this.capacity - 1; 55 | buffer = new int[this.capacity]; 56 | } 57 | 58 | public static int findNextPositivePowerOfTwo(final int value) { 59 | return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); 60 | } 61 | 62 | public boolean add(final Integer e) { 63 | if (offer(e)) { 64 | return true; 65 | } 66 | 67 | throw new IllegalStateException("Queue is full"); 68 | } 69 | 70 | public boolean offer(final Integer e) { 71 | if (null == e) { 72 | throw new NullPointerException("Null is not a valid element"); 73 | } 74 | 75 | final long currentTail = tail.get(); 76 | final long wrapPoint = currentTail - capacity; 77 | if (headCache.value <= wrapPoint) { 78 | headCache.value = head.get(); 79 | if (headCache.value <= wrapPoint) { 80 | return false; 81 | } 82 | } 83 | 84 | buffer[(int) currentTail & mask] = e; 85 | tail.lazySet(currentTail + 1); 86 | 87 | return true; 88 | } 89 | 90 | public Integer poll() { 91 | final long currentHead = head.get(); 92 | if (currentHead >= tailCache.value) { 93 | tailCache.value = tail.get(); 94 | if (currentHead >= tailCache.value) { 95 | return null; 96 | } 97 | } 98 | 99 | final int index = (int) currentHead & mask; 100 | final Integer e = buffer[index]; 101 | head.lazySet(currentHead + 1); 102 | 103 | return e; 104 | } 105 | 106 | public Integer remove() { 107 | final Integer e = poll(); 108 | if (null == e) { 109 | throw new NoSuchElementException("Queue is empty"); 110 | } 111 | 112 | return e; 113 | } 114 | 115 | public Integer element() { 116 | final Integer e = peek(); 117 | if (null == e) { 118 | throw new NoSuchElementException("Queue is empty"); 119 | } 120 | 121 | return e; 122 | } 123 | 124 | public Integer peek() { 125 | return buffer[(int) head.get() & mask]; 126 | } 127 | 128 | public int size() { 129 | return (int) (tail.get() - head.get()); 130 | } 131 | 132 | public boolean isEmpty() { 133 | return tail.get() == head.get(); 134 | } 135 | 136 | public boolean contains(final Object o) { 137 | if (null == o) { 138 | return false; 139 | } 140 | 141 | for (long i = head.get(), limit = tail.get(); i < limit; i++) { 142 | final Integer e = buffer[(int) i & mask]; 143 | if (o.equals(e)) { 144 | return true; 145 | } 146 | } 147 | 148 | return false; 149 | } 150 | 151 | public Iterator iterator() { 152 | throw new UnsupportedOperationException(); 153 | } 154 | 155 | public Object[] toArray() { 156 | throw new UnsupportedOperationException(); 157 | } 158 | 159 | public T[] toArray(final T[] a) { 160 | throw new UnsupportedOperationException(); 161 | } 162 | 163 | public boolean remove(final Object o) { 164 | throw new UnsupportedOperationException(); 165 | } 166 | 167 | public boolean containsAll(final Collection c) { 168 | for (final Object o : c) { 169 | if (!contains(o)) { 170 | return false; 171 | } 172 | } 173 | 174 | return true; 175 | } 176 | 177 | public boolean addAll(final Collection c) { 178 | for (final Integer e : c) { 179 | add(e); 180 | } 181 | 182 | return true; 183 | } 184 | 185 | public boolean removeAll(final Collection c) { 186 | throw new UnsupportedOperationException(); 187 | } 188 | 189 | public boolean retainAll(final Collection c) { 190 | throw new UnsupportedOperationException(); 191 | } 192 | 193 | public void clear() { 194 | Object value; 195 | do { 196 | value = poll(); 197 | } while (null != value); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/java/uk/co/real_logic/queues/PaddedAtomicLong.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package uk.co.real_logic.queues; 17 | 18 | import java.util.concurrent.atomic.AtomicLong; 19 | 20 | public class PaddedAtomicLong extends AtomicLong { 21 | public PaddedAtomicLong() { 22 | } 23 | 24 | public PaddedAtomicLong(final long initialValue) { 25 | super(initialValue); 26 | } 27 | 28 | public volatile long p1, p2, p3, p4, p5, p6 = 7; 29 | } 30 | -------------------------------------------------------------------------------- /src/java/uk/co/real_logic/queues/QueuePerfTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Real Logic Ltd. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package uk.co.real_logic.queues; 17 | 18 | import java.util.Queue; 19 | import java.util.concurrent.ArrayBlockingQueue; 20 | 21 | import psy.lob.saw.queues.P1C1OffHeapQueue; 22 | import psy.lob.saw.queues.P1C1Queue2CacheLinesHeapBuffer; 23 | import psy.lob.saw.queues.P1C1Queue4CacheLinesHeapBuffer; 24 | import psy.lob.saw.queues.P1C1Queue4CacheLinesHeapBufferUnsafe; 25 | 26 | public class QueuePerfTest { 27 | // 15 == 32* 1024 28 | public static final int QUEUE_CAPACITY = 1 << Integer.getInteger("scale", 29 | 15); 30 | public static final int REPETITIONS = Integer.getInteger("reps", 50) * 1000 * 1000; 31 | public static final Integer TEST_VALUE = Integer.valueOf(777); 32 | 33 | public static void main(final String[] args) throws Exception { 34 | System.out.println("capacity:" + QUEUE_CAPACITY + " reps:" 35 | + REPETITIONS); 36 | final Queue queue = createQueue(args[0]); 37 | 38 | for (int i = 0; i < 20; i++) { 39 | System.gc(); 40 | performanceRun(i, queue); 41 | } 42 | } 43 | 44 | private static Queue createQueue(final String option) { 45 | switch (Integer.parseInt(option)) { 46 | case 0: 47 | return new ArrayBlockingQueue(QUEUE_CAPACITY); 48 | case 1: 49 | return new P1C1QueueOriginal1(QUEUE_CAPACITY); 50 | case 12: 51 | return new P1C1QueueOriginal12(QUEUE_CAPACITY); 52 | case 2: 53 | return new P1C1QueueOriginal2(QUEUE_CAPACITY); 54 | case 21: 55 | return new P1C1QueueOriginal21(QUEUE_CAPACITY); 56 | case 22: 57 | return new P1C1QueueOriginal22(QUEUE_CAPACITY); 58 | case 23: 59 | return new P1C1QueueOriginal23(QUEUE_CAPACITY); 60 | case 3: 61 | return new P1C1QueueOriginal3(QUEUE_CAPACITY); 62 | case 4: 63 | return new P1C1Queue2CacheLinesHeapBuffer(QUEUE_CAPACITY); 64 | case 5: 65 | return new P1C1Queue4CacheLinesHeapBuffer(QUEUE_CAPACITY); 66 | case 6: 67 | return new P1C1Queue4CacheLinesHeapBufferUnsafe( 68 | QUEUE_CAPACITY); 69 | case 7: 70 | return new P1C1OffHeapQueue(QUEUE_CAPACITY); 71 | case 8: 72 | return new P1C1QueueOriginalPrimitive(QUEUE_CAPACITY); 73 | 74 | default: 75 | throw new IllegalArgumentException("Invalid option: " + option); 76 | } 77 | } 78 | 79 | private static void performanceRun(final int runNumber, 80 | final Queue queue) throws Exception { 81 | final long start = System.nanoTime(); 82 | final Thread thread = new Thread(new Producer(queue)); 83 | thread.start(); 84 | 85 | Integer result; 86 | int i = REPETITIONS; 87 | do { 88 | while (null == (result = queue.poll())) { 89 | Thread.yield(); 90 | } 91 | } while (0 != --i); 92 | 93 | thread.join(); 94 | 95 | final long duration = System.nanoTime() - start; 96 | final long ops = (REPETITIONS * 1000L * 1000L * 1000L) / duration; 97 | System.out.format("%d - ops/sec=%,d - %s result=%d\n", Integer 98 | .valueOf(runNumber), Long.valueOf(ops), queue.getClass() 99 | .getSimpleName(), result); 100 | } 101 | 102 | public static class Producer implements Runnable { 103 | private final Queue queue; 104 | 105 | public Producer(final Queue queue) { 106 | this.queue = queue; 107 | } 108 | 109 | public void run() { 110 | int i = REPETITIONS; 111 | do { 112 | while (!queue.offer(TEST_VALUE)) { 113 | Thread.yield(); 114 | } 115 | } while (0 != --i); 116 | } 117 | } 118 | } 119 | --------------------------------------------------------------------------------