Cache least recently used output buffer.

- reduce memory allocations especially when Xml is serialized inside a
tight inner loop,
- performance slightly improved.

BEFORE:
100 packages:
[2/4] android.util.XmlPerfTest#timeWrite_Binary: PASSED (10.516s)
	timeWrite_Binary_mean: 674979
	threadAllocCount_mean: 59
	timeWrite_Binary_min: 671194
	timeWrite_Binary_median: 674798
	timeWrite_Binary_standardDeviation: 3658
	threadAllocSize_mean: 530176
10 packages:
[2/4] android.util.XmlPerfTest#timeWrite_Binary: PASSED (10.542s)
	timeWrite_Binary_mean: 71113
	threadAllocCount_mean: 56
	timeWrite_Binary_min: 70201
	timeWrite_Binary_median: 71237
	timeWrite_Binary_standardDeviation: 692
	threadAllocSize_mean: 59136
1 package:
[2/4] android.util.XmlPerfTest#timeWrite_Binary: PASSED (10.091s)
	timeWrite_Binary_mean: 15698
	threadAllocCount_mean: 56
	timeWrite_Binary_min: 15072
	timeWrite_Binary_median: 15677
	timeWrite_Binary_standardDeviation: 410
	threadAllocSize_mean: 6439

AFTER:
100 packages:
[1/1] android.util.XmlPerfTest#timeWrite_Binary: PASSED (10.221s)
	timeWrite_Binary_mean: 605862
	threadAllocCount_mean: 53
	timeWrite_Binary_min: 602353
	timeWrite_Binary_median: 604424
	timeWrite_Binary_standardDeviation: 4972
	threadAllocSize_mean: 492748
10 packages:
[1/1] android.util.XmlPerfTest#timeWrite_Binary: PASSED (10.575s)
	timeWrite_Binary_mean: 58710
	threadAllocCount_mean: 50
	timeWrite_Binary_min: 57551
	timeWrite_Binary_median: 57798
	timeWrite_Binary_standardDeviation: 2129
	threadAllocSize_mean: 21696
1 package:
[1/1] android.util.XmlPerfTest#timeWrite_Binary: PASSED (10.151s)
	timeWrite_Binary_mean: 11576
	threadAllocCount_mean: 50
	timeWrite_Binary_min: 11315
	timeWrite_Binary_median: 11381
	timeWrite_Binary_standardDeviation: 431
	threadAllocSize_mean: 4336

Test: atest FastDataPerfTest XmlPerfTest
Bug: 195994150
Fixes: 195994150

Merged-In: Ieedd27a676d718967c2fac30bc48a68ae636a180
Change-Id: Ieedd27a676d718967c2fac30bc48a68ae636a180
This commit is contained in:
Alex Buynytskyy
2021-08-10 11:20:56 -07:00
committed by Jeff Sharkey
parent 8a3803d401
commit 2be5b4c118
3 changed files with 64 additions and 8 deletions

View File

@@ -64,9 +64,13 @@ public class FastDataPerfTest {
final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
os.reset();
final FastDataOutput out = new FastDataOutput(os, BUFFER_SIZE);
doWrite(out);
out.flush();
final FastDataOutput out = FastDataOutput.obtain(os);
try {
doWrite(out);
out.flush();
} finally {
out.release();
}
}
}

View File

@@ -124,7 +124,7 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer {
throw new UnsupportedOperationException();
}
mOut = new FastDataOutput(os, BUFFER_SIZE);
mOut = FastDataOutput.obtain(os);
mOut.write(PROTOCOL_MAGIC_VERSION_0);
mTagCount = 0;
@@ -138,7 +138,9 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer {
@Override
public void flush() throws IOException {
mOut.flush();
if (mOut != null) {
mOut.flush();
}
}
@Override
@@ -157,6 +159,9 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer {
public void endDocument() throws IOException {
mOut.writeByte(END_DOCUMENT | TYPE_NULL);
flush();
mOut.release();
mOut = null;
}
@Override

View File

@@ -30,6 +30,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
/**
* Optimized implementation of {@link DataOutput} which buffers data in memory
@@ -41,23 +42,26 @@ import java.util.Objects;
public class FastDataOutput implements DataOutput, Flushable, Closeable {
private static final int MAX_UNSIGNED_SHORT = 65_535;
private static final int BUFFER_SIZE = 32_768;
private static AtomicReference<FastDataOutput> sOutCache = new AtomicReference<>();
private final VMRuntime mRuntime;
private final OutputStream mOut;
private final byte[] mBuffer;
private final long mBufferPtr;
private final int mBufferCap;
private OutputStream mOut;
private int mBufferPos;
/**
* Values that have been "interned" by {@link #writeInternedUTF(String)}.
*/
private HashMap<String, Short> mStringRefs = new HashMap<>();
private final HashMap<String, Short> mStringRefs = new HashMap<>();
public FastDataOutput(@NonNull OutputStream out, int bufferSize) {
mRuntime = VMRuntime.getRuntime();
mOut = Objects.requireNonNull(out);
if (bufferSize < 8) {
throw new IllegalArgumentException();
}
@@ -65,6 +69,48 @@ public class FastDataOutput implements DataOutput, Flushable, Closeable {
mBuffer = (byte[]) mRuntime.newNonMovableArray(byte.class, bufferSize);
mBufferPtr = mRuntime.addressOf(mBuffer);
mBufferCap = mBuffer.length;
setOutput(out);
}
/**
* Create a new FastDataOutput object or retrieve one from cache.
*/
public static FastDataOutput obtain(@NonNull OutputStream out) {
FastDataOutput instance = sOutCache.getAndSet(null);
if (instance != null) {
instance.setOutput(out);
return instance;
}
return new FastDataOutput(out, BUFFER_SIZE);
}
/**
* Put a FastDataOutput object back into the cache.
* You must not touch the object after this call.
*/
public void release() {
if (mBufferPos > 0) {
throw new IllegalStateException("Lingering data, call flush() before releasing.");
}
mOut = null;
mBufferPos = 0;
mStringRefs.clear();
if (mBufferCap == BUFFER_SIZE) {
// Try to return to the cache.
sOutCache.compareAndSet(null, this);
}
}
/**
* Re-initializes the object for the new output.
*/
private void setOutput(@NonNull OutputStream out) {
mOut = Objects.requireNonNull(out);
mBufferPos = 0;
mStringRefs.clear();
}
private void drain() throws IOException {
@@ -83,6 +129,7 @@ public class FastDataOutput implements DataOutput, Flushable, Closeable {
@Override
public void close() throws IOException {
mOut.close();
release();
}
@Override