From 162e59848ad1801ab26e501c3c93ee08e83f5065 Mon Sep 17 00:00:00 2001 From: Francesco Nigro Date: Wed, 9 Sep 2020 16:10:26 +0200 Subject: [PATCH] Improve predictability of writeUtf8/writeAscii performance (#10368) Motivation: writeUtf8 can suffer from inlining issues and/or megamorphic call-sites on the hot path due to ByteBuf hierarchy Modifications: Duplicate and specialize the code paths to reduce the need of polymorphic calls Result: Performance are more stable in user code --- .../java/io/netty/buffer/AbstractByteBuf.java | 2 +- .../java/io/netty/buffer/ByteBufUtil.java | 265 +++- .../java/io/netty/buffer/ByteBufUtilTest.java | 197 ++- .../util/internal/PlatformDependent.java | 13 + .../util/internal/PlatformDependent0.java | 4 + .../buffer/ByteBufUtilBenchmark.java | 19 +- .../buffer/Utf8EncodingBenchmark.java | 277 ++++ microbench/src/main/resources/Utf8Samples.txt | 1399 +++++++++++++++++ 8 files changed, 2044 insertions(+), 132 deletions(-) create mode 100644 microbench/src/main/java/io/netty/microbench/buffer/Utf8EncodingBenchmark.java create mode 100644 microbench/src/main/resources/Utf8Samples.txt diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java index 9ce8ccc5a3..62259e2798 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java @@ -705,7 +705,7 @@ public abstract class AbstractByteBuf extends ByteBuf { } else { checkIndex(index, length); } - return ByteBufUtil.writeUtf8(this, index, sequence, sequence.length()); + return ByteBufUtil.writeUtf8(this, index, length, sequence, sequence.length()); } if (charset.equals(CharsetUtil.US_ASCII) || charset.equals(CharsetUtil.ISO_8859_1)) { int length = sequence.length(); diff --git a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java index f8f72878df..78c3908e22 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java @@ -570,7 +570,7 @@ public final class ByteBufUtil { } else if (buf instanceof AbstractByteBuf) { AbstractByteBuf byteBuf = (AbstractByteBuf) buf; byteBuf.ensureWritable0(reserveBytes); - int written = writeUtf8(byteBuf, byteBuf.writerIndex, seq, start, end); + int written = writeUtf8(byteBuf, byteBuf.writerIndex, reserveBytes, seq, start, end); byteBuf.writerIndex += written; return written; } else if (buf instanceof WrappedByteBuf) { @@ -584,12 +584,111 @@ public final class ByteBufUtil { } } - static int writeUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int len) { - return writeUtf8(buffer, writerIndex, seq, 0, len); + static int writeUtf8(AbstractByteBuf buffer, int writerIndex, int reservedBytes, CharSequence seq, int len) { + return writeUtf8(buffer, writerIndex, reservedBytes, seq, 0, len); } // Fast-Path implementation - static int writeUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int start, int end) { + static int writeUtf8(AbstractByteBuf buffer, int writerIndex, int reservedBytes, + CharSequence seq, int start, int end) { + if (seq instanceof AsciiString) { + writeAsciiString(buffer, writerIndex, (AsciiString) seq, start, end); + return end - start; + } + if (PlatformDependent.hasUnsafe()) { + if (buffer.hasArray()) { + return unsafeWriteUtf8(buffer.array(), PlatformDependent.byteArrayBaseOffset(), + buffer.arrayOffset() + writerIndex, seq, start, end); + } + if (buffer.hasMemoryAddress()) { + return unsafeWriteUtf8(null, buffer.memoryAddress(), writerIndex, seq, start, end); + } + } else { + if (buffer.hasArray()) { + return safeArrayWriteUtf8(buffer.array(), buffer.arrayOffset() + writerIndex, seq, start, end); + } + if (buffer.isDirect()) { + assert buffer.nioBufferCount() == 1; + final ByteBuffer internalDirectBuffer = buffer.internalNioBuffer(writerIndex, reservedBytes); + final int bufferPosition = internalDirectBuffer.position(); + return safeDirectWriteUtf8(internalDirectBuffer, bufferPosition, seq, start, end); + } + } + return safeWriteUtf8(buffer, writerIndex, seq, start, end); + } + + // AsciiString Fast-Path implementation - no explicit bound-checks + static void writeAsciiString(AbstractByteBuf buffer, int writerIndex, AsciiString seq, int start, int end) { + final int begin = seq.arrayOffset() + start; + final int length = end - start; + if (PlatformDependent.hasUnsafe()) { + if (buffer.hasArray()) { + PlatformDependent.copyMemory(seq.array(), begin, + buffer.array(), buffer.arrayOffset() + writerIndex, length); + return; + } + if (buffer.hasMemoryAddress()) { + PlatformDependent.copyMemory(seq.array(), begin, buffer.memoryAddress() + writerIndex, length); + return; + } + } + if (buffer.hasArray()) { + System.arraycopy(seq.array(), begin, buffer.array(), buffer.arrayOffset() + writerIndex, length); + return; + } + buffer.setBytes(writerIndex, seq.array(), begin, length); + } + + // Safe off-heap Fast-Path implementation + private static int safeDirectWriteUtf8(ByteBuffer buffer, int writerIndex, CharSequence seq, int start, int end) { + assert !(seq instanceof AsciiString); + int oldWriterIndex = writerIndex; + + // We can use the _set methods as these not need to do any index checks and reference checks. + // This is possible as we called ensureWritable(...) before. + for (int i = start; i < end; i++) { + char c = seq.charAt(i); + if (c < 0x80) { + buffer.put(writerIndex++, (byte) c); + } else if (c < 0x800) { + buffer.put(writerIndex++, (byte) (0xc0 | (c >> 6))); + buffer.put(writerIndex++, (byte) (0x80 | (c & 0x3f))); + } else if (isSurrogate(c)) { + if (!Character.isHighSurrogate(c)) { + buffer.put(writerIndex++, WRITE_UTF_UNKNOWN); + continue; + } + // Surrogate Pair consumes 2 characters. + if (++i == end) { + buffer.put(writerIndex++, WRITE_UTF_UNKNOWN); + break; + } + // Extra method is copied here to NOT allow inlining of writeUtf8 + // and increase the chance to inline CharSequence::charAt instead + char c2 = seq.charAt(i); + if (!Character.isLowSurrogate(c2)) { + buffer.put(writerIndex++, WRITE_UTF_UNKNOWN); + buffer.put(writerIndex++, Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : (byte) c2); + } else { + int codePoint = Character.toCodePoint(c, c2); + // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. + buffer.put(writerIndex++, (byte) (0xf0 | (codePoint >> 18))); + buffer.put(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f))); + buffer.put(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f))); + buffer.put(writerIndex++, (byte) (0x80 | (codePoint & 0x3f))); + } + } else { + buffer.put(writerIndex++, (byte) (0xe0 | (c >> 12))); + buffer.put(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f))); + buffer.put(writerIndex++, (byte) (0x80 | (c & 0x3f))); + } + } + return writerIndex - oldWriterIndex; + } + + // Safe off-heap Fast-Path implementation + private static int safeWriteUtf8(AbstractByteBuf buffer, int writerIndex, CharSequence seq, int start, int end) { + assert !(seq instanceof AsciiString); int oldWriterIndex = writerIndex; // We can use the _set methods as these not need to do any index checks and reference checks. @@ -611,8 +710,20 @@ public final class ByteBufUtil { buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); break; } - // Extra method to allow inlining the rest of writeUtf8 which is the most likely code path. - writerIndex = writeUtf8Surrogate(buffer, writerIndex, c, seq.charAt(i)); + // Extra method is copied here to NOT allow inlining of writeUtf8 + // and increase the chance to inline CharSequence::charAt instead + char c2 = seq.charAt(i); + if (!Character.isLowSurrogate(c2)) { + buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); + buffer._setByte(writerIndex++, Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : c2); + } else { + int codePoint = Character.toCodePoint(c, c2); + // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. + buffer._setByte(writerIndex++, (byte) (0xf0 | (codePoint >> 18))); + buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f))); + buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f))); + buffer._setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f))); + } } else { buffer._setByte(writerIndex++, (byte) (0xe0 | (c >> 12))); buffer._setByte(writerIndex++, (byte) (0x80 | ((c >> 6) & 0x3f))); @@ -622,19 +733,94 @@ public final class ByteBufUtil { return writerIndex - oldWriterIndex; } - private static int writeUtf8Surrogate(AbstractByteBuf buffer, int writerIndex, char c, char c2) { - if (!Character.isLowSurrogate(c2)) { - buffer._setByte(writerIndex++, WRITE_UTF_UNKNOWN); - buffer._setByte(writerIndex++, Character.isHighSurrogate(c2) ? WRITE_UTF_UNKNOWN : c2); - return writerIndex; + // safe byte[] Fast-Path implementation + private static int safeArrayWriteUtf8(byte[] buffer, int writerIndex, CharSequence seq, int start, int end) { + int oldWriterIndex = writerIndex; + for (int i = start; i < end; i++) { + char c = seq.charAt(i); + if (c < 0x80) { + buffer[writerIndex++] = (byte) c; + } else if (c < 0x800) { + buffer[writerIndex++] = (byte) (0xc0 | (c >> 6)); + buffer[writerIndex++] = (byte) (0x80 | (c & 0x3f)); + } else if (isSurrogate(c)) { + if (!Character.isHighSurrogate(c)) { + buffer[writerIndex++] = WRITE_UTF_UNKNOWN; + continue; + } + // Surrogate Pair consumes 2 characters. + if (++i == end) { + buffer[writerIndex++] = WRITE_UTF_UNKNOWN; + break; + } + char c2 = seq.charAt(i); + // Extra method is copied here to NOT allow inlining of writeUtf8 + // and increase the chance to inline CharSequence::charAt instead + if (!Character.isLowSurrogate(c2)) { + buffer[writerIndex++] = WRITE_UTF_UNKNOWN; + buffer[writerIndex++] = (byte) (Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : c2); + } else { + int codePoint = Character.toCodePoint(c, c2); + // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. + buffer[writerIndex++] = (byte) (0xf0 | (codePoint >> 18)); + buffer[writerIndex++] = (byte) (0x80 | ((codePoint >> 12) & 0x3f)); + buffer[writerIndex++] = (byte) (0x80 | ((codePoint >> 6) & 0x3f)); + buffer[writerIndex++] = (byte) (0x80 | (codePoint & 0x3f)); + } + } else { + buffer[writerIndex++] = (byte) (0xe0 | (c >> 12)); + buffer[writerIndex++] = (byte) (0x80 | ((c >> 6) & 0x3f)); + buffer[writerIndex++] = (byte) (0x80 | (c & 0x3f)); + } } - int codePoint = Character.toCodePoint(c, c2); - // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. - buffer._setByte(writerIndex++, (byte) (0xf0 | (codePoint >> 18))); - buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 12) & 0x3f))); - buffer._setByte(writerIndex++, (byte) (0x80 | ((codePoint >> 6) & 0x3f))); - buffer._setByte(writerIndex++, (byte) (0x80 | (codePoint & 0x3f))); - return writerIndex; + return writerIndex - oldWriterIndex; + } + + // unsafe Fast-Path implementation + private static int unsafeWriteUtf8(byte[] buffer, long memoryOffset, int writerIndex, + CharSequence seq, int start, int end) { + assert !(seq instanceof AsciiString); + long writerOffset = memoryOffset + writerIndex; + final long oldWriterOffset = writerOffset; + for (int i = start; i < end; i++) { + char c = seq.charAt(i); + if (c < 0x80) { + PlatformDependent.putByte(buffer, writerOffset++, (byte) c); + } else if (c < 0x800) { + PlatformDependent.putByte(buffer, writerOffset++, (byte) (0xc0 | (c >> 6))); + PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | (c & 0x3f))); + } else if (isSurrogate(c)) { + if (!Character.isHighSurrogate(c)) { + PlatformDependent.putByte(buffer, writerOffset++, WRITE_UTF_UNKNOWN); + continue; + } + // Surrogate Pair consumes 2 characters. + if (++i == end) { + PlatformDependent.putByte(buffer, writerOffset++, WRITE_UTF_UNKNOWN); + break; + } + char c2 = seq.charAt(i); + // Extra method is copied here to NOT allow inlining of writeUtf8 + // and increase the chance to inline CharSequence::charAt instead + if (!Character.isLowSurrogate(c2)) { + PlatformDependent.putByte(buffer, writerOffset++, WRITE_UTF_UNKNOWN); + PlatformDependent.putByte(buffer, writerOffset++, + (byte) (Character.isHighSurrogate(c2)? WRITE_UTF_UNKNOWN : c2)); + } else { + int codePoint = Character.toCodePoint(c, c2); + // See http://www.unicode.org/versions/Unicode7.0.0/ch03.pdf#G2630. + PlatformDependent.putByte(buffer, writerOffset++, (byte) (0xf0 | (codePoint >> 18))); + PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | ((codePoint >> 12) & 0x3f))); + PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | ((codePoint >> 6) & 0x3f))); + PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | (codePoint & 0x3f))); + } + } else { + PlatformDependent.putByte(buffer, writerOffset++, (byte) (0xe0 | (c >> 12))); + PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | ((c >> 6) & 0x3f))); + PlatformDependent.putByte(buffer, writerOffset++, (byte) (0x80 | (c & 0x3f))); + } + } + return (int) (writerOffset - oldWriterOffset); } /** @@ -742,32 +928,31 @@ public final class ByteBufUtil { */ public static int writeAscii(ByteBuf buf, CharSequence seq) { // ASCII uses 1 byte per char - final int len = seq.length(); - if (seq instanceof AsciiString) { - AsciiString asciiString = (AsciiString) seq; - buf.writeBytes(asciiString.array(), asciiString.arrayOffset(), len); - } else { - for (;;) { - if (buf instanceof WrappedCompositeByteBuf) { - // WrappedCompositeByteBuf is a sub-class of AbstractByteBuf so it needs special handling. - buf = buf.unwrap(); - } else if (buf instanceof AbstractByteBuf) { - AbstractByteBuf byteBuf = (AbstractByteBuf) buf; - byteBuf.ensureWritable0(len); - int written = writeAscii(byteBuf, byteBuf.writerIndex, seq, len); - byteBuf.writerIndex += written; - return written; - } else if (buf instanceof WrappedByteBuf) { - // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path. - buf = buf.unwrap(); + for (;;) { + if (buf instanceof WrappedCompositeByteBuf) { + // WrappedCompositeByteBuf is a sub-class of AbstractByteBuf so it needs special handling. + buf = buf.unwrap(); + } else if (buf instanceof AbstractByteBuf) { + final int len = seq.length(); + AbstractByteBuf byteBuf = (AbstractByteBuf) buf; + byteBuf.ensureWritable0(len); + if (seq instanceof AsciiString) { + writeAsciiString(byteBuf, byteBuf.writerIndex, (AsciiString) seq, 0, len); } else { - byte[] bytes = seq.toString().getBytes(CharsetUtil.US_ASCII); - buf.writeBytes(bytes); - return bytes.length; + final int written = writeAscii(byteBuf, byteBuf.writerIndex, seq, len); + assert written == len; } + byteBuf.writerIndex += len; + return len; + } else if (buf instanceof WrappedByteBuf) { + // Unwrap as the wrapped buffer may be an AbstractByteBuf and so we can use fast-path. + buf = buf.unwrap(); + } else { + byte[] bytes = seq.toString().getBytes(CharsetUtil.US_ASCII); + buf.writeBytes(bytes); + return bytes.length; } } - return len; } // Fast-Path implementation diff --git a/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java b/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java index db627ec435..ccf5a5aae8 100644 --- a/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java +++ b/buffer/src/test/java/io/netty/buffer/ByteBufUtilTest.java @@ -18,11 +18,14 @@ package io.netty.buffer; import io.netty.util.AsciiString; import io.netty.util.CharsetUtil; import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; import java.nio.ByteOrder; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; @@ -31,16 +34,48 @@ import java.util.concurrent.atomic.AtomicReference; import static io.netty.buffer.Unpooled.unreleasableBuffer; import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeFalse; +import static org.junit.Assert.*; import static org.junit.Assume.assumeThat; -import static org.junit.Assume.assumeTrue; +@RunWith(Parameterized.class) public class ByteBufUtilTest { + + private enum BufferType { + DIRECT_UNPOOLED, DIRECT_POOLED, HEAP_POOLED, HEAP_UNPOOLED + } + + private final BufferType bufferType; + + public ByteBufUtilTest(BufferType bufferType) { + this.bufferType = bufferType; + } + + private ByteBuf buffer(int capacity) { + switch (bufferType) { + + case DIRECT_UNPOOLED: + return Unpooled.directBuffer(capacity); + case HEAP_UNPOOLED: + return Unpooled.buffer(capacity); + case DIRECT_POOLED: + return PooledByteBufAllocator.DEFAULT.directBuffer(capacity); + case HEAP_POOLED: + return PooledByteBufAllocator.DEFAULT.buffer(capacity); + default: + throw new AssertionError("unexpected buffer type: " + bufferType); + } + } + + @Parameterized.Parameters(name = "bufferType = {0}") + public static Collection noUnsafe() { + return Arrays.asList(new Object[][] { + { BufferType.DIRECT_POOLED }, + { BufferType.DIRECT_UNPOOLED }, + { BufferType.HEAP_POOLED }, + { BufferType.HEAP_UNPOOLED } + }); + } + @Test public void decodeRandomHexBytesWithEvenLength() { decodeRandomHexBytes(256); @@ -144,14 +179,14 @@ public class ByteBufUtilTest { public void writeShortBE() { int expected = 0x1234; - ByteBuf buf = Unpooled.buffer(2).order(ByteOrder.BIG_ENDIAN); + ByteBuf buf = buffer(2).order(ByteOrder.BIG_ENDIAN); ByteBufUtil.writeShortBE(buf, expected); assertEquals(expected, buf.readShort()); buf.resetReaderIndex(); assertEquals(ByteBufUtil.swapShort((short) expected), buf.readShortLE()); buf.release(); - buf = Unpooled.buffer(2).order(ByteOrder.LITTLE_ENDIAN); + buf = buffer(2).order(ByteOrder.LITTLE_ENDIAN); ByteBufUtil.writeShortBE(buf, expected); assertEquals((short) expected, buf.readShortLE()); buf.resetReaderIndex(); @@ -184,14 +219,14 @@ public class ByteBufUtilTest { public void writeMediumBE() { int mediumValue = 0x123456; - ByteBuf buf = Unpooled.buffer(4).order(ByteOrder.BIG_ENDIAN); + ByteBuf buf = buffer(4).order(ByteOrder.BIG_ENDIAN); ByteBufUtil.writeMediumBE(buf, mediumValue); assertEquals(mediumValue, buf.readMedium()); buf.resetReaderIndex(); assertEquals(ByteBufUtil.swapMedium(mediumValue), buf.readMediumLE()); buf.release(); - buf = Unpooled.buffer(4).order(ByteOrder.LITTLE_ENDIAN); + buf = buffer(4).order(ByteOrder.LITTLE_ENDIAN); ByteBufUtil.writeMediumBE(buf, mediumValue); assertEquals(mediumValue, buf.readMediumLE()); buf.resetReaderIndex(); @@ -202,9 +237,9 @@ public class ByteBufUtilTest { @Test public void testWriteUsAscii() { String usAscii = "NettyRocks"; - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); ByteBufUtil.writeAscii(buf2, usAscii); assertEquals(buf, buf2); @@ -216,9 +251,9 @@ public class ByteBufUtilTest { @Test public void testWriteUsAsciiSwapped() { String usAscii = "NettyRocks"; - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII)); - SwappedByteBuf buf2 = new SwappedByteBuf(Unpooled.buffer(16)); + SwappedByteBuf buf2 = new SwappedByteBuf(buffer(16)); ByteBufUtil.writeAscii(buf2, usAscii); assertEquals(buf, buf2); @@ -230,10 +265,10 @@ public class ByteBufUtilTest { @Test public void testWriteUsAsciiWrapped() { String usAscii = "NettyRocks"; - ByteBuf buf = unreleasableBuffer(Unpooled.buffer(16)); + ByteBuf buf = unreleasableBuffer(buffer(16)); assertWrapped(buf); buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII)); - ByteBuf buf2 = unreleasableBuffer(Unpooled.buffer(16)); + ByteBuf buf2 = unreleasableBuffer(buffer(16)); assertWrapped(buf2); ByteBufUtil.writeAscii(buf2, usAscii); @@ -246,10 +281,10 @@ public class ByteBufUtilTest { @Test public void testWriteUsAsciiComposite() { String usAscii = "NettyRocks"; - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII)); ByteBuf buf2 = Unpooled.compositeBuffer().addComponent( - Unpooled.buffer(8)).addComponent(Unpooled.buffer(24)); + buffer(8)).addComponent(buffer(24)); // write some byte so we start writing with an offset. buf2.writeByte(1); ByteBufUtil.writeAscii(buf2, usAscii); @@ -264,10 +299,10 @@ public class ByteBufUtilTest { @Test public void testWriteUsAsciiCompositeWrapped() { String usAscii = "NettyRocks"; - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(usAscii.getBytes(CharsetUtil.US_ASCII)); ByteBuf buf2 = new WrappedCompositeByteBuf(Unpooled.compositeBuffer().addComponent( - Unpooled.buffer(8)).addComponent(Unpooled.buffer(24))); + buffer(8)).addComponent(buffer(24))); // write some byte so we start writing with an offset. buf2.writeByte(1); ByteBufUtil.writeAscii(buf2, usAscii); @@ -282,9 +317,9 @@ public class ByteBufUtilTest { @Test public void testWriteUtf8() { String usAscii = "Some UTF-8 like äÄ∏ŒŒ"; - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(usAscii.getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); ByteBufUtil.writeUtf8(buf2, usAscii); assertEquals(buf, buf2); @@ -296,10 +331,10 @@ public class ByteBufUtilTest { @Test public void testWriteUtf8Composite() { String utf8 = "Some UTF-8 like äÄ∏ŒŒ"; - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(utf8.getBytes(CharsetUtil.UTF_8)); ByteBuf buf2 = Unpooled.compositeBuffer().addComponent( - Unpooled.buffer(8)).addComponent(Unpooled.buffer(24)); + buffer(8)).addComponent(buffer(24)); // write some byte so we start writing with an offset. buf2.writeByte(1); ByteBufUtil.writeUtf8(buf2, utf8); @@ -314,10 +349,10 @@ public class ByteBufUtilTest { @Test public void testWriteUtf8CompositeWrapped() { String utf8 = "Some UTF-8 like äÄ∏ŒŒ"; - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(utf8.getBytes(CharsetUtil.UTF_8)); ByteBuf buf2 = new WrappedCompositeByteBuf(Unpooled.compositeBuffer().addComponent( - Unpooled.buffer(8)).addComponent(Unpooled.buffer(24))); + buffer(8)).addComponent(buffer(24))); // write some byte so we start writing with an offset. buf2.writeByte(1); ByteBufUtil.writeUtf8(buf2, utf8); @@ -338,9 +373,9 @@ public class ByteBufUtilTest { .append('\uDC00') .append('b') .toString(); - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); ByteBufUtil.writeUtf8(buf2, surrogateString); assertEquals(buf, buf2); @@ -357,9 +392,9 @@ public class ByteBufUtilTest { .append('\uDC00') .append('b') .toString(); - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); ByteBufUtil.writeUtf8(buf2, surrogateString); assertEquals(buf, buf2); @@ -376,9 +411,9 @@ public class ByteBufUtilTest { .append('\uD800') .append('b') .toString(); - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); ByteBufUtil.writeUtf8(buf2, surrogateString); assertEquals(buf, buf2); @@ -396,9 +431,9 @@ public class ByteBufUtilTest { .append('\uD800') .append('b') .toString(); - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); ByteBufUtil.writeUtf8(buf2, surrogateString); assertEquals(buf, buf2); @@ -416,9 +451,9 @@ public class ByteBufUtilTest { .append('\uD800') .append('b') .toString(); - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); ByteBufUtil.writeUtf8(buf2, surrogateString); assertEquals(buf, buf2); @@ -435,9 +470,9 @@ public class ByteBufUtilTest { .append('\uDC00') .append('b') .toString(); - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); ByteBufUtil.writeUtf8(buf2, surrogateString); assertEquals(buf, buf2); @@ -452,9 +487,9 @@ public class ByteBufUtilTest { String surrogateString = new StringBuilder(2) .append('\uD800') .toString(); - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); ByteBufUtil.writeUtf8(buf2, surrogateString); assertEquals(buf, buf2); @@ -469,9 +504,9 @@ public class ByteBufUtilTest { String surrogateString = new StringBuilder(2) .append('\uDC00') .toString(); - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(surrogateString.getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); ByteBufUtil.writeUtf8(buf2, surrogateString); assertEquals(buf, buf2); @@ -484,9 +519,10 @@ public class ByteBufUtilTest { @Test public void testWriteUsAsciiString() { AsciiString usAscii = new AsciiString("NettyRocks"); - ByteBuf buf = Unpooled.buffer(16); + int expectedCapacity = usAscii.length(); + ByteBuf buf = buffer(expectedCapacity); buf.writeBytes(usAscii.toString().getBytes(CharsetUtil.US_ASCII)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(expectedCapacity); ByteBufUtil.writeAscii(buf2, usAscii); assertEquals(buf, buf2); @@ -498,17 +534,17 @@ public class ByteBufUtilTest { @Test public void testWriteUtf8Wrapped() { String usAscii = "Some UTF-8 like äÄ∏ŒŒ"; - ByteBuf buf = unreleasableBuffer(Unpooled.buffer(16)); + ByteBuf buf = unreleasableBuffer(buffer(16)); assertWrapped(buf); buf.writeBytes(usAscii.getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = unreleasableBuffer(Unpooled.buffer(16)); + ByteBuf buf2 = unreleasableBuffer(buffer(16)); assertWrapped(buf2); ByteBufUtil.writeUtf8(buf2, usAscii); assertEquals(buf, buf2); - buf.release(); - buf2.release(); + buf.unwrap().release(); + buf2.unwrap().release(); } private static void assertWrapped(ByteBuf buf) { @@ -518,9 +554,9 @@ public class ByteBufUtilTest { @Test public void testWriteUtf8Subsequence() { String usAscii = "Some UTF-8 like äÄ∏ŒŒ"; - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(usAscii.substring(5, 18).getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); ByteBufUtil.writeUtf8(buf2, usAscii, 5, 18); assertEquals(buf, buf2); @@ -532,9 +568,9 @@ public class ByteBufUtilTest { @Test public void testWriteUtf8SubsequenceSplitSurrogate() { String usAscii = "\uD800\uDC00"; // surrogate pair: one code point, two chars - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(usAscii.substring(0, 1).getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); ByteBufUtil.writeUtf8(buf2, usAscii, 0, 1); assertEquals(buf, buf2); @@ -546,9 +582,9 @@ public class ByteBufUtilTest { @Test public void testReserveAndWriteUtf8Subsequence() { String usAscii = "Some UTF-8 like äÄ∏ŒŒ"; - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); buf.writeBytes(usAscii.substring(5, 18).getBytes(CharsetUtil.UTF_8)); - ByteBuf buf2 = Unpooled.buffer(16); + ByteBuf buf2 = buffer(16); int count = ByteBufUtil.reserveAndWriteUtf8(buf2, usAscii, 5, 18, 16); assertEquals(buf, buf2); @@ -575,7 +611,7 @@ public class ByteBufUtilTest { private void testInvalidSubsequences(TestMethod method) { for (int [] range : INVALID_RANGES) { - ByteBuf buf = Unpooled.buffer(16); + ByteBuf buf = buffer(16); try { method.invoke(buf, "Some UTF-8 like äÄ∏ŒŒ", range[0], range[1]); fail("Did not throw IndexOutOfBoundsException for range (" + range[0] + ", " + range[1] + ")"); @@ -641,8 +677,8 @@ public class ByteBufUtilTest { CompositeByteBuf buffer = Unpooled.compositeBuffer(); try { byte[] bytes = "1234".getBytes(CharsetUtil.UTF_8); - buffer.addComponent(Unpooled.buffer(bytes.length).writeBytes(bytes)); - buffer.addComponent(Unpooled.buffer(bytes.length).writeBytes(bytes)); + buffer.addComponent(buffer(bytes.length).writeBytes(bytes)); + buffer.addComponent(buffer(bytes.length).writeBytes(bytes)); assertEquals("1234", buffer.toString(bytes.length, bytes.length, CharsetUtil.UTF_8)); } finally { buffer.release(); @@ -703,7 +739,7 @@ public class ByteBufUtilTest { @Test public void testIsTextWithInvalidIndexAndLength() { - ByteBuf buffer = Unpooled.buffer(); + ByteBuf buffer = buffer(4); try { buffer.writeBytes(new byte[4]); int[][] validIndexLengthPairs = { @@ -760,8 +796,8 @@ public class ByteBufUtilTest { checkUtf8Bytes(s); } - private static void checkUtf8Bytes(final CharSequence charSequence) { - final ByteBuf buf = Unpooled.buffer(ByteBufUtil.utf8MaxBytes(charSequence)); + private void checkUtf8Bytes(final CharSequence charSequence) { + final ByteBuf buf = buffer(ByteBufUtil.utf8MaxBytes(charSequence)); try { final int writtenBytes = ByteBufUtil.writeUtf8(buf, charSequence); final int utf8Bytes = ByteBufUtil.utf8Bytes(charSequence); @@ -771,8 +807,8 @@ public class ByteBufUtilTest { } } - private static void assertIsText(byte[] bytes, boolean expected, Charset charset) { - ByteBuf buffer = Unpooled.buffer(); + private void assertIsText(byte[] bytes, boolean expected, Charset charset) { + ByteBuf buffer = buffer(bytes.length); try { buffer.writeBytes(bytes); assertEquals(expected, ByteBufUtil.isText(buffer, charset)); @@ -783,6 +819,7 @@ public class ByteBufUtilTest { @Test public void testIsTextMultiThreaded() throws Throwable { + assumeThat(bufferType, is(BufferType.HEAP_UNPOOLED)); final ByteBuf buffer = Unpooled.copiedBuffer("Hello, World!", CharsetUtil.ISO_8859_1); try { @@ -822,21 +859,9 @@ public class ByteBufUtilTest { } @Test - public void testGetBytesHeap() { - final ByteBuf buf = Unpooled.buffer(4); + public void testGetBytes() { + final ByteBuf buf = buffer(4); try { - assumeTrue(buf.hasArray()); - checkGetBytes(buf); - } finally { - buf.release(); - } - } - - @Test - public void testGetBytesDirect() { - final ByteBuf buf = Unpooled.directBuffer(4); - try { - assumeFalse(buf.hasArray()); checkGetBytes(buf); } finally { buf.release(); @@ -845,16 +870,17 @@ public class ByteBufUtilTest { @Test public void testGetBytesHeapWithNonZeroArrayOffset() { - final ByteBuf buf = Unpooled.buffer(5); + assumeThat(bufferType, is(BufferType.HEAP_UNPOOLED)); + final ByteBuf buf = buffer(5); try { buf.setByte(0, 0x05); final ByteBuf slice = buf.slice(1, 4); slice.writerIndex(0); - assumeTrue(slice.hasArray()); - assumeThat(slice.arrayOffset(), is(1)); - assumeThat(slice.array().length, is(buf.capacity())); + assertTrue(slice.hasArray()); + assertThat(slice.arrayOffset(), is(1)); + assertThat(slice.array().length, is(buf.capacity())); checkGetBytes(slice); } finally { @@ -864,16 +890,17 @@ public class ByteBufUtilTest { @Test public void testGetBytesHeapWithArrayLengthGreaterThanCapacity() { - final ByteBuf buf = Unpooled.buffer(5); + assumeThat(bufferType, is(BufferType.HEAP_UNPOOLED)); + final ByteBuf buf = buffer(5); try { buf.setByte(4, 0x05); final ByteBuf slice = buf.slice(0, 4); slice.writerIndex(0); - assumeTrue(slice.hasArray()); - assumeThat(slice.arrayOffset(), is(0)); - assumeThat(slice.array().length, greaterThan(slice.capacity())); + assertTrue(slice.hasArray()); + assertThat(slice.arrayOffset(), is(0)); + assertThat(slice.array().length, greaterThan(slice.capacity())); checkGetBytes(slice); } finally { diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent.java b/common/src/main/java/io/netty/util/internal/PlatformDependent.java index 1ccaf7ca54..c7a35c8da5 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent.java @@ -273,6 +273,10 @@ public final class PlatformDependent { LINUX_OS_CLASSIFIERS = Collections.unmodifiableSet(availableClassifiers); } + public static long byteArrayBaseOffset() { + return BYTE_ARRAY_BASE_OFFSET; + } + public static boolean hasDirectBufferNoCleanerConstructor() { return PlatformDependent0.hasDirectBufferNoCleanerConstructor(); } @@ -659,6 +663,10 @@ public final class PlatformDependent { PlatformDependent0.putByte(data, index, value); } + public static void putByte(Object data, long offset, byte value) { + PlatformDependent0.putByte(data, offset, value); + } + public static void putShort(byte[] data, int index, short value) { PlatformDependent0.putShort(data, index, value); } @@ -687,6 +695,11 @@ public final class PlatformDependent { PlatformDependent0.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex, null, dstAddr, length); } + public static void copyMemory(byte[] src, int srcIndex, byte[] dst, int dstIndex, long length) { + PlatformDependent0.copyMemory(src, BYTE_ARRAY_BASE_OFFSET + srcIndex, + dst, BYTE_ARRAY_BASE_OFFSET + dstIndex, length); + } + public static void copyMemory(long srcAddr, byte[] dst, int dstIndex, long length) { PlatformDependent0.copyMemory(null, srcAddr, dst, BYTE_ARRAY_BASE_OFFSET + dstIndex, length); } diff --git a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java index d95aaa8cdb..ffcd6daa1e 100644 --- a/common/src/main/java/io/netty/util/internal/PlatformDependent0.java +++ b/common/src/main/java/io/netty/util/internal/PlatformDependent0.java @@ -590,6 +590,10 @@ final class PlatformDependent0 { UNSAFE.putByte(data, BYTE_ARRAY_BASE_OFFSET + index, value); } + static void putByte(Object data, long offset, byte value) { + UNSAFE.putByte(data, offset, value); + } + static void putShort(byte[] data, int index, short value) { UNSAFE.putShort(data, BYTE_ARRAY_BASE_OFFSET + index, value); } diff --git a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufUtilBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/ByteBufUtilBenchmark.java index 12e99cf520..78ce92911c 100644 --- a/microbench/src/main/java/io/netty/microbench/buffer/ByteBufUtilBenchmark.java +++ b/microbench/src/main/java/io/netty/microbench/buffer/ByteBufUtilBenchmark.java @@ -22,6 +22,7 @@ import io.netty.microbench.util.AbstractMicrobenchmark; import io.netty.util.CharsetUtil; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Param; import org.openjdk.jmh.annotations.Scope; import org.openjdk.jmh.annotations.Setup; import org.openjdk.jmh.annotations.State; @@ -34,6 +35,11 @@ import org.openjdk.jmh.annotations.Warmup; @Measurement(iterations = 10) public class ByteBufUtilBenchmark extends AbstractMicrobenchmark { + + @Param({ "true", "false" }) + private boolean direct; + @Param({ "8", "16", "64", "128" }) + private int length; private ByteBuf buffer; private ByteBuf wrapped; private ByteBuf asciiBuffer; @@ -48,18 +54,19 @@ public class @Setup public void setup() { // Use buffer sizes that will also allow to write UTF-8 without grow the buffer - buffer = Unpooled.directBuffer(512); - wrapped = Unpooled.unreleasableBuffer(Unpooled.directBuffer(512)); - asciiSequence = new StringBuilder(128); - for (int i = 0; i < 128; i++) { + final int maxBytes = ByteBufUtil.utf8MaxBytes(length); + buffer = direct? Unpooled.directBuffer(maxBytes) : Unpooled.buffer(maxBytes); + wrapped = Unpooled.unreleasableBuffer(direct? Unpooled.directBuffer(maxBytes) : Unpooled.buffer(maxBytes)); + asciiSequence = new StringBuilder(length); + for (int i = 0; i < length; i++) { asciiSequence.append('a'); } ascii = asciiSequence.toString(); // Generate some mixed UTF-8 String for benchmark - utf8Sequence = new StringBuilder(128); + utf8Sequence = new StringBuilder(length); char[] chars = "Some UTF-8 like äÄ∏ŒŒ".toCharArray(); - for (int i = 0; i < 128; i++) { + for (int i = 0; i < length; i++) { utf8Sequence.append(chars[i % chars.length]); } utf8 = utf8Sequence.toString(); diff --git a/microbench/src/main/java/io/netty/microbench/buffer/Utf8EncodingBenchmark.java b/microbench/src/main/java/io/netty/microbench/buffer/Utf8EncodingBenchmark.java new file mode 100644 index 0000000000..03d96c57e0 --- /dev/null +++ b/microbench/src/main/java/io/netty/microbench/buffer/Utf8EncodingBenchmark.java @@ -0,0 +1,277 @@ +/* + * Copyright 2020 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.microbench.buffer; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; +import io.netty.microbench.util.AbstractMicrobenchmark; +import io.netty.util.AsciiString; +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.CompilerControl; +import org.openjdk.jmh.annotations.CompilerControl.Mode; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Setup; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +@Fork(value = 2, jvmArgsAppend = "-XX:MaxInlineLevel=9") +public class Utf8EncodingBenchmark extends AbstractMicrobenchmark { + private static class AnotherCharSequence implements CharSequence { + private final char[] chars; + + AnotherCharSequence(String chars) { + this.chars = new char[chars.length()]; + chars.getChars(0, chars.length(), this.chars, 0); + } + + @Override + public int length() { + return chars.length; + } + + @Override + public char charAt(int i) { + return chars[i]; + } + + @Override + public CharSequence subSequence(int start, int end) { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + throw new UnsupportedOperationException(); + } + } + + // experiment test input + private String[] strings; + private StringBuilder[] stringBuilders; + private AnotherCharSequence[] anotherCharSequences; + private AsciiString[] asciiStrings; + @Param({ "false", "true" }) + private boolean direct; + private ByteBuf buffer; + @Param({ "false", "true" }) + private boolean noUnsafe; + private int dataSetLength; + + @Setup + public void init() { + System.setProperty("io.netty.noUnsafe", Boolean.valueOf(noUnsafe).toString()); + InputStream testTextStream = null; + InputStreamReader inStreamReader = null; + BufferedReader buffReader = null; + int maxExpectedSize = 0; + List strings = new ArrayList(); + List stringBuilders = new ArrayList(); + List anotherCharSequenceList = new ArrayList(); + List asciiStrings = new ArrayList(); + try { + testTextStream = getClass().getResourceAsStream("/Utf8Samples.txt"); + inStreamReader = new InputStreamReader(testTextStream, "UTF-8"); + buffReader = new BufferedReader(inStreamReader); + String line; + while ((line = buffReader.readLine()) != null) { + strings.add(line); + stringBuilders.add(new StringBuilder(line)); + anotherCharSequenceList.add(new AnotherCharSequence(line)); + asciiStrings.add(new AsciiString(line)); + maxExpectedSize = Math.max(maxExpectedSize, ByteBufUtil.utf8MaxBytes(line.length())); + } + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + closeStream(testTextStream); + closeReader(inStreamReader); + closeReader(buffReader); + } + buffer = direct? Unpooled.directBuffer(maxExpectedSize, maxExpectedSize) : + Unpooled.buffer(maxExpectedSize, maxExpectedSize); + buffer.setByte(maxExpectedSize - 1, 0); + this.strings = strings.toArray(new String[strings.size()]); + this.stringBuilders = stringBuilders.toArray(new StringBuilder[stringBuilders.size()]); + this.anotherCharSequences = + anotherCharSequenceList.toArray(new AnotherCharSequence[anotherCharSequenceList.size()]); + this.asciiStrings = asciiStrings.toArray(new AsciiString[asciiStrings.size()]); + this.dataSetLength = this.strings.length; + } + + private static void closeStream(InputStream inStream) { + if (inStream != null) { + try { + inStream.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + private static void closeReader(Reader buffReader) { + if (buffReader != null) { + try { + buffReader.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + + @Benchmark + @CompilerControl(Mode.DONT_INLINE) + public int nestedByteBufUtilWriteUtf8String() { + int countBytes = 0; + for (String string : strings) { + countBytes += nestedByteBufUtilWriteUtf8String1(string); + } + return countBytes; + } + + private int nestedByteBufUtilWriteUtf8String1(String string) { + return nestedByteBufUtilWriteUtf8String2(string); + } + + private int nestedByteBufUtilWriteUtf8String2(String string) { + return nestedByteBufUtilWriteUtf8String3(string); + } + + private int nestedByteBufUtilWriteUtf8String3(String string) { + return nestedByteBufUtilWriteUtf8String4(string); + } + + private int nestedByteBufUtilWriteUtf8String4(String string) { + return nestedByteBufUtilWriteUtf8String5(string); + } + + private int nestedByteBufUtilWriteUtf8String5(String string) { + return nestedByteBufUtilWriteUtf8String6(string); + } + + private int nestedByteBufUtilWriteUtf8String6(String string) { + // this calls should be inlined but...what happen to the subsequent calls > MaxInlineLevel? + buffer.resetWriterIndex(); + ByteBufUtil.writeUtf8(buffer, string, 0, string.length()); + return buffer.writerIndex(); + } + + @Benchmark + @CompilerControl(Mode.DONT_INLINE) + public int byteBufUtilWriteUtf8String() { + int countBytes = 0; + for (String string : strings) { + buffer.resetWriterIndex(); + ByteBufUtil.writeUtf8(buffer, string, 0, string.length()); + countBytes += buffer.writerIndex(); + } + return countBytes; + } + + @Benchmark + @CompilerControl(Mode.DONT_INLINE) + public int byteBufUtilWriteUtf8Bimorphic() { + int countBytes = 0; + for (int i = 0, size = dataSetLength; i < size; i++) { + final StringBuilder stringBuilder = stringBuilders[i]; + final String string = strings[i]; + buffer.resetWriterIndex(); + ByteBufUtil.writeUtf8(buffer, stringBuilder, 0, stringBuilder.length()); + countBytes += buffer.writerIndex(); + buffer.resetWriterIndex(); + ByteBufUtil.writeUtf8(buffer, string, 0, string.length()); + countBytes += buffer.writerIndex(); + } + return countBytes; + } + + @Benchmark + @CompilerControl(Mode.DONT_INLINE) + public int byteBufUtilWriteUtf8Megamorphic() { + int countBytes = 0; + for (int i = 0, size = dataSetLength; i < size; i++) { + final StringBuilder stringBuilder = stringBuilders[i]; + final String string = strings[i]; + final AnotherCharSequence anotherCharSequence = anotherCharSequences[i]; + buffer.resetWriterIndex(); + ByteBufUtil.writeUtf8(buffer, stringBuilder, 0, stringBuilder.length()); + countBytes += buffer.writerIndex(); + buffer.resetWriterIndex(); + ByteBufUtil.writeUtf8(buffer, string, 0, string.length()); + countBytes += buffer.writerIndex(); + buffer.resetWriterIndex(); + ByteBufUtil.writeUtf8(buffer, anotherCharSequence, 0, anotherCharSequence.length()); + countBytes += buffer.writerIndex(); + } + return countBytes; + } + + @Benchmark + @CompilerControl(Mode.DONT_INLINE) + public int byteBufUtilWriteUtf8CommonCharSequences() { + int countBytes = 0; + for (int i = 0, size = dataSetLength; i < size; i++) { + final StringBuilder stringBuilder = stringBuilders[i]; + final String string = strings[i]; + final AsciiString asciiString = asciiStrings[i]; + buffer.resetWriterIndex(); + ByteBufUtil.writeUtf8(buffer, stringBuilder, 0, stringBuilder.length()); + countBytes += buffer.writerIndex(); + buffer.resetWriterIndex(); + ByteBufUtil.writeUtf8(buffer, string, 0, string.length()); + countBytes += buffer.writerIndex(); + buffer.resetWriterIndex(); + ByteBufUtil.writeUtf8(buffer, asciiString, 0, asciiString.length()); + countBytes += buffer.writerIndex(); + } + return countBytes; + } + + @Benchmark + @CompilerControl(Mode.DONT_INLINE) + public int byteBufUtilWriteUtf8AsciiString() { + int countBytes = 0; + for (int i = 0, size = dataSetLength; i < size; i++) { + final AsciiString asciiString = asciiStrings[i]; + buffer.resetWriterIndex(); + ByteBufUtil.writeUtf8(buffer, asciiString, 0, asciiString.length()); + countBytes += buffer.writerIndex(); + } + return countBytes; + } + + @Benchmark + @CompilerControl(Mode.DONT_INLINE) + public int writeGetBytes() throws UnsupportedEncodingException { + int countBytes = 0; + for (String string : strings) { + buffer.resetWriterIndex(); + final byte[] bytes = string.getBytes("UTF-8"); + buffer.writeBytes(bytes); + countBytes += buffer.writerIndex(); + } + return countBytes; + } + +} diff --git a/microbench/src/main/resources/Utf8Samples.txt b/microbench/src/main/resources/Utf8Samples.txt new file mode 100644 index 0000000000..d895285054 --- /dev/null +++ b/microbench/src/main/resources/Utf8Samples.txt @@ -0,0 +1,1399 @@ +Kon nie 'n tydelike lêer skep vir storing van: %s +Wil jy hê ek moet probeer om die muis-konfigurasieprogram te laat loop? Neem kennis datjy die stamwagwoord hiervoor nodig het. +Verifiëringsbewysruiling vir gebruiker %s het gefaal +Moenie woorde oor twee reëls _opbreek nie +Seçili Körpü +AtkHyperlink cisminin seçildiyini bildirir +Lövbər Sayı +AtkHyperlink cismi ilə əlaqələndirilmiş lövbər ədədini bildirir. +Son indeks +AtkHyperlink cisminin son indeksi +Başlanğıc indeksi +AtkHyperlink cisminin başlanğıc indeksi +hökmsüz +sür'ətləndirici etiketi +xəbərdarlıq +animasiya +ox +təqvim +kanvas +işarələmə qutusu +işarələmə menyusu üzvü +rəng seçicisi +sütun başlığı +kombo qutusu +tarix editoru +masa üstü timsalı +masa üstü çərçivəsi +zəng-et +dialoq +cərgə lövhəsi +rəsm sahəsi +fayl seçicisi +doldurucu +yazı növü seçicisi +çərçivə +ayna lövhəsi +html konteyneri +timsal +şəkil +daxili çərçivə +etiket +laylanmış lövhə +siyahı +siyahı üzvü +menyu +menyu çubuğu +menyu üzvü +seçim lövhəsi +səhifə səkməsi +səhifə səkməsi siyahısı +panel +şifrə mətni +popup menyu +ilərləmə çubuğu +basma düyməsi +qərar düyməsi +qərar menyusu üzvü +kök lövhə +sətir başlığı +sürüşdürmə çubuğu +sürüşdürmə lövhəsi +ayırıcı +sürüşdürücü +ayırma lövhəsi +dönən düymə +vəziyyət-çubuğu +cədvəl +cədvəl hücrəsi +cədvəl sütun başlığı +cədvəl sətir başlığı +qopardıla bilən menyu üzvü +terminal +mətn +keçiş düyməsi +vasitə çubuğu +məsləhət +ağac +budaq cədvəli +namə'lum +nümayiş-lövhəsi +pəncərə +başlıq +altlıq +paraqraf +tə'minat +avtomatik tamamlama +düzəlişlər çubuğu +daxili tərkib hissəsi +Yetişilə Bilən Ad +Yardımçı texnologiyalara yetişmə üçün şəkilləndilirmiş cismin adı +Yetişilə Bilən İzahat +Yardımçı texnologiyalara yetişmə üçün şəkilləndilirmiş cismin izahatı +Yetişilə Bilən Valideyn +Valideynin dəyişildiyini bildirmək üçün işlədilir +Yetişilə Bilən Qiymət +Qiymətin dəyişildiyini bildirmək üçün işlədilir +Yetişilə Bilən Rol +Bu üzvün yetişilə bilən rolu +Yetişilə Bilən Lay +Bu üzvün yetişilə bilən layı +Yetişilə Bilən MDI Qiyməti +Bu üzvün yetişilə bilən MDI qiyməti +Yetişilə Bilən Cədvəl Etiketi +Cədvəl etiket +Вылучаная спасылка +Задае, ці зьяўляецца аб'ект AtkHyperlink вылучаным +Колькасьць якараў +Колькасьць якараў, якія зьвязаныя з аб'ектам AtkHyperlink +Канцавы індэкс +Канцавы індэкс аб'екту AtkHyperlink +Пачатковы індэкс +Пачатковы індэкс аб'екту AtkHyperlink +нерэчаіснае +адмеціна паскаральніку +сыгнал +анімацыя +стрэлка +каляндар +канва +кнопка-пазначальнік +пункт-пазначальнік мэню +абіральнік колеру +загаловак слупку +камбінаваны элемэнт +рэдактар даты +значка стальцу +кадар стальцу +лічбавае табло +дыялёг +акно тэчкі +прастора маляваньня +абіральнік файлу +фільтар +абіральнік шрыфту +кадар +акно празрыстасьці +кантэйнэр html +значка +відарыс +унутраны кадар +адмеціна +акно узроўняў +сьпіс +пункт сьпісу +мэню +радок мэню +пункт мэню +акно выбараў +ўкладка старонкі +сьпіс ўкладак старонкі +панэль +тэкст паролю +усплыўное мэню +палоска посьпеху +кнопка націску +кнопка ўзаемавыключнага выбару +пункт мэню ўзаемавыключнага выбару +карэннае акно +загаловак радку +паласа пракруткі +акно пракруткі +дзельнік +паўзунок +дзельнік вокнаў +пракрутка +радок стану +табліца +ячэя табліцы +загаловак слупку табліцы +загаловак радку табліцы +пункт зачапленьня мэню +тэрмінал +тэкст +пераключальнік +панэль сродкаў +падказка +дрэва +табліца дрэва +невядома +акно прагляду +акно +загаловак +ніжняя частка акна +абзац +дастасаваньне +аўтазавяршэньне +радок рэдагаваньня +убудаваны кампанэнт +Даступная назва +Назва экзэмпляру аб'екту, фарматаваная для тэхналёгіі садзеяньня +Апісаньне даступнасьці +Апісаньне аб'екта, фарматаванае для тэхналёгіі садзеяньня +Даступнасьць бацькоўскага аб'екту +Выкарыстоўваецца, каб паведамляць пра зьмены бацькоўскага аб'екту +Значэньне даступнасьці +Выкарыстоўваецца, каб паведамляць пра зьмену значэньня +Роля даступнасьці +Роля аб'екту ў даступнасьці +Узровень даступнасьці +Узровень даступнасьці гэтага +Избрана препратка +Определя дали обектът AtkHyperlink е избран +Брой котви +Броят на котвите свързани с обект AtkHyperlink +Край на индекс +Крайният индекс на обект AtkHyperlink +Начало на индекс +Началният индекс на обект AtkHyperlink +невалидно +надпис на ускорител +внимание +анимация +стрелка +календар +платно +кутийка за отмятане +елемент на менюто за отмятане +избор на цвят +заглавие на колона +кутийка за избор от определени стойности +редактор на дата +икона за работния плот +рамка на работния плот +скала +диалогова кутия +изглед към папки +област за рисуване +избор на файл +пълнител +избор на шрифт +рамка +най-горна рамка +контейнер с код на html +икона +картина +вътрешна рамка +етикет +рамка със слоеве обекти +списък +елемент на списък +меню +лента с менюта +елемент от меню +панел с опции +таб за страница +списък с табове +панел +текстово поле за парола +изскачащо меню +лента за прогрес +бутон +радио-бутон +радио-елемент от меню +основна рамка +заглавие на ред +лента за прелистване +рамка, която се прелиства +разделител +плъзгач +разделителна рамка +бутон за последователен избор +лента за състояние +таблица +клетка от таблица +заглавие на колона в таблица +заглавие на ред в таблица +елемент на менюто, който може да се отдели от него +терминал +текст +превключващ бутон +лента с инструменти +подсказка +дърво +дървовидна таблица +непознато +изглед +прозорец +горен колонтитул +долен колонтитул +абзац +приложение +автоматично довършване +лента за редакция +вграден компонент +Достъпно име +Име на обект от някакъв клас. Името е форматирано за технологии за достъпност +Достъпно описание +Описание на обект, форматиран за технологии за достъпност +Достъпен родител +Използва се, за да уведоми за промяна в родителя +Достъпна стойност +Използва се, за да се уведоми за промяна на стойността + +Izabrani link +Označava da li je izabran objekt AtkHyperlink +Broj sidara +Broj sidara združenih s objektom AtkHyperlink +Završni indeks +Završni indeks objekta AtkHyperlink +Početni indeks +Početni indeks objekta AtkHyperlink +nevažeće +oznaka akceleratora +uzbuna +animacija +strelica +kalendar +kanafas +okvir za izbor +stavka menija za izbor +birač boja +zaglavlje kolone +kombinovani okvir +editor datuma +desktop ikona +okvir za desktop +biranje broja +dijalog +okno za direktorije +područje za crtanje +birač datoteka +punjač +birač fontova +okvir +stakleno okno +html spremište +ikona +slika +interni okvir +oznaka +okno sa slojevima +lista +stavka liste +meni +traka menija +stavka menija +okno za opcije +kartica +lista kartica +panel +tekst šifre +popup meni +traka napredovanja +dugme +radio dugme +stavka radio menija +glavno okno +zaglavlje reda +traka s klizačem +okno sa klizačem +linija razdvajanja +klizač +razdijeljeno okno +spin dugme +statusna traka +tablica +polje tablice +zaglavlje kolone tablice +zaglavlje reda tablice +odvojiva stavka menija +terminal +tekst +dugme prekidač +traka s alatima +opis alata +stablo +tablica stabla +nepoznato +port pregleda +prozor +zaglavlje +tekst na dnu +paragraf +aplikacija +samodopuna +traka za izmjene +ugrađena komponenta +Dostupno ime +Ime primjerka objekta je formatirano za dostup uz pomoćnu tehnologiju +Dostupan opis +Opis objekta, formatiran za dostup uz pomoćnu tehnologiju +Dostupno porijeklo +Koristi se za obavijest o promjeni porijekla +Dostupna vrijednost +Koristi se za obavijest o promjeni vrijednosti +Dostupna uloga +Dostupna uloga ovog objekta +Dostupan sloj +Dostupan sloj ovog objekta +Dostupna MDI vrijednost +Dostupna MDI vrijednost ovog objekta +Dostupan natpis tablice +Koristi se za obavijest o promjeni natpisa tablice. Ova osobina se ne bi trebala +Pot ser voleu actualitzar a una versió nova del sistema %s +Seleccioneu el producte per a l'informe d'error. +Escolliu un component, versió, i nivell de gravetat per al producte %s +Seleccioneu una aplicació per a l'informe d'error. +Escolliu un component, versió, i nivell de gravetat per a l'aplicació %s +Aquesta aplicació té informació d'error, però Bug Buddy no sap res d'ella. Seleccioneu una altra aplicació. +Heu d'indicar el component a què es refereix l'informe d'error. +Nom de l'informador de l'error +Adreça electrònica +Adreça de correu amb la qual enviar errors al Bugzilla del GNOME. Aquesta adreça s'utilitzarà per a la correspondència relacionada amb l'error que esteu enviant. Si ja teniu un compte al Bugzilla del GNOME, utilitzeu-la com la vostra adreça de correu. +Fitxer on desar els informes d'error +Fitxer on voleu desar el vostre informe d'error per a enviar-lo més tard. +Última vegada que Bug Buddy va comprovar si hi ha actualitzacions +Camí al vostre sistema de fitxers local on es troba sendmail o un d'equivalent. +Camí al programa de correu semblant a sendmail +Nom real de l'usuari que està enviant l'informe d'error. +Aquesta és l'última vegada (marca de temps UNIX) que Bug Buddy va comprovar si hi havien actualitzacions de la informació de Bugzilla. +Utilitza sendmail per a enviar els informes d'error al Bugzilla del GNOME. És l'únic mètode suportat per ara. +Utilitza sendmail per a enviar informes d'error +Vybraný odkaz +Určuje, jestli je objekt AtkHyperlink vybrán +Počet ukotvení +Počet ukotvení asociovaných s objektem AtkHyperlink +Index konce +Index konce objektu AtkHyperlink +Index začátku +Indek začátku objektu AtkHyperlink +neplatný +popisek klávesové zkratky +upozornění +animace +šipka +kalendář +plátno +zaškrtávací políčko +zaškrtávací položka menu +výběr barvy +záhlaví sloupce +kombo box +editor data +ikona pracovní plochy +rám pracovní plochy +vytáčení +dialog +panel adresářů +kreslicí oblast +výběr souboru +plnič +výběr písma +rám +skleněný panel +kontejner html +ikona +obrázek +interní rám +popisek +vrstvený panel +seznam +položka seznamu +menu +lišta menu +položka menu +panel voleb +záložka stránky +seznam záložek stránky +panel +text hesla +vyskakovací menu +lišta průběhu +tlačítko +rádiové tlačítko +rádiová položka menu +kořenový panel +záhlaví řádku +posuvná lišta +posuvný panel +oddělovač +posunovač +oddělený panel +otáčecí tlačítko +stavová-lišta +tabulka +buňka tabulky +záhlaví sloupce tabulky +záhlaví řádku tabulky +odtrhávací položka menu +terminál +text +přepínač +nástrojová lišta +tip +strom +stromová tabulka +neznámý +pohled +okno +záhlaví +zápatí +odstavec +aplikace +autodoplnění +lišta úprav +vložená komponenta +Zpřístupněný název +Název instance objektu formátovaný pro použití technologiemi zpřístupnění +Zpřístupněný popis +Popis objektu formátovaný pro použití technologiemi zpřístupnění +Zpřístupněný rodič +Používá se pro upozornění, že rodič byl změněn +Zpřístupněná hodnota +Používá se pro upozornění, že hodnota byla změněna +Zpřístupněná role +Zpřístupněná role tohoto objektu +Zpřístupněná vrstva +Zpřístupněná vrstva tohoto objektu +Zpřístupněná hodnota MDI +Zpřístupněná hodnota MDI tohoto objektu +Zpřístupněný nadpis tabulky +Používá se pro upozornění, že nadpis tabulky byl změn +Valgt henvisning +Angiver om AtkHyperlink-objektet er valgt +Antal ankre +Antallet af ankre der er associeret med AtkHyperlink-objektet +Slutindeks +Slutindeks for AtkHyperlink-objektet +Startindeks +Startindeks for AtkHyperlink-objektet +ugyldig +genvejsetiket +advarsel +animation +pil +kalender +lærred +afkrydsningsboks +afkrydsningsmenupunkt +farvevælger +kolonnetitel +kombinationsboks +datoredigering +skrivebordsikon +skrivebordsramme +opkald +vindue +mappepanel +tegneområde +filvælger +udfylder +skrifttypevælger +ramme +glaspanel +html-beholder +ikon +billede +intern ramme +etiket +lagdelt panel +liste +listepunkt +menu +menulinje +menupunkt +indstillingspanel +sidefane +sidefaneliste +panel +adgangskodetekst +pop-op-menu +fremgangslinje +trykknap +radioknap +radiomenupunkt +rodpanel +rækketitel +rulleskakt +rullepanel +adskiller +skyder +delt panel +talindtastning +statuslinje +tabel +tabelcelle +tabelkolonnetitel +tabelrækketitel +afrivningsmenupunkt +terminal +tekst +skifteknap +værktøjslinje +værktøjstip +træ +trætabel +ukendt +visningsområde +vindue +sidehoved +sidefod +afsnit +program +autofuldførelse +redigér linje +indlejret komponent +Tilgængelighedsnavn +Objektinstansens navn formateret til assisterende teknologier +Tilgængelighedsbeskrivelse +Beskrivelse af eet objekt, formateret til assisterende teknologier +Tilgængelighedsophav +Bruges til at bekendtgøre at ophavet er ændret +Tilgængelighedsværdi +Bruges til at bekendtgøre at værdien er ændret +Tilgængelighedsrolle +Tilgængelighedsrolle for dette objekt +Tilgængelighedslag +Tilgængelighedslaget for dette objekt +Tilgængeligheds-MDI-værdi +Tilgængeligheds-MDI-værdien for dette objekt +Tilgængelighedstabeltitel +Bruges til at bekendtgøre at tabeltitlen er ændret; denne egenskab bør ikke benyttes. accessible-table-caption-object bør bruges i +Ausgewählter Link +Gibt an, ob das AtkHyperlink-Objekt ausgewählt ist +Ankeranzahl +Die Anzahl der mit dem AtkHyperlink-Objekt assoziierten Anker +Endindex +Der Endindex des AtkHyperlink-Objekts +Anfangsindex +Der Anfangsindex des AtkHyperlink-Objekts +ungültig +Kürzelbeschriftung +Alarm +Animation +Pfeil +Kalender +Leinwand +Kontrollkästchen +Kontrollmenüobjekt +Farbwähler +Spaltenkopf +Kombinationsfeld +Datumseditor +Desktop-Symbol +Desktop-Rahmen +wählen +Dialog +Verzeichnisleiste +Zeichenfeld +Dateiwähler +Füller +Schriftwähler +Rahmen +Glasleiste +HTML-Container +Symbol +Bild +interner Rahmen +Beschriftung +Schichtleiste +Liste +Listenobjekt +Menü +Menüleiste +Menüobjekt +Optionsleiste +Seitenreiter +Seitenreiter-Liste +Panel +Passworttext +Popup-Menü +Fortschrittsleiste +Druckknopf +Radioknopf +Radiomenüobjekt +Wurzelleiste +Zeilenkopf +Rollbalken +Rollleiste +Trennlinie +Schieber +geteilte Leiste +Spin-Knopf +Statusleiste +Tabelle +Tabellenzelle +Spaltenkopf einer Tabelle +Zeilenkopf einer Tabelle +Objekt in Abrissmenü +Terminal +Text +Auslöseknopf +Werkzeugleiste +Minihilfe +Baum +Baumtabelle +unbekannt +Sichtfeld +Fenster +Kopfzeilen +Fußzeilen +Absatz +Anwendung +Auto-Vervollständigung +Bearbeitungsleiste +eingebettete Komponente +Barrierefreier Name +Name der Objektinstanz, formatiert für den Zugriff durch Hilfstechnologien +Barrierefreie Beschreibung +Beschreibung eines Objekts, formatiert für den Zugriff durch Hilfstechnologien +Barrierefreies Eltern-Element +Wird zur Benachrichtigung bei Änderungen am Eltern-Element verwendet +Barrierefreier Wert +Wird zur Benachrichtigung bei Wertänderungen verwendet +Barrierefreie Rolle +Die barrierefreie Rolle dieses Objekts +Barrierefreie Ebene +Die barrierefreie Ebene dieses Objekts +Barrierefreier MDI-Wert +Der barrierefreie MDI-Wert dieses Objekts +Barr +Επιλεγμένος Σύνδεσμος +Καθορίζει αν το αντικείμενο AtkHyperlink είναι επιλεγμένο +Αριθμός Αγκύρων +Ο αριθμός από άγκυρες που σχετίζονται με το αντικείμενο AtkHyperlink +End index +Το end index του αντικειμένου AtkHyperlink +Start index +Το start index του αντικειμένου AtkHyperlink +μη έγκυρο +ετικέτα συντόμ +ειδοποίηση +κινούμενο σχέδιο +βέλος +ημερολόγιο +canvas +κουτί επιλογής +αντικείμενο μενού επιλογής +επιλογέας χρώματος +κεφαλίδα στήλης +κουτί πολλαπλών +επεξεργαστής-ημερομηνίας +εικονίδιο επιφάνειας +πλαίσιο επιφάνειας εργασίας +dial +διάλογος +ταμπλώ καταλόγου +περιοχή σχεδίασης +επιλογέας αρχείου +filler +επιλογέας γραμματοσειράς +πλαίσιο +glass pane +html container +εικονίδιο +εικόνα +εσωτερικό πλαίσιο +ετικέτα +layered pane +λίστα +αντικείμενο λίστας +μενού +εργαλειοθήκη μενού +αντικείμενο μενού +ταμπλώ επιλογών +στήλη σελίδας +λίστα στήλης σελίδας +ταμπλώ +κείμενο κωδικού +αναδυόμενο μενού +μπάρα προόδου +κουμπί πίεσης +κουμπί radio +αντικείμενο μενού radio +ταμπλώ root +κεφαλίδα γραμμής +γραμμή κύλισης +ταμπλώ κύλισης +διαχωριστικό +μπάρα κύλισης +διαχωρ ταμπλώ +κουμπί στροβιλισμού (spin) +γραμμή κατάστασης +πίνακας +κελί πίνακα +κεφαλίδα στήλης πίνακα +κεφαλίδα γραμμής πίνακα +αντικείμενο αποσπώμενου μενού +τερματικό +κείμενο +κουμπί εναλλαγής +εργαλειοθήκη +συμβουλή εργαλείου +δένδρο +δένδρο πίνακα +άγνωστο +θύρα όψης +παράθυρο +κεφαλίδα +υποσέλιδο +παράγραφος +εφαρμογή +αυτόματη συμπλήρωση +επεξεργασία εργαλειοθήκης +ενσωματομένο συστατικό +Προσβάσιμο Όνομα +Το όνομα της εμφάνισης του αντικειμένου μορφοποιημένο για πρόσβαση από την βοηθητική τεχνολογία +Προσιτή περιγραφή +Περιγραφή του αντικειμένου μορφοποιημένη για πρόσβαση από την βοηθητική τεχνολογία +Προσβάσιμο Γονικό +Χρησιμοποιείται για ειδοποίηση ότι το μητρικό έχει αλλαξει +Προσβάσιμη Τιμή +La información de errores almacenada en su sistema no está actualizada. Elija «Actualizar» para hacerlo. Si elije «No actualizar» forzará a informar el error usando los datos antiguos. +Avanzado +Fenómeno Assbarn +Archivo binario: +Bug Buddy +Bug Buddy puede enviar información de depuración junto a su informe de error. +Las opciones correctas se deben haber seleccionado automáticamente. +Bug Buddy está recogiendo información sobre su cuelgue para enviarla al sistema de seguimiento de errores. Este proceso está automatizado, y tardará unos pocos minutos. Cuando haya terminado, puede pulsar «Mostrar detalles de depuración» para ver la información o pulsar «Continuar» para moverse al siguiente paso en el proceso. +Bug Buddy usa el correo electrónico para enviar un informe de errores. +Elija cómo le gustaría que Bug Buddy envíe el correo-e. +Cc: +Archivo core +D_epurar una aplicación colgada o una aplicación en funcionamiento (sólo expertos) +Depuración +Descripción +Descargando archivos +Correo-e: +Incluir un archivo de texto +Guard_arlo en un archivo para que pueda enviar el informe manualmente +Nombre: +Ruta de sendmail: +Elija un componente, versión y nivel de severidad. +Haga cualquier corrección final al informe de errores. Dese cuenta de que será mostrado en http://bugzilla.gnome.org Incluirá su nombre, dirección de correo-e, y quizá alguna información acerca de cómo falló la aplicación. Si el documento en el que estaba trabajando contenía información sensible, quizá no quiera enviar este informe +Ühtegi rakendust "%s"-i avamiseks ei ole. Sa saad selle asemel selle alla laadida. +Salvesta _kui... +"%s" protokoll ei ole toetatud. +Toetatud protokollid on "http", "https", "ftp", "file", "smb" ja "sftp". +Faili “%s” ei leitud. +Kontrolli faili asukohta ja proovi uuesti. +“%s” pole võimalik leida. +Kontrolli kas oled ühendatud internetti ja kas aadress on korrektne. +Juhul kui see leht oli varem olemas, siis arhiveeritud versiooni võid leida: +"%s" keeldus ühendusest. +“%s” katkestas ühenduse. +“%s” ei vasta. +Vigane aadress +Sinu sisestatud aadress on vigane. +“%s” suunati ümber liiga palju kordi. +Turvakaalutlustel ümbersuunamine lõpetati. +“%s” nõuab krüptitud ühendust. +“%s” loobus ühendusest. +Ühendus serveriga katkes enne kui andmeid lugeda jõuti. +Autonoomses režiimis pole võimalik dokumenti laadida. +“%s” keelab ligipääsu pordile “%d”. +Proksiserveriga pole võimalik ühenduda. +Google'i vahemälust +Internetiarhiivist +Nimetu +Kõik failid +Veebilehed +Tekstifailid +Pildid +XML failid +XUL failid +_Vali sertifikaat +Enda tõendamiseks kasutatava sertifikaadi valimine. +Sertifikaadi ü_ksikasjad +_Ava sertifikaat +_Nõustu +Kas usaldad ebakorrektset turvainfot? +Kas ühenduda ebausaldusväärsesse saiti? +_Usalda seda turvainfot edaspidigi +Üh_endu +Kas võtad vastu kehtivuse kaotanud turvainfo? +"%s" turvaandmed aegusid %s. +Kas võtad vastu veel mittekehtiva turvainfo? +%a, %d, %b %Y +Sa peaks veenduma, et su arvuti kell on õige. +Ühendust "%s"-ga pole võimalik luua. +"%s" sertifikaatide tühistusnimekiri (CRL) vajab uuendamist. +Palun pöördu nõuannete saamiseks oma süsteemihalduri poole. +Kas usaldada sertifitseerimiskeskust? +_Usalda sertifitseerimiskeskust +Kas usaldada uut sertifitseerimiskeskust "%s" veebisaitide tuvastamiseks? +Lien sélectionné +Spécifie si l'objet AtkHyperlink est sélectionné +Nombre d'ancres +Le nombre d'ancres associés avec l'objet AtkHyperlink +Fin d'index +La fin de l'index de l'objet AtkHyperlink +Début d'index +Le début de l'index de l'objet AtkHyperlink +non valide +étiquette du raccourci +alerte +animation +curseur +calendrier +canevas +case à cocher +élément de menu avec case à cocher +sélecteur de couleurs +en-tête de colonne +liste combinée +éditeur de dates +icône du bureau +cadre du bureau +bouton de réglage +boîte de dialogue +panneau des répertoires +zone de dessin +sélecteur de fichiers +caractère de remplissage +sélecteur de polices +cadre +panneau de verre +conteneur html +icône +image +cadre interne +étiquette +panneau superposé +liste +élément de liste +menu +barre de menus +élément de menu +panneau d'options +onglet de page +liste d'onglets de page +tableau de bord +champ de mot de passe +menu contextuel +barre de progression +bouton poussoir +bouton radio +élément de menu avec bouton radio +panneau racine +en-tête de ligne +barre de défilement +panneau de défilement +séparateur +case de défilement +panneau divisible +bouton de réglages +barre d'état +tableau +cellule de tableau +en-tête de colonne de tableau +en-tête de ligne de tableau +élément de menu détachable +console +texte +bouton à état +barre d'outils +bulle d'aide +arbre +Tableau arborescent +inconnu +fenêtre de travail +fenêtre +en-tête de page +bas de page +paragraphe +application +Autocomplétion +barre d'édition +composant incorporé +Nom accessible +Nom d'une instance d'objet formaté pour être accessible par les aides techniques +Description accessible +Description d'un objet, formaté pour être accessible par les aides techniques +Parent accessible +Est utilisé pour notifier que le parent a changé +Valeur accessible +ሬዲዮ-ቁልፍ +ዶሴ `%s'ን ማስፈጠር አልተቻለም፦ +አዲስ ዶሴ ፍጠር +በመጫን ላይ... +Ne cúðe findan þone pæþ +Onlícnesgesceap ungecnáwen +لتنقيح أيّة عمليّة يطلب اسم التّطبيق أيضاً.رجاءً زوّد أيضاً بمعامل سطر أوامر --appname. +الملف الذي فيه سيحفظ تقرير الخلال +تعذّر تحديد نسق الملف %s +^إرسال تقرير خلل +ارسال تقريرا لك فقط +حفظ التقرير في ملف +التطبيق المنهار +الملف النواة +ﻻ شيء +اسم الموصول +اﻻسم +عنوان البريد الالكتروني للموصول +البريد اﻻلكتروني +الرزمة المحتوية على البرنامج +الرزمة +اصدارة الرزمة +اﻻصدارة +اسم ملف البرنامج المنهار +الملف +PID البرنامج المنهار +PID +الملف النواة من البرنامج +ملف النص الذي سيُضمن في التقرير +لم يمكن العثور على قطعة باسم %s عند %s +لم ينته gdb من تلقي معلومات التنقيح. +ا اقتل عملية gdb (ستكون مصفوفة تتبع السير منقوصة)؟ +لقد خرج gdb مسبقا +حفظ تتبع السير +الرجاء الانتظار حالما يحفظ باك بدي مصفوفة تتيع السير... +لم تحفظ مصفوفة تتبع السير في %s: +সংলাপ পৰিচয় প্রমাণ +সংলাপ সংক্রান্ত তথ্য +সংলাপ সংক্রান্ত সতর্কবাণী +সংলাপ সংকপ্রশ্ন +টনা আৰু এৰি দিয়া +বহুবাৰ টনা আৰু এৰি দিয়া +যোগ +কার্যকৰ +ডাঠ +বাতিল +সিডৰূপান্তৰ কৰা +কপি +কটা +মোছা +চলোৱা +বিচৰা +বিচৰা আৰু পৰিবৰতন কৰা +ফ্লপি +তলিলৈ @মূধচলৈ যোৱা +পিছলৈ যোৱা +তললৈ যোৱা +আগলৈ যোৱা +উপৰলৈ যোৱা +সহায় +ঘৰ +সূচিপত্র +টিফাই কৰা +সুষমভাবে জাস্টিফাই কৰা +বাঁও পিনৰ পৰা জাস্টিফাই কৰা +সোঁ পিনৰ পৰা জাসয় +ঠিক আছে +খোলা +আঠা দিয়া +পছন্দ +ছপা কৰা +ছপাৰ আগতে ছোৱা +বৈশিষ্ট +প্রস্থান +আ +সংৰক্ষিত কপি ব্যবহাৰ কৰা +সংৰক্ষণ +নতুন নামেৰে সংৰক্ষণ +ৰং বাছা +ফন্ট বাছা +উর +বানান-পৰীক্ষণ +ৰখা +মাজেৰে ৰেখা অকাঁ +মোছা ৰদ্দ কৰা +তলেৰে ৰেখা অকাঁ +হয় +১০০ ডাঙৰ কৰা +সৰু কৰা +সময়ৰক্ষক +সময়ৰক্ষক স্থগিত কৰা +আবর্জনা +আবর্জনা পূর্ণ +নম্বৰ^মিডি +মাইক্রোফোন +লাইন ইন +মেইল +মেইল গ্রহণ +মেইল প্রেৰণ +মেইলৰ উত্তৰ +মেইল আগলৈক্ত কৰা +কিতাপ ৰঙা +কিতাপ সেউজীয়া +কিতাপ নীলা +কিতাপ হালধীয়া +কিতাপ খোলা +একাধিক পূর্ণ কৰা +টেক্সট অবচ্ছেদ(Indent) +টেক্সট অবচ্ছেদ(Indent) ৰদ্দ কৰা +টেক্সট বুলেৰযুক্ত তালিকা +পৰিচয় প্রমাণ প্রক্রিয়া +(অঙ্কুর বাংলা প্রকল্পের তরফ থেকে) +ড্যাশার +ড্যাশার একটি প্রেডিকটিভ টেক্সট এন্ট %ld সেকেন্ডেেশন +%d অক্ষর আউটপুট +৫: +৬: +৭: +৮: +৯: +বর্ণ নির্বাচন: +বাটন নিয়ন্ত্রণ সেটাপ: +রং +নিয়ন্ত্রণের ধরণ: +অন্যান্য পছন্দসমূহ: +পারিপার্শ্বিক অবস্থান: +চালু করা হচ্ছে ও বন্ধ করা হচ্ছে: +টগল বাটন মোড: +প্রদর্শন সম্/b> +X/Y অক্ষ: +Y অক্ষ সম্বন্ধীয় পছন্দসমূহ: +অগ্রসর +বর্ণ +বর্ণ ডিউপর +বাটন নিয়ন্ত্রণ সেটাপ +বাটন মোড +বাটন: +স্বয়ংক্রীয়ভাবে রঙের স্কীম বদলাও +রং^ কিছু কপি কর (_স) +থামার পরে সমস্ত কিছু কপি কর +কাটো +আবর্তমান বাটন মোড +ড্যাশারসের অবস্থানে শুরুর জন্যে মধ্যম রেখার থেকে দুরত্ব: +নীচে/৩: +বাক্সের সীমা আঁকো +কবাটন নিয়ন্ত্রণ সম্বন্ধীয় পছন্দ সম্পাদন করুন +অন্য উইন্ডো-তে লেখো +আইট্র্যাকার মোপর এবং নিচ ব্যবহারেিত থাকলে, উ +কোঅর্ডিনেট সামনের বা পেছনের দিকে আবর্ত হবে। আর ডান এ নির্বাচিত মানটি নিশ্চিত করব +নতুন ফাইল +সাধারণ +y অংশটিকে যতগুলি পিক্সেল দিয়ে ঢাকা হবে +এক মাত্রিক মোড +ফাইয়িক বিরতি +ডান থেকে বামদিকে +ডান/৪ +ফাইল সংরক্ষণ করো +নতুন নামে ফাইল সংরক্ষণ +ফনস্থান দেখাও +গতি নির্দেশক স্লাইডার দেখাও +টুলবার দেখাও +থামলে কথা বোলো +গতি +বামরা হলে শুরু কোরো +স্পেস বার ব্যবহার করা হলে শুরু কোরো +মাউসের অবস্থান বদল করা হলম্প কোরো +উপর থেকে নীচে +প্রশিক্ষণ চলছে +ড্যাশারকে প্রশিক্ষণ দেওয়া হচ্ছে - অনুগ্এই কন্ট্রোলটি ব্যবহার করুন যদি আপনি আপনার লেটার বক্সের +আকার সমন্বয় করতে চান। মনে রাখবেন উচ্চ মান নির্বাচন +আপনার লেখার গতিকে কমিয়ে দিবে +খুব বড় +দর্শন +পরিচিতি (_প) +ফাইলের শেষে যট (_ড) +ড্যাশার ফন্টের সাইজ (_ড) +সম্পাদন (_ম) +ফন্ট সম্পাদন (_ট) +ফাইল (_ফ) +সহক্ষণ টেক্সট ইমপোর্ট করো (_ই) +পছন্দসমূহ (_প) +পুরোনো ফন্টে ফেরত যাও (_ফ) +অআইঈউঊ +এই মোডটি নির্বাচিত থাকলে, উপর এবং নিচ ব্যবহারে +ির্বাচন সীমা +কার্সার থেকে নির্বাচি +諾してみますか? +かしな認証ドメイン: %s +発行者: %s +IMAP サーバから予期しない応答がありました: %s +%s (ポート %s) へ接続できませんでした: %s +ニュース・ストアにフォルダを生成できません: 代わりに購読を申請して下さい。 +指定されたフォルダ名は不適切です: %s +작업중인 문서 내에 공개하기 민감한 정보가 들어 있다면, +이 버그 보고서를 보내지 않는 편이 좋을 것입니다. +꼭 영어로 보고서를 작성하십시>오. +버그를 알릴 제품이름이나 프로그램을 선택하십시오. +보고하려는 버그가 가장 >자주 보고되는 버그 중에 들어 있다면 잠시 기다려 주십시오. +버그가 이미 보고되어 있다면, 또 보고하지는 말아 주십시오. +버그버디가 제품 목록>을 갱신하는 동안 +기다려 주십시오. +프로세스 ID: +저장할 파일: +보고를 새 이름으로 저장하기... +저 +장: +sendmail 설정 +심각성: +프로그램 보기(_A) +디버깅 정보 보여주기(_D) +제품 보기(_P) +다음의 가장 많이 보고되는 버그 보여주기: +간단히 +시작 +중지 +또다른 >버그를 보내기 +요약 +프로그램이 올바르게 동작하지 않습니다(_A) +문서가 틀렸습니>다(_D) +번역이 틀렸습니다(_T) +받는 사람: +sendmail 직접 사용(_S) +버전: +업데이 +트 하지 않음(_D) +빠진 기능을 요청하기(_R) +업데이트(_U) +컴포넌트 +설명 +전자메 +일 정보 +완료됨 +gdb +소개 +메일 설정 +가장 잦은 +제품 +%2$d개중 %1$d개 ID +제>품명 +컴포넌트 +버그버디에서 '%s'을(를) 열 수 없습니다. +버그버디가 제대로 설치되었는지 확인하십시오. +버그버디는 지금 종료합니다. +버그버디에서 버그를 어디에 제출할 지에 관한 정보를 읽지 못했습니다. +버그버디가 제대로 설치되었는 지 확인하십시오. +@圖像/標籤框 +在警告對話視窗中,包圍標籤及圖像框線的闊度 +警告類型 +警告的類型 +警告按鈕 +出現在警告對話視窗中的按鈕 +顯示更多訊息(_D) +文字 +標籤中的文>字。 +對齊方式 +文字在標籤中排列時的對齊方式。它不會影響標籤元件本身在空位內排列 +的位置。該方面的資料請參考 GtkMisc::xalign。 +換行 +如選用本選項,當字句太長時會 +自動換行。 +游標位置 +游標目前的位置,以字元表示。 +選擇範圍 +從游標位置至選擇區 +域末端之間的字元數目。 +全部選取 +輸入法 +GConf 錯誤: +GConf 錯誤: %s +所有進一步的錯誤訊息只會在終端機中顯示。 +GConf 錯誤 +01/01/00, 01:00 上午 +1/1/00, 1:00 上午 + 1/ 1/00, 1:00 上午 +沒有選取任何圖像。 +請按下圖像來選取它。 +沒有選取任何東西 +預設 +圖示 +名稱 +沒有選取任何程式 +%s 文件 +不明 +選取一程式來開啟 %s 及類型為“%s”的檔案 +無法執行程式 +>無法找到‘%s’ +無法找到程式 +無法加入程式 +無法將程式加入程式資料庫中 +選取一個程 +式 +以其它應用程式開啟 +選取一程式來檢視它的描述。 +使用自訂的指令(_C) +瀏覽(_B)... +開啟(_O) +以其它程式開啟 %s及類型為“%s”的檔案: +加入(_A) +加入程式 +您可以按下取消來停止這次操作。 +資訊 +警告 +錯誤 +問題 +(無效的統一碼) +覆寫檔案 %s? +檔案已經存在。您確定要覆蓋它? +略過 +覆寫 +儲存 %s 時發生錯>誤。 +重試 +無法決定目的地 uri。 +無法決定 %s 的檔案格式 +請使用一個合適的檔案名 +稱後置字,或選擇一個檔案格式。 +儲存圖片時發生錯誤。 +列印 +預覽列印 +您確定要將 + %i 個檔案丟進回收筒? +無法存取回收筒。 +當刪除圖片 %s 時發生錯誤 +%i x %i 像素 + %s %i%% +原因:%s +載入 %s 的圖片失敗 +檔案(_F) +編輯(_E) +顯示(_V) +圖片(_I) +求助(_H) +新增視窗(_N) +開啟新視窗 +開啟(_O)... +開啟檔案 +開啟資料夾(_F)... +開啟資料夾 +關閉(_C) +關閉視窗 +偏好設定(_N) +Eye of GNOME 偏好設定 +內容(_C) +本應用程式的說明文件 +關於(_A) +關於本程式 +工具列(_T) +在目前的視窗中切換是否>顯示工具列 +狀態列(_S) +在目前的視窗中切換是否顯示狀態列 +儲存(_S) +另存新檔(_A) +復原(_U) +左右相反(_H) +上下顛倒(_V) +順時針方向旋轉(_R) +逆時針方向旋轉(_L) +180 度旋轉(_E) +刪除 +全螢幕(_F) +投影片(_S) +拉近(_Z) +拉遠(_O) +原來大小(_N) +最適大小(_F) +圖片資訊(_I) +在目前的視窗中切換是否顯示資訊窗格 +新增 +開啟 +關閉 +儲存 +復原 +右 +左 +拉近 +拉遠 +原來大小 +符合 +找不到使用者介面的描述。 +無法 +建立 Eye of GNOME 使用者介面 +是否同時開啟多個的個別視窗? +這樣將會同時開啟 %i 個視窗。您是否想以圖片集的方式開啟? +單一視窗 +圖片集 +找不到檔案 +無法開啟‘%s’ +حاشیهی تصویر/برچسب +عرض حاشیهی دور برچسب و تصویر در محاورهی آژیر +نوع آژیر +نوع آژیر +دکمههای آژیر +دکمههای نشان داده شده در محاورهی آژیر +نشان دادن _جزئیات بیشتر +متن +متن برچسب +تراز کردن +پیچش سطرها +اگر یک شود، اگر سطرها خیلی عریض شوند میپیچند. +موقعیت مکاننما +موقعیت فعلی مکاننمای درج در نویسهها. +موقعیت طرف دیگر انتخاب نسبت به مکاننما به نویسه. +انتخاب همه +روشهای ورودی +خطای GConf: + %s +خطای GConf: %s +همهی خطاهای بعدی فقط روی پایانه نمایش مییابند. +خطای GConf +تصویری انتخاب نشد. +برای انتخاب یک تصویر باید رویش کلیک کنید. +انتخابی انجام نشد +پیشفرضنام +برنامهای انتخاب نشد +%s نوشتار +نامعلوم +برنامهای برای باز کردن %s و دیگر پروندههای از نوع "%s" انتخاب کنید +برنامه اجرا نشد +'%s' پیدا نشد +برنامه پیدا نشد +افزودن برنامه ممکن نیست +افزودن برنامه به پایگاه دادهی برنامه ممکن نیست +یک برنامه انتخاب کنید +باز کردن با +برنامهای برای باز کردن %s و دیگر پروندههای از نوع "%s" انتخاب کنید _مرور... باز کردن %s و دیگر پروندههای نوع "%s" با: +_افزودن +افزودن برنامه +میتوانید با کلیک کردن روی انصراف، این عملیات را متوقف کنید. +اطلاعات +هشدار +خطا +سؤال + (یونیکد نامعتبر) \ No newline at end of file