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

Commit 7058a51d authored by Dmitri Plotnikov's avatar Dmitri Plotnikov Committed by Android (Google) Code Review
Browse files

Merge "Add LongArrayMultiStateCounter"

parents 80908af6 2f618d1b
Loading
Loading
Loading
Loading
+172 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.os;

import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;

import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.os.LongArrayMultiStateCounter;

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

@RunWith(AndroidJUnit4.class)
@LargeTest
public class LongArrayMultiStateCounterPerfTest {

    @Rule
    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();

    /**
     * A complete line-for-line reimplementation of
     * {@link }com.android.internal.os.CpuTimeInFreqMultiStateCounter}, only in Java instead of
     * native.
     */
    private static class TestLongArrayMultiStateCounter {
        private final int mStateCount;
        private final int mArrayLength;
        private int mCurrentState;
        private long mLastStateChangeTimestampMs;
        private long mLastUpdateTimestampMs;

        private static class State {
            private long mTimeInStateSinceUpdate;
            private long[] mCounter;
        }

        private final State[] mStates;
        private final long[] mLastTimeInFreq;
        private final long[] mDelta;

        TestLongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState,
                long timestampMs) {
            mStateCount = stateCount;
            mArrayLength = arrayLength;
            mCurrentState = initialState;
            mLastStateChangeTimestampMs = timestampMs;
            mLastUpdateTimestampMs = timestampMs;
            mStates = new State[stateCount];
            for (int i = 0; i < mStateCount; i++) {
                mStates[i] = new State();
                mStates[i].mCounter = new long[mArrayLength];
            }
            mLastTimeInFreq = new long[mArrayLength];
            mDelta = new long[mArrayLength];
        }

        public void setState(int state, long timestampMs) {
            if (timestampMs >= mLastStateChangeTimestampMs) {
                mStates[mCurrentState].mTimeInStateSinceUpdate +=
                        timestampMs - mLastStateChangeTimestampMs;
            } else {
                for (int i = 0; i < mStateCount; i++) {
                    mStates[i].mTimeInStateSinceUpdate = 0;
                }
            }
            mCurrentState = state;
            mLastStateChangeTimestampMs = timestampMs;
        }

        public void updateValue(long[] timeInFreq, long timestampMs) {
            setState(mCurrentState, timestampMs);

            if (timestampMs > mLastUpdateTimestampMs) {
                if (delta(mLastTimeInFreq, timeInFreq, mDelta)) {
                    long timeSinceUpdate = timestampMs - mLastUpdateTimestampMs;
                    for (int i = 0; i < mStateCount; i++) {
                        long timeInState = mStates[i].mTimeInStateSinceUpdate;
                        if (timeInState > 0) {
                            add(mStates[i].mCounter, mDelta, timeInState, timeSinceUpdate);
                            mStates[i].mTimeInStateSinceUpdate = 0;
                        }
                    }
                } else {
                    throw new RuntimeException();
                }
            } else if (timestampMs < mLastUpdateTimestampMs) {
                throw new RuntimeException();
            }
            System.arraycopy(timeInFreq, 0, mLastTimeInFreq, 0, mArrayLength);
            mLastUpdateTimestampMs = timestampMs;
        }

        private boolean delta(long[] timeInFreq1, long[] timeInFreq2, long[] delta) {
            if (delta.length != mArrayLength) {
                throw new RuntimeException();
            }

            boolean is_delta_valid = true;
            for (int i = 0; i < mStateCount; i++) {
                if (timeInFreq2[i] >= timeInFreq1[i]) {
                    delta[i] = timeInFreq2[i] - timeInFreq1[i];
                } else {
                    delta[i] = 0;
                    is_delta_valid = false;
                }
            }

            return is_delta_valid;
        }

        private void add(long[] counter, long[] delta, long numerator, long denominator) {
            if (numerator != denominator) {
                for (int i = 0; i < mArrayLength; i++) {
                    counter[i] += delta[i] * numerator / denominator;
                }
            } else {
                for (int i = 0; i < mArrayLength; i++) {
                    counter[i] += delta[i];
                }
            }
        }
    }

    @Test
    public void javaImplementation() {
        TestLongArrayMultiStateCounter counter =
                new TestLongArrayMultiStateCounter(2, 4, 0, 1000);
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        long time = 1000;
        long[] timeInFreq = {100, 200, 300, 400};
        while (state.keepRunning()) {
            counter.setState(1, time);
            counter.setState(0, time + 1000);
            counter.updateValue(timeInFreq, time + 2000);
            time += 10000;
        }
    }

    @Test
    public void nativeImplementation() {
        LongArrayMultiStateCounter counter = new LongArrayMultiStateCounter(2, 4, 0, 1000);
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        long time = 1000;
        LongArrayMultiStateCounter.LongArrayContainer timeInFreq =
                new LongArrayMultiStateCounter.LongArrayContainer(4);
        timeInFreq.setValues(new long[]{100, 200, 300, 400});
        while (state.keepRunning()) {
            counter.setState(1, time);
            counter.setState(0, time + 1000);
            counter.updateValues(timeInFreq, time + 2000);
            time += 10000;
        }
    }
}
+181 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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 com.android.internal.os;

import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;

import libcore.util.NativeAllocationRegistry;

/**
 * Performs per-state counting of multi-element values over time. The class' behavior is illustrated
 * by this example:
 * <pre>
 *   // At 0 ms, the state of the tracked object is 0
 *   counter.setState(0, 0);
 *
 *   // At 1000 ms, the state changes to 1
 *   counter.setState(1, 1000);
 *
 *   // At 3000 ms, the tracked values are updated to {100, 200}
 *   arrayContainer.setValues(new long[]{{30, 300}};
 *   counter.updateValues(arrayContainer, 3000);
 *
 *   // The values are distributed between states 0 and 1 according to the time
 *   // spent in those respective states. In this specific case, 1000 and 2000 ms.
 *   counter.getValues(arrayContainer, 0);
 *   // arrayContainer now has values {10, 100}
 *   counter.getValues(arrayContainer, 1);
 *   // arrayContainer now has values {20, 200}
 * </pre>
 *
 * The tracked values are expected to increase monotonically.
 *
 * @hide
 */
public class LongArrayMultiStateCounter {

    /**
     * Container for a native equivalent of a long[].
     */
    public static class LongArrayContainer {
        private static final NativeAllocationRegistry sRegistry =
                NativeAllocationRegistry.createMalloced(
                        LongArrayContainer.class.getClassLoader(), native_getReleaseFunc());

        private final long mNativeObject;
        private final int mLength;

        public LongArrayContainer(int length) {
            mLength = length;
            mNativeObject = native_init(length);
            sRegistry.registerNativeAllocation(this, mNativeObject);
        }

        /**
         * Copies the supplied values into the underlying native array.
         */
        public void setValues(long[] array) {
            if (array.length != mLength) {
                throw new IllegalArgumentException(
                        "Invalid array length: " + mLength + ", expected: " + mLength);
            }
            native_setValues(mNativeObject, array);
        }

        /**
         * Copies the underlying native array values to the supplied array.
         */
        public void getValues(long[] array) {
            if (array.length != mLength) {
                throw new IllegalArgumentException(
                        "Invalid array length: " + mLength + ", expected: " + mLength);
            }
            native_getValues(mNativeObject, array);
        }

        @CriticalNative
        private static native long native_init(int length);

        @CriticalNative
        private static native long native_getReleaseFunc();

        @FastNative
        private native void native_setValues(long nativeObject, long[] array);

        @FastNative
        private native void native_getValues(long nativeObject, long[] array);
    }

    private static final NativeAllocationRegistry sRegistry =
            NativeAllocationRegistry.createMalloced(
                    LongArrayMultiStateCounter.class.getClassLoader(), native_getReleaseFunc());

    private final int mStateCount;
    private final int mLength;
    private final long mNativeObject;

    public LongArrayMultiStateCounter(int stateCount, int arrayLength, int initialState,
            long timestampMs) {
        mStateCount = stateCount;
        mLength = arrayLength;
        mNativeObject = native_init(stateCount, arrayLength, initialState, timestampMs);
        sRegistry.registerNativeAllocation(this, mNativeObject);
    }

    /**
     * Sets the current state to the supplied value.
     */
    public void setState(int state, long timestampMs) {
        if (state < 0 || state >= mStateCount) {
            throw new IllegalArgumentException(
                    "State: " + state + ", outside the range: [0-" + mStateCount + "]");
        }
        native_setState(mNativeObject, state, timestampMs);
    }

    /**
     * Sets the new values.  The delta between the previously set values and these values
     * is distributed among the state according to the time the object spent in those states
     * since the previous call to updateValues.
     */
    public void updateValues(LongArrayContainer longArrayContainer, long timestampMs) {
        if (longArrayContainer.mLength != mLength) {
            throw new IllegalArgumentException(
                    "Invalid array length: " + longArrayContainer.mLength + ", expected: "
                            + mLength);
        }
        native_updateValues(mNativeObject, longArrayContainer.mNativeObject, timestampMs);
    }

    /**
     * Populates longArrayContainer with the accumulated counts for the specified state.
     */
    public void getCounts(LongArrayContainer longArrayContainer, int state) {
        if (state < 0 || state >= mStateCount) {
            throw new IllegalArgumentException(
                    "State: " + state + ", outside the range: [0-" + mStateCount + "]");
        }
        native_getCounts(mNativeObject, longArrayContainer.mNativeObject, state);
    }

    @Override
    public String toString() {
        return native_toString(mNativeObject);
    }

    @CriticalNative
    private static native long native_init(int stateCount, int arrayLength, int initialState,
            long timestampMs);

    @CriticalNative
    private static native long native_getReleaseFunc();

    @CriticalNative
    private static native void native_setState(long nativeObject, int state, long timestampMs);

    @CriticalNative
    private static native void native_updateValues(long nativeObject,
            long longArrayContainerNativeObject, long timestampMs);

    @CriticalNative
    private static native void native_getCounts(long nativeObject,
            long longArrayContainerNativeObject, int state);

    @FastNative
    private native String native_toString(long nativeObject);
}
+2 −0
Original line number Diff line number Diff line
@@ -216,6 +216,7 @@ cc_library_shared {
                "com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
                "com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp",
                "com_android_internal_os_KernelSingleUidTimeReader.cpp",
                "com_android_internal_os_LongArrayMultiStateCounter.cpp",
                "com_android_internal_os_Zygote.cpp",
                "com_android_internal_os_ZygoteCommandBuffer.cpp",
                "com_android_internal_os_ZygoteInit.cpp",
@@ -246,6 +247,7 @@ cc_library_shared {
                "av-types-aidl-cpp",
                "android.hardware.camera.device@3.2",
                "libandroidicu",
                "libbattery",
                "libbpf_android",
                "libnetdbpf",
                "libnetdutils",
+2 −0
Original line number Diff line number Diff line
@@ -203,6 +203,7 @@ extern int register_com_android_internal_os_KernelCpuTotalBpfMapReader(JNIEnv* e
extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
extern int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv* env);
extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env);
extern int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv* env);
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
@@ -1585,6 +1586,7 @@ static const RegJNIRec gRegJNI[] = {
        REG_JNI(register_com_android_internal_content_om_OverlayConfig),
        REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),
        REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
        REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter),
        REG_JNI(register_com_android_internal_os_Zygote),
        REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer),
        REG_JNI(register_com_android_internal_os_ZygoteInit),
+144 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.
 */

#include <nativehelper/ScopedPrimitiveArray.h>
#include <cstring>
#include "LongArrayMultiStateCounter.h"
#include "core_jni_helpers.h"

namespace android {

static jlong native_init(jint stateCount, jint arrayLength, jint initialState, jlong timestamp) {
    battery::LongArrayMultiStateCounter *counter =
            new battery::LongArrayMultiStateCounter(stateCount, initialState,
                                                    std::vector<uint64_t>(arrayLength), timestamp);
    return reinterpret_cast<jlong>(counter);
}

static void native_dispose(void *nativePtr) {
    battery::LongArrayMultiStateCounter *counter =
            reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
    delete counter;
}

static jlong native_getReleaseFunc() {
    return reinterpret_cast<jlong>(native_dispose);
}

static void native_setState(jlong nativePtr, jint state, jlong timestamp) {
    battery::LongArrayMultiStateCounter *counter =
            reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
    counter->setState(state, timestamp);
}

static void native_updateValues(jlong nativePtr, jlong longArrayContainerNativePtr,
                                jlong timestamp) {
    battery::LongArrayMultiStateCounter *counter =
            reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
    std::vector<uint64_t> *vector =
            reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr);

    counter->updateValue(*vector, timestamp);
}

static void native_getCounts(jlong nativePtr, jlong longArrayContainerNativePtr, jint state) {
    battery::LongArrayMultiStateCounter *counter =
            reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
    std::vector<uint64_t> *vector =
            reinterpret_cast<std::vector<uint64_t> *>(longArrayContainerNativePtr);

    *vector = counter->getCount(state);
}

static jobject native_toString(JNIEnv *env, jlong nativePtr, jobject self) {
    battery::LongArrayMultiStateCounter *counter =
            reinterpret_cast<battery::LongArrayMultiStateCounter *>(nativePtr);
    return env->NewStringUTF(counter->toString().c_str());
}

static jlong native_init_LongArrayContainer(jint length) {
    return reinterpret_cast<jlong>(new std::vector<uint64_t>(length));
}

static const JNINativeMethod g_LongArrayMultiStateCounter_methods[] = {
        // @CriticalNative
        {"native_init", "(IIIJ)J", (void *)native_init},
        // @CriticalNative
        {"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc},
        // @CriticalNative
        {"native_setState", "(JIJ)V", (void *)native_setState},
        // @CriticalNative
        {"native_updateValues", "(JJJ)V", (void *)native_updateValues},
        // @CriticalNative
        {"native_getCounts", "(JJI)V", (void *)native_getCounts},
        // @FastNative
        {"native_toString", "(J)Ljava/lang/String;", (void *)native_toString},
};

/////////////////////// LongArrayMultiStateCounter.LongArrayContainer ////////////////////////

static void native_dispose_LongArrayContainer(jlong nativePtr) {
    std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
    delete vector;
}

static jlong native_getReleaseFunc_LongArrayContainer() {
    return reinterpret_cast<jlong>(native_dispose_LongArrayContainer);
}

static void native_setValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr,
                                                jlongArray jarray) {
    std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
    ScopedLongArrayRO scopedArray(env, jarray);
    const uint64_t *array = reinterpret_cast<const uint64_t *>(scopedArray.get());
    uint8_t size = scopedArray.size();

    // Boundary checks are performed in the Java layer
    std::copy(array, array + size, vector->data());
}

static void native_getValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr,
                                                jlongArray jarray) {
    std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
    ScopedLongArrayRW scopedArray(env, jarray);

    // Boundary checks are performed in the Java layer
    std::copy(vector->data(), vector->data() + vector->size(), scopedArray.get());
}

static const JNINativeMethod g_LongArrayContainer_methods[] = {
        // @CriticalNative
        {"native_init", "(I)J", (void *)native_init_LongArrayContainer},
        // @CriticalNative
        {"native_getReleaseFunc", "()J", (void *)native_getReleaseFunc_LongArrayContainer},
        // @FastNative
        {"native_setValues", "(J[J)V", (void *)native_setValues_LongArrayContainer},
        // @FastNative
        {"native_getValues", "(J[J)V", (void *)native_getValues_LongArrayContainer},
};

int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv *env) {
    // 0 represents success, thus "|" and not "&"
    return RegisterMethodsOrDie(env, "com/android/internal/os/LongArrayMultiStateCounter",
                                g_LongArrayMultiStateCounter_methods,
                                NELEM(g_LongArrayMultiStateCounter_methods)) |
            RegisterMethodsOrDie(env,
                                 "com/android/internal/os/LongArrayMultiStateCounter"
                                 "$LongArrayContainer",
                                 g_LongArrayContainer_methods, NELEM(g_LongArrayContainer_methods));
}

} // namespace android
Loading