OSDN Git Service

Replacing FloatMath native implementation with calls to Math
authorNick Kralevich <nnk@google.com>
Sat, 18 Oct 2014 21:10:46 +0000 (14:10 -0700)
committerNeil Fuller <nfuller@google.com>
Mon, 20 Oct 2014 08:24:11 +0000 (09:24 +0100)
On modern versions of Android running in AOT mode
FloatMath is slower than Math. Calls to Math.sqrt(),
etc. are replaced by intrinsics which can be as small
as a single CPU opcode.

When running in interpreted mode the new
implementation is unfortunately slower, but I'm
judging this acceptable and likely to be improved
over time. This change saves a small amount of native
code.

Example timings:

Mako AOSP AOT:

Method: Original / New / Direct call to Math
ceil: 596ns / 146.ns / 111ns
sqrt: 694ns / 56ns / 25ns

Mako AOSP interpreted:

Method: Original / New / Direct call to Math
ceil: 1900ns / 2307ns / 1485ns
sqrt: 1998ns / 2603ns / 1788ns

Other calls Mako AOT:

Method: Original / New
cos: 635ns / 270ns
exp: 566ns / 324ns
floor: 604ns / 150ns
hypot: 631ns / 232ns
pow: 936ns / 643ns
sin: 641ns / 299ns

The advice to use Math directly, in preference to
FloatMath, is still good. FloatMath will be deprecated
separately.

Bug: https://code.google.com/p/android/issues/detail?id=36199
Change-Id: If07fcbd78543d13bc6d75f9743f999860e8d58d7

core/java/android/util/FloatMath.java
core/jni/Android.mk
core/jni/AndroidRuntime.cpp
core/jni/android_util_FloatMath.cpp [deleted file]
core/tests/benchmarks/src/android/util/FloatMathBenchmark.java [new file with mode: 0644]

index 0ffd5bd..90e0636 100644 (file)
 package android.util;
 
 /**
- * Math routines similar to those found in {@link java.lang.Math}. On
- * versions of Android with a JIT, these are significantly slower than
- * the equivalent {@code Math} functions, which should be used in preference
- * to these.
+ * Math routines similar to those found in {@link java.lang.Math}.
+ *
+ * <p>Historically these methods were faster than the equivalent double-based
+ * {@link java.lang.Math} methods. On versions of Android with a JIT they
+ * became slower and have since been re-implemented to wrap calls to
+ * {@link java.lang.Math}. {@link java.lang.Math} should be used in
+ * preference.
  */
 public class FloatMath {
 
@@ -34,7 +37,9 @@ public class FloatMath {
      * @param value to be converted
      * @return the floor of value
      */
-    public static native float floor(float value);
+    public static float floor(float value) {
+        return (float) Math.floor(value);
+    }
 
     /**
      * Returns the float conversion of the most negative (i.e. closest to
@@ -43,7 +48,9 @@ public class FloatMath {
      * @param value to be converted
      * @return the ceiling of value
      */
-    public static native float ceil(float value);
+    public static float ceil(float value) {
+        return (float) Math.ceil(value);
+    }
 
     /**
      * Returns the closest float approximation of the sine of the argument.
@@ -51,7 +58,9 @@ public class FloatMath {
      * @param angle to compute the cosine of, in radians
      * @return the sine of angle
      */
-    public static native float sin(float angle);
+    public static float sin(float angle) {
+        return (float) Math.sin(angle);
+    }
 
     /**
      * Returns the closest float approximation of the cosine of the argument.
@@ -59,7 +68,9 @@ public class FloatMath {
      * @param angle to compute the cosine of, in radians
      * @return the cosine of angle
      */
-    public static native float cos(float angle);
+    public static float cos(float angle) {
+        return (float) Math.cos(angle);
+    }
 
     /**
      * Returns the closest float approximation of the square root of the
@@ -68,7 +79,9 @@ public class FloatMath {
      * @param value to compute sqrt of
      * @return the square root of value
      */
-    public static native float sqrt(float value);
+    public static float sqrt(float value) {
+        return (float) Math.sqrt(value);
+    }
 
     /**
      * Returns the closest float approximation of the raising "e" to the power
@@ -77,7 +90,9 @@ public class FloatMath {
      * @param value to compute the exponential of
      * @return the exponential of value
      */
-    public static native float exp(float value);
+    public static float exp(float value) {
+        return (float) Math.exp(value);
+    }
 
     /**
      * Returns the closest float approximation of the result of raising {@code
@@ -87,7 +102,9 @@ public class FloatMath {
      * @param y the exponent of the operation.
      * @return {@code x} to the power of {@code y}.
      */
-    public static native float pow(float x, float y);
+    public static float pow(float x, float y) {
+        return (float) Math.pow(x, y);
+    }
 
     /**
      * Returns {@code sqrt(}<i>{@code x}</i><sup>{@code 2}</sup>{@code +} <i>
@@ -97,5 +114,7 @@ public class FloatMath {
      * @param y a float number
      * @return the hypotenuse
      */
-    public static native float hypot(float x, float y);
+    public static float hypot(float x, float y) {
+        return (float) Math.hypot(x, y);
+    }
 }
index 5d10f3c..9b3b091 100644 (file)
@@ -80,7 +80,6 @@ LOCAL_SRC_FILES:= \
        android_util_Binder.cpp \
        android_util_EventLog.cpp \
        android_util_Log.cpp \
-       android_util_FloatMath.cpp \
        android_util_Process.cpp \
        android_util_StringBlock.cpp \
        android_util_XmlBlock.cpp \
index 0d2cdb9..796a0c3 100644 (file)
@@ -90,8 +90,6 @@ extern int register_android_media_AudioTrack(JNIEnv *env);
 extern int register_android_media_JetPlayer(JNIEnv *env);
 extern int register_android_media_ToneGenerator(JNIEnv *env);
 
-extern int register_android_util_FloatMath(JNIEnv* env);
-
 namespace android {
 
 /*
@@ -1229,7 +1227,6 @@ static const RegJNIRec gRegJNI[] = {
     REG_JNI(register_android_os_SystemClock),
     REG_JNI(register_android_util_EventLog),
     REG_JNI(register_android_util_Log),
-    REG_JNI(register_android_util_FloatMath),
     REG_JNI(register_android_content_AssetManager),
     REG_JNI(register_android_content_StringBlock),
     REG_JNI(register_android_content_XmlBlock),
diff --git a/core/jni/android_util_FloatMath.cpp b/core/jni/android_util_FloatMath.cpp
deleted file mode 100644 (file)
index 73b7a6f..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-#include "jni.h"
-#include <android_runtime/AndroidRuntime.h>
-#include <math.h>
-#include <float.h>
-#include "SkTypes.h"
-
-class MathUtilsGlue {
-public:
-    static float FloorF(JNIEnv* env, jobject clazz, float x) {
-        return floorf(x);
-    }
-    
-    static float CeilF(JNIEnv* env, jobject clazz, float x) {
-        return ceilf(x);
-    }
-    
-    static float SinF(JNIEnv* env, jobject clazz, float x) {
-        return sinf(x);
-    }
-    
-    static float CosF(JNIEnv* env, jobject clazz, float x) {
-        return cosf(x);
-    }
-    
-    static float SqrtF(JNIEnv* env, jobject clazz, float x) {
-        return sqrtf(x);
-    }
-
-    static float ExpF(JNIEnv* env, jobject clazz, float x) {
-        return expf(x);
-    }
-
-    static float PowF(JNIEnv* env, jobject clazz, float x, float y) {
-        return powf(x, y);
-    }
-
-    static float HypotF(JNIEnv* env, jobject clazz, float x, float y) {
-        return hypotf(x, y);
-    }
-};
-
-static JNINativeMethod gMathUtilsMethods[] = {
-    {"floor", "(F)F", (void*) MathUtilsGlue::FloorF},
-    {"ceil", "(F)F", (void*) MathUtilsGlue::CeilF},
-    {"sin", "(F)F", (void*) MathUtilsGlue::SinF},
-    {"cos", "(F)F", (void*) MathUtilsGlue::CosF},
-    {"sqrt", "(F)F", (void*) MathUtilsGlue::SqrtF},
-    {"exp", "(F)F", (void*) MathUtilsGlue::ExpF},
-    {"pow", "(FF)F", (void*) MathUtilsGlue::PowF},
-    {"hypot", "(FF)F", (void*) MathUtilsGlue::HypotF},
-};
-
-int register_android_util_FloatMath(JNIEnv* env)
-{
-    int result = android::AndroidRuntime::registerNativeMethods(env,
-                                            "android/util/FloatMath",
-                                            gMathUtilsMethods,
-                                            SK_ARRAY_COUNT(gMathUtilsMethods));
-    return result;
-}
-
diff --git a/core/tests/benchmarks/src/android/util/FloatMathBenchmark.java b/core/tests/benchmarks/src/android/util/FloatMathBenchmark.java
new file mode 100644 (file)
index 0000000..2858128
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014 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.util;
+
+import com.google.caliper.Param;
+import com.google.caliper.Runner;
+import com.google.caliper.SimpleBenchmark;
+
+import android.util.FloatMath;
+
+public class FloatMathBenchmark extends SimpleBenchmark {
+
+    public float timeFloatMathCeil(int reps) {
+        // Keep an answer so we don't optimize the method call away.
+        float f = 0.0f;
+        for (int i = 0; i < reps; i++) {
+            f += FloatMath.ceil(100.123f);
+        }
+        return f;
+    }
+
+    public float timeFloatMathCeil_math(int reps) {
+        // Keep an answer so we don't optimize the method call away.
+        float f = 0.0f;
+        for (int i = 0; i < reps; i++) {
+            f += (float) Math.ceil(100.123f);
+        }
+        return f;
+    }
+
+    public float timeFloatMathCos(int reps) {
+        // Keep an answer so we don't optimize the method call away.
+        float f = 0.0f;
+        for (int i = 0; i < reps; i++) {
+            f += FloatMath.cos(100.123f);
+        }
+        return f;
+    }
+
+    public float timeFloatMathExp(int reps) {
+        // Keep an answer so we don't optimize the method call away.
+        float f = 0.0f;
+        for (int i = 0; i < reps; i++) {
+            f += FloatMath.exp(100.123f);
+        }
+        return f;
+    }
+
+    public float timeFloatMathFloor(int reps) {
+        // Keep an answer so we don't optimize the method call away.
+        float f = 0.0f;
+        for (int i = 0; i < reps; i++) {
+            f += FloatMath.floor(100.123f);
+        }
+        return f;
+    }
+
+    public float timeFloatMathHypot(int reps) {
+        // Keep an answer so we don't optimize the method call away.
+        float f = 0.0f;
+        for (int i = 0; i < reps; i++) {
+            f += FloatMath.hypot(100.123f, 100.123f);
+        }
+        return f;
+    }
+
+    public float timeFloatMathPow(int reps) {
+        // Keep an answer so we don't optimize the method call away.
+        float f = 0.0f;
+        for (int i = 0; i < reps; i++) {
+            f += FloatMath.pow(10.123f, 10.123f);
+        }
+        return f;
+    }
+
+    public float timeFloatMathSin(int reps) {
+        // Keep an answer so we don't optimize the method call away.
+        float f = 0.0f;
+        for (int i = 0; i < reps; i++) {
+            f += FloatMath.sin(100.123f);
+        }
+        return f;
+    }
+
+    public float timeFloatMathSqrt(int reps) {
+        // Keep an answer so we don't optimize the method call away.
+        float f = 0.0f;
+        for (int i = 0; i < reps; i++) {
+            f += FloatMath.sqrt(100.123f);
+        }
+        return f;
+    }
+
+    public float timeFloatMathSqrt_math(int reps) {
+        // Keep an answer so we don't optimize the method call away.
+        float f = 0.0f;
+        for (int i = 0; i < reps; i++) {
+            f += (float) Math.sqrt(100.123f);
+        }
+        return f;
+    }
+
+}