diff --git a/libc/tzcode/localtime.c b/libc/tzcode/localtime.c index 447907e0f..689bbd318 100644 --- a/libc/tzcode/localtime.c +++ b/libc/tzcode/localtime.c @@ -404,10 +404,7 @@ static int toint(unsigned char *s) { } static int -tzload(name, sp, doextend) -register const char * name; -register struct state * const sp; -register const int doextend; +tzload(const char* name, struct state* const sp, const int doextend) { register const char * p; register int i; @@ -2068,32 +2065,55 @@ struct tm * const tmp; } // BEGIN android-added -time_t -mktime_tz(tmp, tz) -struct tm * const tmp; -char const * tz; -{ - struct state st; - if (tzload(tz, &st, TRUE) != 0) { - // TODO: not sure what's best here, but for now, we fall back to gmt. - gmtload(&st); - } - return time1(tmp, localsub, 0L, &st); + +// Caches the most recent timezone (http://b/8270865). +static int __bionic_tzload_cached(const char* name, struct state* const sp, const int doextend) { + _tzLock(); + + // Our single-item cache. + static char* gCachedTimeZoneName; + static struct state gCachedTimeZone; + + // Do we already have this timezone cached? + if (gCachedTimeZoneName != NULL && strcmp(name, gCachedTimeZoneName) == 0) { + *sp = gCachedTimeZone; + _tzUnlock(); + return 0; + } + + // Can we load it? + int rc = tzload(name, sp, doextend); + if (rc == 0) { + // Update the cache. + free(gCachedTimeZoneName); + gCachedTimeZoneName = strdup(name); + gCachedTimeZone = *sp; + } + + _tzUnlock(); + return rc; } -void -localtime_tz(timep, tmp, tz) -const time_t * const timep; -struct tm * tmp; -const char* tz; -{ - struct state st; - if (tzload(tz, &st, TRUE) != 0) { - // TODO: not sure what's best here, but for now, we fall back to gmt. - gmtload(&st); - } - localsub(timep, 0L, tmp, &st); +// Non-standard API: mktime(3) but with an explicit timezone parameter. +time_t mktime_tz(struct tm* const tmp, const char* tz) { + struct state st; + if (__bionic_tzload_cached(tz, &st, TRUE) != 0) { + // TODO: not sure what's best here, but for now, we fall back to gmt. + gmtload(&st); + } + return time1(tmp, localsub, 0L, &st); } + +// Non-standard API: localtime(3) but with an explicit timezone parameter. +void localtime_tz(const time_t* const timep, struct tm* tmp, const char* tz) { + struct state st; + if (__bionic_tzload_cached(tz, &st, TRUE) != 0) { + // TODO: not sure what's best here, but for now, we fall back to gmt. + gmtload(&st); + } + localsub(timep, 0L, tmp, &st); +} + // END android-added #ifdef STD_INSPIRED diff --git a/tests/Android.mk b/tests/Android.mk index 1e4ef59ab..633a6a991 100644 --- a/tests/Android.mk +++ b/tests/Android.mk @@ -32,6 +32,7 @@ benchmark_src_files = \ benchmark_main.cpp \ math_benchmark.cpp \ string_benchmark.cpp \ + time_benchmark.cpp \ # Build benchmarks for the device (with bionic's .so). Run with: # adb shell bionic-benchmarks diff --git a/tests/time_benchmark.cpp b/tests/time_benchmark.cpp new file mode 100644 index 000000000..75132e50e --- /dev/null +++ b/tests/time_benchmark.cpp @@ -0,0 +1,38 @@ +/* + * 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. + */ + +#include "benchmark.h" + +#include + +#if defined(__BIONIC__) + +// Used by the horrible android.text.format.Time class, which is used by Calendar. http://b/8270865. +extern "C" void localtime_tz(const time_t* const timep, struct tm* tmp, const char* tz); + +static void BM_time_localtime_tz(int iters) { + StartBenchmarkTiming(); + + time_t now(time(NULL)); + tm broken_down_time; + for (int i = 0; i < iters; ++i) { + localtime_tz(&now, &broken_down_time, "Europe/Berlin"); + } + + StopBenchmarkTiming(); +} +BENCHMARK(BM_time_localtime_tz); +#endif