Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 19f8adfb authored by Alex Buynytskyy's avatar Alex Buynytskyy
Browse files

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

Change-Id: Ieedd27a676d718967c2fac30bc48a68ae636a180
parent 5cef861a
Loading
Loading
Loading
Loading
+7 −3
Original line number Diff line number Diff line
@@ -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);
            final FastDataOutput out = FastDataOutput.obtain(os);
            try {
                doWrite(out);
                out.flush();
            } finally {
                out.release();
            }
        }
    }

+7 −2
Original line number Diff line number Diff line
@@ -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,8 +138,10 @@ public final class BinaryXmlSerializer implements TypedXmlSerializer {

    @Override
    public void flush() throws IOException {
        if (mOut != null) {
            mOut.flush();
        }
    }

    @Override
    public void startDocument(@Nullable String encoding, @Nullable Boolean standalone)
@@ -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
+50 −3
Original line number Diff line number Diff line
@@ -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