Cache the results of slow math operations on machines that don't
have hardware fpu instructions to execute them. Review URL: http://codereview.chromium.org/179059 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2790 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
62e9956c91
commit
be70d6fc4d
27
src/heap.cc
27
src/heap.cc
@ -257,6 +257,7 @@ void Heap::ReportStatisticsAfterGC() {
|
||||
|
||||
|
||||
void Heap::GarbageCollectionPrologue() {
|
||||
TranscendentalCache::Clear();
|
||||
gc_count_++;
|
||||
#ifdef DEBUG
|
||||
ASSERT(allocation_allowed_ && gc_state_ == NOT_IN_GC);
|
||||
@ -3986,4 +3987,30 @@ bool Heap::GarbageCollectionGreedyCheck() {
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
TranscendentalCache::TranscendentalCache(TranscendentalCache::Type t)
|
||||
: type_(t) {
|
||||
uint32_t in0 = 0xffffffffu; // Bit-pattern for a NaN that isn't
|
||||
uint32_t in1 = 0xffffffffu; // generated by the FPU.
|
||||
for (int i = 0; i < kCacheSize; i++) {
|
||||
elements_[i].in[0] = in0;
|
||||
elements_[i].in[1] = in1;
|
||||
elements_[i].output = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TranscendentalCache* TranscendentalCache::caches_[kNumberOfCaches];
|
||||
|
||||
|
||||
void TranscendentalCache::Clear() {
|
||||
for (int i = 0; i < kNumberOfCaches; i++) {
|
||||
if (caches_[i] != NULL) {
|
||||
delete caches_[i];
|
||||
caches_[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
88
src/heap.h
88
src/heap.h
@ -28,8 +28,11 @@
|
||||
#ifndef V8_HEAP_H_
|
||||
#define V8_HEAP_H_
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "zone-inl.h"
|
||||
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
|
||||
@ -1519,6 +1522,91 @@ class GCTracer BASE_EMBEDDED {
|
||||
int previous_marked_count_;
|
||||
};
|
||||
|
||||
|
||||
class TranscendentalCache {
|
||||
public:
|
||||
enum Type {ACOS, ASIN, ATAN, COS, EXP, LOG, SIN, TAN, kNumberOfCaches};
|
||||
|
||||
explicit TranscendentalCache(Type t);
|
||||
|
||||
// Returns a heap number with f(input), where f is a math function specified
|
||||
// by the 'type' argument.
|
||||
static inline Object* Get(Type type, double input) {
|
||||
TranscendentalCache* cache = caches_[type];
|
||||
if (cache == NULL) {
|
||||
caches_[type] = cache = new TranscendentalCache(type);
|
||||
}
|
||||
return cache->Get(input);
|
||||
}
|
||||
|
||||
// The cache contains raw Object pointers. This method disposes of
|
||||
// them before a garbage collection.
|
||||
static void Clear();
|
||||
|
||||
private:
|
||||
inline Object* Get(double input) {
|
||||
Converter c;
|
||||
c.dbl = input;
|
||||
int hash = Hash(c);
|
||||
Element e = elements_[hash];
|
||||
if (e.in[0] == c.integers[0] &&
|
||||
e.in[1] == c.integers[1]) {
|
||||
ASSERT(e.output != NULL);
|
||||
return e.output;
|
||||
}
|
||||
double answer = Calculate(input);
|
||||
Object* heap_number = Heap::AllocateHeapNumber(answer);
|
||||
if (!heap_number->IsFailure()) {
|
||||
elements_[hash].in[0] = c.integers[0];
|
||||
elements_[hash].in[1] = c.integers[1];
|
||||
elements_[hash].output = heap_number;
|
||||
}
|
||||
return heap_number;
|
||||
}
|
||||
|
||||
inline double Calculate(double input) {
|
||||
switch (type_) {
|
||||
case ACOS:
|
||||
return acos(input);
|
||||
case ASIN:
|
||||
return asin(input);
|
||||
case ATAN:
|
||||
return atan(input);
|
||||
case COS:
|
||||
return cos(input);
|
||||
case EXP:
|
||||
return exp(input);
|
||||
case LOG:
|
||||
return log(input);
|
||||
case SIN:
|
||||
return sin(input);
|
||||
case TAN:
|
||||
return tan(input);
|
||||
default:
|
||||
return 0.0; // Never happens.
|
||||
}
|
||||
}
|
||||
static const int kCacheSize = 512;
|
||||
struct Element {
|
||||
uint32_t in[2];
|
||||
Object* output;
|
||||
};
|
||||
union Converter {
|
||||
double dbl;
|
||||
uint32_t integers[2];
|
||||
};
|
||||
inline static int Hash(const Converter& c) {
|
||||
uint32_t hash = (c.integers[0] ^ c.integers[1]);
|
||||
hash ^= hash >> 16;
|
||||
hash ^= hash >> 8;
|
||||
return (hash & (kCacheSize - 1));
|
||||
}
|
||||
static TranscendentalCache* caches_[kNumberOfCaches];
|
||||
Element elements_[kCacheSize];
|
||||
Type type_;
|
||||
};
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
||||
#endif // V8_HEAP_H_
|
||||
|
@ -4058,7 +4058,7 @@ static Object* Runtime_Math_acos(Arguments args) {
|
||||
ASSERT(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_CHECKED(x, args[0]);
|
||||
return Heap::AllocateHeapNumber(acos(x));
|
||||
return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
|
||||
}
|
||||
|
||||
|
||||
@ -4067,7 +4067,7 @@ static Object* Runtime_Math_asin(Arguments args) {
|
||||
ASSERT(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_CHECKED(x, args[0]);
|
||||
return Heap::AllocateHeapNumber(asin(x));
|
||||
return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
|
||||
}
|
||||
|
||||
|
||||
@ -4076,7 +4076,7 @@ static Object* Runtime_Math_atan(Arguments args) {
|
||||
ASSERT(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_CHECKED(x, args[0]);
|
||||
return Heap::AllocateHeapNumber(atan(x));
|
||||
return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
|
||||
}
|
||||
|
||||
|
||||
@ -4117,7 +4117,7 @@ static Object* Runtime_Math_cos(Arguments args) {
|
||||
ASSERT(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_CHECKED(x, args[0]);
|
||||
return Heap::AllocateHeapNumber(cos(x));
|
||||
return TranscendentalCache::Get(TranscendentalCache::COS, x);
|
||||
}
|
||||
|
||||
|
||||
@ -4126,7 +4126,7 @@ static Object* Runtime_Math_exp(Arguments args) {
|
||||
ASSERT(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_CHECKED(x, args[0]);
|
||||
return Heap::AllocateHeapNumber(exp(x));
|
||||
return TranscendentalCache::Get(TranscendentalCache::EXP, x);
|
||||
}
|
||||
|
||||
|
||||
@ -4144,7 +4144,7 @@ static Object* Runtime_Math_log(Arguments args) {
|
||||
ASSERT(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_CHECKED(x, args[0]);
|
||||
return Heap::AllocateHeapNumber(log(x));
|
||||
return TranscendentalCache::Get(TranscendentalCache::LOG, x);
|
||||
}
|
||||
|
||||
|
||||
@ -4232,7 +4232,7 @@ static Object* Runtime_Math_sin(Arguments args) {
|
||||
ASSERT(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_CHECKED(x, args[0]);
|
||||
return Heap::AllocateHeapNumber(sin(x));
|
||||
return TranscendentalCache::Get(TranscendentalCache::SIN, x);
|
||||
}
|
||||
|
||||
|
||||
@ -4250,7 +4250,7 @@ static Object* Runtime_Math_tan(Arguments args) {
|
||||
ASSERT(args.length() == 1);
|
||||
|
||||
CONVERT_DOUBLE_CHECKED(x, args[0]);
|
||||
return Heap::AllocateHeapNumber(tan(x));
|
||||
return TranscendentalCache::Get(TranscendentalCache::TAN, x);
|
||||
}
|
||||
|
||||
|
||||
|
63
test/mjsunit/transcendentals.js
Normal file
63
test/mjsunit/transcendentals.js
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright 2009 the V8 project authors. All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are
|
||||
// met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
// * Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
// Two fp numbers that have the same hash value (see TranscendentalCache
|
||||
// in heap.h).
|
||||
var x = 0x123456789ABCD;
|
||||
var y = 0x1134567899BCD;
|
||||
|
||||
assertEquals(-0.5582508193778007, Math.sin(x));
|
||||
assertEquals(-0.7367701055966746, Math.sin(y));
|
||||
|
||||
assertEquals(-0.8296722380940645, Math.cos(x));
|
||||
assertEquals(-0.6761433365042245, Math.cos(y));
|
||||
|
||||
assertEquals(0.6728570557696649, Math.tan(x));
|
||||
assertEquals(1.0896655573149632, Math.tan(y));
|
||||
|
||||
assertEquals(33.400141709152514, Math.log(x));
|
||||
assertEquals(33.343643692997280, Math.log(y));
|
||||
|
||||
// These also have the same hash value but they are < 1 so they can be
|
||||
// used for the asin and other functions.
|
||||
x = 0x123456789ABCD / 0x2000000000000;
|
||||
y = 0x1134567899BCD / 0x2000000000000;
|
||||
|
||||
assertEquals(0.6051541873165459, Math.asin(x));
|
||||
assertEquals(0.5676343396849298, Math.asin(y));
|
||||
|
||||
assertEquals(0.9656421394783508, Math.acos(x));
|
||||
assertEquals(1.0031619871099668, Math.acos(y));
|
||||
|
||||
assertEquals(0.5172294898564562, Math.atan(x));
|
||||
assertEquals(0.4933034078249788, Math.atan(y));
|
||||
|
||||
assertEquals(1.7663034013841883, Math.exp(x));
|
||||
assertEquals(1.7119599587777090, Math.exp(y));
|
||||
|
||||
print("OK");
|
Loading…
Reference in New Issue
Block a user