diff --git a/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java b/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java new file mode 100644 index 0000000000000..f3d73902414c7 --- /dev/null +++ b/apct-tests/perftests/core/src/android/os/LongArrayMultiStateCounterPerfTest.java @@ -0,0 +1,172 @@ +/* + * 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; + } + } +} diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java new file mode 100644 index 0000000000000..1a3b29dbc90e9 --- /dev/null +++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java @@ -0,0 +1,181 @@ +/* + * 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: + *
+ * // 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}
+ *
+ *
+ * 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);
+}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index f46aadfa3c3f0..e8e9d0539d35b 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -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",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index aa9995d5abf7c..5e11b6a427e56 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -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),
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
new file mode 100644
index 0000000000000..953902a2d7199
--- /dev/null
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -0,0 +1,144 @@
+/*
+ * 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