diff options
3 files changed, 111 insertions, 17 deletions
diff --git a/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java b/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java new file mode 100644 index 0000000..02d8f97 --- /dev/null +++ b/benchmarks/src/benchmarks/regression/DateIntervalFormatBenchmark.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2013 Google Inc. + * + * 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 benchmarks.regression; + +import com.google.caliper.SimpleBenchmark; + +import java.util.Locale; +import java.util.TimeZone; + +import static libcore.icu.DateIntervalFormat.*; + +public class DateIntervalFormatBenchmark extends SimpleBenchmark { + public void timeDateIntervalFormat_formatDateRange_DATE(int reps) throws Exception { + Locale l = Locale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY; + + for (int rep = 0; rep < reps; ++rep) { + formatDateRange(l, utc, 0L, 0L, flags); + } + } + + public void timeDateIntervalFormat_formatDateRange_TIME(int reps) throws Exception { + Locale l = Locale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + int flags = FORMAT_SHOW_TIME | FORMAT_24HOUR; + + for (int rep = 0; rep < reps; ++rep) { + formatDateRange(l, utc, 0L, 0L, flags); + } + } + + public void timeDateIntervalFormat_formatDateRange_DATE_TIME(int reps) throws Exception { + Locale l = Locale.US; + TimeZone utc = TimeZone.getTimeZone("UTC"); + int flags = FORMAT_SHOW_DATE | FORMAT_SHOW_WEEKDAY | FORMAT_SHOW_TIME | FORMAT_24HOUR; + + for (int rep = 0; rep < reps; ++rep) { + formatDateRange(l, utc, 0L, 0L, flags); + } + } +} diff --git a/luni/src/main/java/libcore/icu/DateIntervalFormat.java b/luni/src/main/java/libcore/icu/DateIntervalFormat.java index a71989e..ab9085f 100644 --- a/luni/src/main/java/libcore/icu/DateIntervalFormat.java +++ b/luni/src/main/java/libcore/icu/DateIntervalFormat.java @@ -19,6 +19,7 @@ package libcore.icu; import java.util.Calendar; import java.util.Locale; import java.util.TimeZone; +import libcore.util.BasicLruCache; /** * Exposes icu4c's DateIntervalFormat. @@ -46,7 +47,18 @@ public final class DateIntervalFormat { private static final int DAY_IN_MS = 24 * 60 * 60 * 1000; private static final int EPOCH_JULIAN_DAY = 2440588; - // TODO: check whether icu4c's DateIntervalFormat is expensive enough to warrant a native peer. + private static final FormatterCache CACHED_FORMATTERS = new FormatterCache(); + + static class FormatterCache extends BasicLruCache<String, Long> { + FormatterCache() { + super(8); + } + + protected void entryEvicted(String key, Long value) { + destroyDateIntervalFormat(value); + } + }; + private DateIntervalFormat() { } @@ -86,7 +98,20 @@ public final class DateIntervalFormat { } String skeleton = toSkeleton(startCalendar, endCalendar, flags); - return formatDateInterval(skeleton, locale.toString(), tz.getID(), startMs, endMs); + synchronized (CACHED_FORMATTERS) { + return formatDateInterval(getFormatter(skeleton, locale.toString(), tz.getID()), startMs, endMs); + } + } + + private static long getFormatter(String skeleton, String localeName, String tzName) { + String key = skeleton + "\t" + localeName + "\t" + tzName; + Long formatter = CACHED_FORMATTERS.get(key); + if (formatter != null) { + return formatter; + } + long address = createDateIntervalFormat(skeleton, localeName, tzName); + CACHED_FORMATTERS.put(key, address); + return address; } private static String toSkeleton(Calendar startCalendar, Calendar endCalendar, int flags) { @@ -204,5 +229,7 @@ public final class DateIntervalFormat { return (int) (utcMs / DAY_IN_MS) + EPOCH_JULIAN_DAY; } - private static native String formatDateInterval(String skeleton, String localeName, String timeZoneName, long fromDate, long toDate); + private static native long createDateIntervalFormat(String skeleton, String localeName, String tzName); + private static native void destroyDateIntervalFormat(long address); + private static native String formatDateInterval(long address, long fromDate, long toDate); } diff --git a/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp b/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp index 65ace4e..72bc631 100644 --- a/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp +++ b/luni/src/main/native/libcore_icu_DateIntervalFormat.cpp @@ -23,41 +23,52 @@ #include "cutils/log.h" #include "unicode/dtitvfmt.h" -static jstring DateIntervalFormat_formatDateInterval(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLocaleName, jstring javaTzName, jlong fromDate, jlong toDate) { +static jlong DateIntervalFormat_createDateIntervalFormat(JNIEnv* env, jclass, jstring javaSkeleton, jstring javaLocaleName, jstring javaTzName) { Locale locale = getLocale(env, javaLocaleName); ScopedJavaUnicodeString skeletonHolder(env, javaSkeleton); if (!skeletonHolder.valid()) { - return NULL; - } - - ScopedJavaUnicodeString tzNameHolder(env, javaTzName); - if (!tzNameHolder.valid()) { - return NULL; + return 0; } UErrorCode status = U_ZERO_ERROR; - UniquePtr<DateIntervalFormat> formatter(DateIntervalFormat::createInstance(skeletonHolder.unicodeString(), locale, status)); + DateIntervalFormat* formatter(DateIntervalFormat::createInstance(skeletonHolder.unicodeString(), locale, status)); if (maybeThrowIcuException(env, "DateIntervalFormat::createInstance", status)) { - return NULL; + return 0; } + ScopedJavaUnicodeString tzNameHolder(env, javaTzName); + if (!tzNameHolder.valid()) { + return 0; + } formatter->adoptTimeZone(TimeZone::createTimeZone(tzNameHolder.unicodeString())); + return reinterpret_cast<uintptr_t>(formatter); +} + +static void DateIntervalFormat_destroyDateIntervalFormat(JNIEnv*, jclass, jlong address) { + delete reinterpret_cast<DateIntervalFormat*>(address); +} + +static jstring DateIntervalFormat_formatDateInterval(JNIEnv* env, jclass, jlong address, jlong fromDate, jlong toDate) { + DateIntervalFormat* formatter(reinterpret_cast<DateIntervalFormat*>(address)); DateInterval date_interval(fromDate, toDate); - UnicodeString result; + UnicodeString s; FieldPosition pos = 0; - formatter->format(&date_interval, result, pos, status); + UErrorCode status = U_ZERO_ERROR; + formatter->format(&date_interval, s, pos, status); if (maybeThrowIcuException(env, "DateIntervalFormat::format", status)) { - return NULL; + return NULL; } - return env->NewString(result.getBuffer(), result.length()); + return env->NewString(s.getBuffer(), s.length()); } static JNINativeMethod gMethods[] = { - NATIVE_METHOD(DateIntervalFormat, formatDateInterval, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;JJ)Ljava/lang/String;"), + NATIVE_METHOD(DateIntervalFormat, createDateIntervalFormat, "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J"), + NATIVE_METHOD(DateIntervalFormat, destroyDateIntervalFormat, "(J)V"), + NATIVE_METHOD(DateIntervalFormat, formatDateInterval, "(JJJ)Ljava/lang/String;"), }; void register_libcore_icu_DateIntervalFormat(JNIEnv* env) { jniRegisterNativeMethods(env, "libcore/icu/DateIntervalFormat", gMethods, NELEM(gMethods)); |