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

Commit 17c5bf61 authored by Miguel Aranda's avatar Miguel Aranda Committed by Automerger Merge Worker
Browse files

Merge "Additional libcore benchmarks." am: e81851f5 am: e981334c

parents 3f030742 e981334c
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