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

Commit e81851f5 authored by Miguel Aranda's avatar Miguel Aranda Committed by Gerrit Code Review
Browse files

Merge "Additional libcore benchmarks."

parents 6e5f312d 4ca7bb87
Loading
Loading
Loading
Loading
+220 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed 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 android.libcore;

import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.test.suitebuilder.annotation.LargeTest;

import androidx.test.runner.AndroidJUnit4;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.math.BigInteger;

/**
 * Tries to measure important BigInteger operations across a variety of BigInteger sizes. Note that
 * BigInteger implementations commonly need to use wildly different algorithms for different sizes,
 * so relative performance may change substantially depending on the size of the integer. This is
 * not structured as a proper benchmark; just run main(), e.g. with vogar
 * libcore/benchmarks/src/benchmarks/BigIntegerBenchmark.java.
 */
@RunWith(AndroidJUnit4.class)
@LargeTest
public class BigIntegerPerfTest {
    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();

    // A simple sum of products computation, mostly so we can check timing in the
    // absence of any division. Computes the sum from 1 to n of ((10^prec) << 30) + 1)^2,
    // repeating the multiplication, but not addition of 1, each time through the loop.
    // Check the last few bits of the result as we go. Assumes n < 2^30.
    // Note that we're actually squaring values in computing the product.
    // That affects the algorithm used by some implementations.
    private static void inner(int n, int prec) {
        BigInteger big = BigInteger.TEN.pow(prec).shiftLeft(30).add(BigInteger.ONE);
        BigInteger sum = BigInteger.ZERO;
        for (int i = 0; i < n; ++i) {
            sum = sum.add(big.multiply(big));
        }
        if (sum.and(BigInteger.valueOf(0x3fffffff)).intValue() != n) {
            throw new AssertionError(
                    "inner() got " + sum.and(BigInteger.valueOf(0x3fffffff)) + " instead of " + n);
        }
    }

    // Execute the above rep times, optionally timing it.
    @Test
    public void repeatInner() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            for (int i = 10; i <= 10_000; i *= 10) {
                inner(100, i);
            }
        }
    }

    // Approximate the sum of the first 1000 terms of the harmonic series (sum of 1/m as m
    // goes from 1 to n) to about prec digits. The result has an implicit decimal point
    // prec digits from the right.
    private static BigInteger harmonic1000(int prec) {
        BigInteger scaledOne = BigInteger.TEN.pow(prec);
        BigInteger sum = BigInteger.ZERO;
        for (int i = 1; i <= 1000; ++i) {
            sum = sum.add(scaledOne.divide(BigInteger.valueOf(i)));
        }
        return sum;
    }

    // Execute the above rep times, optionally timing it.
    // Check results for equality, and print one, to compaare against reference.
    @Test
    public void repeatHarmonic1000() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            for (int i = 5; i <= 5_000; i *= 10) {
                BigInteger refRes = harmonic1000(i);
                BigInteger newRes = harmonic1000(i);
                if (!newRes.equals(refRes)) {
                    throw new AssertionError(newRes + " != " + refRes);
                }
                if (i >= 50
                        && !refRes.toString()
                                .startsWith("748547086055034491265651820433390017652167916970")) {
                    throw new AssertionError("harmanic(" + i + ") incorrectly produced " + refRes);
                }
            }
        }
    }

    // Repeatedly execute just the base conversion from the last test, allowing
    // us to time and check it for consistency as well.
    @Test
    public void repeatToString() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            for (int i = 5; i <= 5_000; i *= 10) {
                BigInteger refRes = harmonic1000(i);
                String refString = refRes.toString();
                // Disguise refRes to avoid compiler optimization issues.
                BigInteger newRes = refRes.shiftLeft(30).add(BigInteger.valueOf(i)).shiftRight(30);
                // The time-consuming part:
                String newString = newRes.toString();
            }
        }
    }

    // Compute base^exp, where base and result are scaled/multiplied by scaleBy to make them
    // integers. exp >= 0 .
    private static BigInteger myPow(BigInteger base, int exp, BigInteger scaleBy) {
        if (exp == 0) {
            return scaleBy; // Return one.
        } else if ((exp & 1) != 0) {
            BigInteger tmp = myPow(base, exp - 1, scaleBy);
            return tmp.multiply(base).divide(scaleBy);
        } else {
            BigInteger tmp = myPow(base, exp / 2, scaleBy);
            return tmp.multiply(tmp).divide(scaleBy);
        }
    }

    // Approximate e by computing (1 + 1/n)^n to prec decimal digits.
    // This isn't necessarily a very good approximation to e.
    // Return the result, scaled by 10^prec.
    private static BigInteger eApprox(int n, int prec) {
        BigInteger scaledOne = BigInteger.TEN.pow(prec);
        BigInteger base = scaledOne.add(scaledOne.divide(BigInteger.valueOf(n)));
        return myPow(base, n, scaledOne);
    }

    // Repeatedly execute and check the above, printing one of the results
    // to compare to reference.
    @Test
    public void repeatEApprox() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            for (int i = 10; i <= 10_000; i *= 10) {
                BigInteger refRes = eApprox(100_000, i);
                BigInteger newRes = eApprox(100_000, i);
                if (!newRes.equals(refRes)) {
                    throw new AssertionError(newRes + " != " + refRes);
                }
                if (i >= 10 && !refRes.toString().startsWith("271826")) {
                    throw new AssertionError(
                            "eApprox(" + 100_000 + "," + i + ") incorrectly produced " + refRes);
                }
            }
        }
    }

    // Test / time modPow()
    @Test
    public void repeatModPow() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            for (int i = 5; i <= 500; i *= 10) {
                BigInteger odd1 = BigInteger.TEN.pow(i / 2).add(BigInteger.ONE);
                BigInteger odd2 = BigInteger.TEN.pow(i / 2).add(BigInteger.valueOf(17));
                BigInteger product = odd1.multiply(odd2);
                BigInteger exponent = BigInteger.TEN.pow(i / 2 - 1);
                BigInteger base = BigInteger.TEN.pow(i / 4);
                BigInteger newRes = base.modPow(exponent, product);
                if (!newRes.mod(odd1).equals(base.modPow(exponent, odd1))) {
                    throw new AssertionError(
                            "ModPow() result incorrect mod odd1:"
                                    + odd1
                                    + "; lastRes.mod(odd1)="
                                    + newRes.mod(odd1)
                                    + " vs. "
                                    + "base.modPow(exponent, odd1)="
                                    + base.modPow(exponent, odd1)
                                    + " base="
                                    + base
                                    + " exponent="
                                    + exponent);
                }
                if (!newRes.mod(odd2).equals(base.modPow(exponent, odd2))) {
                    throw new AssertionError("ModPow() result incorrect mod odd2");
                }
            }
        }
    }

    // Test / time modInverse()
    @Test
    public void repeatModInverse() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            for (int i = 10; i <= 10_000; i *= 10) {
                BigInteger odd1 = BigInteger.TEN.pow(i / 2).add(BigInteger.ONE);
                BigInteger odd2 = BigInteger.TEN.pow(i / 2).add(BigInteger.valueOf(17));
                BigInteger product = odd1.multiply(odd2);
                BigInteger arg = BigInteger.ONE.shiftLeft(i / 4);
                BigInteger lastRes = null;
                BigInteger newRes = arg.modInverse(product);
                lastRes = newRes;
                if (!lastRes.mod(odd1).equals(arg.modInverse(odd1))) {
                    throw new AssertionError("ModInverse() result incorrect mod odd1");
                }
                if (!lastRes.mod(odd2).equals(arg.modInverse(odd2))) {
                    throw new AssertionError("ModInverse() result incorrect mod odd2");
                }
            }
        }
    }
}
+107 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed 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 android.libcore;

import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.test.suitebuilder.annotation.LargeTest;

import androidx.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Random;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

@RunWith(AndroidJUnit4.class)
@LargeTest
public final class BufferedZipFilePerfTest {
    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();

    int[] mReadSize = new int[] {4, 32, 128};
    int[] mCompressedSize = new int[] {128, 1024, 8192, 65536};
    private File mFile;

    @Before
    public void setUp() throws Exception {
        mFile = File.createTempFile("BufferedZipFilePerfTest", ".zip");
        mFile.deleteOnExit();
        Random random = new Random(0);
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(mFile));
        for (int i = 0; i < mCompressedSize.length; i++) {
            byte[] data = new byte[8192];
            out.putNextEntry(new ZipEntry("entry.data" + mCompressedSize[i]));
            int written = 0;
            while (written < mCompressedSize[i]) {
                random.nextBytes(data);
                int toWrite = Math.min(mCompressedSize[i] - written, data.length);
                out.write(data, 0, toWrite);
                written += toWrite;
            }
        }
        out.close();
    }

    @Test
    public void timeUnbufferedRead() throws Exception {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            for (int i = 0; i < mCompressedSize.length; i++) {
                for (int j = 0; j < mReadSize.length; j++) {
                    ZipFile zipFile = new ZipFile(mFile);
                    ZipEntry entry = zipFile.getEntry("entry.data" + mCompressedSize[i]);
                    InputStream in = zipFile.getInputStream(entry);
                    byte[] buffer = new byte[mReadSize[j]];
                    while (in.read(buffer) != -1) {
                        // Keep reading
                    }
                    in.close();
                    zipFile.close();
                }
            }
        }
    }

    @Test
    public void timeBufferedRead() throws Exception {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            for (int i = 0; i < mCompressedSize.length; i++) {
                for (int j = 0; j < mReadSize.length; j++) {
                    ZipFile zipFile = new ZipFile(mFile);
                    ZipEntry entry = zipFile.getEntry("entry.data" + mCompressedSize[i]);
                    InputStream in = new BufferedInputStream(zipFile.getInputStream(entry));
                    byte[] buffer = new byte[mReadSize[j]];
                    while (in.read(buffer) != -1) {
                        // Keep reading
                    }
                    in.close();
                    zipFile.close();
                }
            }
        }
    }
}
+59 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed 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 android.libcore;

import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.test.suitebuilder.annotation.LargeTest;

import androidx.test.runner.AndroidJUnit4;

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
@LargeTest
public class ClassLoaderResourcePerfTest {
    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();

    private static final String EXISTENT_RESOURCE = "java/util/logging/logging.properties";
    private static final String MISSING_RESOURCE = "missing_entry";

    @Test
    public void timeGetBootResource_hit() {
        ClassLoader currentClassLoader = getClass().getClassLoader();
        Assert.assertNotNull(currentClassLoader.getResource(EXISTENT_RESOURCE));

        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            currentClassLoader.getResource(EXISTENT_RESOURCE);
        }
    }

    @Test
    public void timeGetBootResource_miss() {
        ClassLoader currentClassLoader = getClass().getClassLoader();
        Assert.assertNull(currentClassLoader.getResource(MISSING_RESOURCE));

        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            currentClassLoader.getResource(MISSING_RESOURCE);
        }
    }
}
+1197 −0

File added.

Preview size limit exceeded, changes collapsed.

+169 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed 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 android.libcore;

import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.test.suitebuilder.annotation.LargeTest;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.Arrays;
import java.util.Collection;

@RunWith(Parameterized.class)
@LargeTest
public class DeepArrayOpsPerfTest {
    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();

    private Object[] mArray;
    private Object[] mArray2;

    @Parameterized.Parameter(0)
    public int mArrayLength;

    @Parameterized.Parameters(name = "mArrayLength({0})")
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {{1}, {4}, {16}, {32}, {2048}});
    }

    @Before
    public void setUp() throws Exception {
        mArray = new Object[mArrayLength * 14];
        mArray2 = new Object[mArrayLength * 14];
        for (int i = 0; i < mArrayLength; i += 14) {
            mArray[i] = new IntWrapper(i);
            mArray2[i] = new IntWrapper(i);

            mArray[i + 1] = new16ElementObjectmArray();
            mArray2[i + 1] = new16ElementObjectmArray();

            mArray[i + 2] = new boolean[16];
            mArray2[i + 2] = new boolean[16];

            mArray[i + 3] = new byte[16];
            mArray2[i + 3] = new byte[16];

            mArray[i + 4] = new char[16];
            mArray2[i + 4] = new char[16];

            mArray[i + 5] = new short[16];
            mArray2[i + 5] = new short[16];

            mArray[i + 6] = new float[16];
            mArray2[i + 6] = new float[16];

            mArray[i + 7] = new long[16];
            mArray2[i + 7] = new long[16];

            mArray[i + 8] = new int[16];
            mArray2[i + 8] = new int[16];

            mArray[i + 9] = new double[16];
            mArray2[i + 9] = new double[16];

            // SubmArray types are concrete objects.
            mArray[i + 10] = new16ElementArray(String.class, String.class);
            mArray2[i + 10] = new16ElementArray(String.class, String.class);

            mArray[i + 11] = new16ElementArray(Integer.class, Integer.class);
            mArray2[i + 11] = new16ElementArray(Integer.class, Integer.class);

            // SubmArray types is an interface.
            mArray[i + 12] = new16ElementArray(CharSequence.class, String.class);
            mArray2[i + 12] = new16ElementArray(CharSequence.class, String.class);

            mArray[i + 13] = null;
            mArray2[i + 13] = null;
        }
    }

    @Test
    public void deepHashCode() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            Arrays.deepHashCode(mArray);
        }
    }

    @Test
    public void deepEquals() {
        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            Arrays.deepEquals(mArray, mArray2);
        }
    }

    private static Object[] new16ElementObjectmArray() {
        Object[] array = new Object[16];
        for (int i = 0; i < 16; ++i) {
            array[i] = new IntWrapper(i);
        }

        return array;
    }

    @SuppressWarnings("unchecked")
    private static <T, V> T[] new16ElementArray(Class<T> mArrayType, Class<V> type)
            throws Exception {
        T[] array = (T[]) Array.newInstance(type, 16);
        if (!mArrayType.isAssignableFrom(type)) {
            throw new IllegalArgumentException(mArrayType + " is not assignable from " + type);
        }

        Constructor<V> constructor = type.getDeclaredConstructor(String.class);
        for (int i = 0; i < 16; ++i) {
            array[i] = (T) constructor.newInstance(String.valueOf(i + 1000));
        }

        return array;
    }

    /**
     * A class that provides very basic equals() and hashCode() operations and doesn't resort to
     * memoization tricks like {@link java.lang.Integer}.
     *
     * <p>Useful for providing equal objects that aren't the same (a.equals(b) but a != b).
     */
    public static final class IntWrapper {
        private final int mWrapped;

        public IntWrapper(int wrap) {
            mWrapped = wrap;
        }

        @Override
        public int hashCode() {
            return mWrapped;
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof IntWrapper)) {
                return false;
            }

            return ((IntWrapper) o).mWrapped == this.mWrapped;
        }
    }
}
Loading