Change the implementation of Math.random to use George
authorkasperl@chromium.org <kasperl@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 15 Jun 2009 08:04:47 +0000 (08:04 +0000)
committerkasperl@chromium.org <kasperl@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Mon, 15 Jun 2009 08:04:47 +0000 (08:04 +0000)
Marsaglia's multiply-with-carry instead of mixing the
bits obtained from calling the system random() twice.

This seems to be a bit faster and gives a better
distribution than the system random() in particular on
Windows.
Review URL: http://codereview.chromium.org/126113

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2159 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

14 files changed:
src/api.cc
src/arm/codegen-arm.cc
src/arm/codegen-arm.h
src/assembler.cc
src/assembler.h
src/codegen.cc
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/math.js
src/runtime.cc
src/runtime.h
src/serialize.cc
src/v8.cc
src/v8.h

index 7b7f290816232cb77fccd0c86f18f5aab24c1962..097c2ea68384ab985dbd9fef45d59f832e48cbea 100644 (file)
@@ -2124,7 +2124,9 @@ int v8::Object::GetIdentityHash() {
   } else {
     int attempts = 0;
     do {
-      hash_value = random() & i::Smi::kMaxValue;  // Limit range to fit a smi.
+      // Generate a random 32-bit hash value but limit range to fit
+      // within a smi.
+      hash_value = i::V8::Random() & i::Smi::kMaxValue;
       attempts++;
     } while (hash_value == 0 && attempts < 30);
     hash_value = hash_value != 0 ? hash_value : 1;  // never return 0
index a3d9b6a4de30e35a1ab50d239b5a984056e76ac3..c8fd287a4616b686ac952145f684a366e2a9f47d 100644 (file)
@@ -3405,6 +3405,15 @@ void CodeGenerator::GenerateArgumentsAccess(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateRandomPositiveSmi(ZoneList<Expression*>* args) {
+  VirtualFrame::SpilledScope spilled_scope;
+  ASSERT(args->length() == 0);
+  __ Call(ExternalReference::random_positive_smi_function().address(),
+          RelocInfo::RUNTIME_ENTRY);
+  frame_->EmitPush(r0);
+}
+
+
 void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
   VirtualFrame::SpilledScope spilled_scope;
   ASSERT(args->length() == 2);
index a8cb777d79d394d61e82dfc48ea53341c572f610..4dfbdabed5243e48ea65abdd812a380a395780dd 100644 (file)
@@ -349,6 +349,9 @@ class CodeGenerator: public AstVisitor {
 
   void GenerateLog(ZoneList<Expression*>* args);
 
+  // Fast support for Math.random().
+  void GenerateRandomPositiveSmi(ZoneList<Expression*>* args);
+
   // Methods and constants for fast case switch statement support.
   //
   // Only allow fast-case switch if the range of labels is at most
index 0b4559c539ba278d9ca4073f9e342140ac741898..7b7778c045f9493f6782cbf4e2f29b99442841f5 100644 (file)
@@ -553,6 +553,11 @@ ExternalReference ExternalReference::builtin_passed_function() {
 }
 
 
+ExternalReference ExternalReference::random_positive_smi_function() {
+  return ExternalReference(Redirect(FUNCTION_ADDR(V8::RandomPositiveSmi)));
+}
+
+
 ExternalReference ExternalReference::the_hole_value_location() {
   return ExternalReference(Factory::the_hole_value().location());
 }
index 961f92fbe143756bf6af38c9450288801ee106cd..0abd852a165fa9065bf91d4f81810d179c356687 100644 (file)
@@ -388,8 +388,8 @@ class ExternalReference BASE_EMBEDDED {
   // ExternalReferenceTable in serialize.cc manually.
 
   static ExternalReference perform_gc_function();
-
   static ExternalReference builtin_passed_function();
+  static ExternalReference random_positive_smi_function();
 
   // Static variable Factory::the_hole_value.location()
   static ExternalReference the_hole_value_location();
index 99a6c013b59ce42bc636e228e01193a1427ae27f..4a456777587326895cd8764e687b69bf9068c7b6 100644 (file)
@@ -422,7 +422,8 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
   {&CodeGenerator::GenerateSetValueOf, "_SetValueOf"},
   {&CodeGenerator::GenerateFastCharCodeAt, "_FastCharCodeAt"},
   {&CodeGenerator::GenerateObjectEquals, "_ObjectEquals"},
-  {&CodeGenerator::GenerateLog, "_Log"}
+  {&CodeGenerator::GenerateLog, "_Log"},
+  {&CodeGenerator::GenerateRandomPositiveSmi, "_RandomPositiveSmi"}
 };
 
 
index b9414931ecd6868923815a5420aa3431debc00e1..f79cb96fbabd4fe6481d317583b02296336dee5d 100644 (file)
@@ -4934,6 +4934,15 @@ void CodeGenerator::GenerateGetFramePointer(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateRandomPositiveSmi(ZoneList<Expression*>* args) {
+  ASSERT(args->length() == 0);
+  frame_->SpillAll();
+  __ call(FUNCTION_ADDR(V8::RandomPositiveSmi), RelocInfo::RUNTIME_ENTRY);
+  Result result = allocator_->Allocate(eax);
+  frame_->Push(&result);
+}
+
+
 void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
   if (CheckForInlineRuntimeCall(node)) {
     return;
index 9b609a1562ac50088d7e0885a9c53dbd5e22cc73..aed6966b9cb9cbf28ab9942190bd2ebe4fa53885 100644 (file)
@@ -518,6 +518,9 @@ class CodeGenerator: public AstVisitor {
 
   void GenerateGetFramePointer(ZoneList<Expression*>* args);
 
+  // Fast support for Math.random().
+  void GenerateRandomPositiveSmi(ZoneList<Expression*>* args);
+
   // Methods and constants for fast case switch statement support.
   //
   // Only allow fast-case switch if the range of labels is at most
index 86d6dd101d01918621a4e6b35ab7df7bfb01dcaa..c8dd0cd5026feecc27c4c3d3b80cd67a5b944e49 100644 (file)
@@ -44,39 +44,73 @@ $Math.__proto__ = global.Object.prototype;
 
 // ECMA 262 - 15.8.2.1
 function MathAbs(x) {
-  if (%_IsSmi(x)) {
-    return x >= 0 ? x : -x;
-  } else {
-    return %Math_abs(ToNumber(x));
-  }
+  if (%_IsSmi(x)) return x >= 0 ? x : -x;
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  return %Math_abs(x);
 }
 
 // ECMA 262 - 15.8.2.2
-function MathAcos(x) { return %Math_acos(ToNumber(x)); }
+function MathAcos(x) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  return %Math_acos(x);
+}
 
 // ECMA 262 - 15.8.2.3
-function MathAsin(x) { return %Math_asin(ToNumber(x)); }
+function MathAsin(x) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  return %Math_asin(x);
+}
 
 // ECMA 262 - 15.8.2.4
-function MathAtan(x) { return %Math_atan(ToNumber(x)); }
+function MathAtan(x) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  return %Math_atan(x);
+}
 
 // ECMA 262 - 15.8.2.5
-function MathAtan2(x, y) { return %Math_atan2(ToNumber(x), ToNumber(y)); }
+function MathAtan2(x, y) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  if (!IS_NUMBER(y)) y = ToNumber(y);
+  return %Math_atan2(x, y);
+}
 
 // ECMA 262 - 15.8.2.6
-function MathCeil(x) { return %Math_ceil(ToNumber(x)); }
+function MathCeil(x) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  return %Math_ceil(x);
+}
 
 // ECMA 262 - 15.8.2.7
-function MathCos(x) { return %Math_cos(ToNumber(x)); }
+function MathCos(x) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  return %Math_cos(x);
+}
 
 // ECMA 262 - 15.8.2.8
-function MathExp(x) { return %Math_exp(ToNumber(x)); }
+function MathExp(x) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  return %Math_exp(x);
+}
 
 // ECMA 262 - 15.8.2.9
-function MathFloor(x) { return %Math_floor(ToNumber(x)); }
+function MathFloor(x) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  if (0 < x && x <= 0xFFFFFFFF) {
+    // Numbers in the range [0, 2^32) can be floored by converting
+    // them to an unsigned 32-bit value using the shift operator.
+    // We avoid doing so for -0, because the result of Math.floor(-0)
+    // has to be -0, which wouldn't be the case with the shift.
+    return x << 0;
+  } else {
+    return %Math_floor(x);
+  }
+}
 
 // ECMA 262 - 15.8.2.10
-function MathLog(x) { return %Math_log(ToNumber(x)); }
+function MathLog(x) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  return %Math_log(x);
+}
 
 // ECMA 262 - 15.8.2.11
 function MathMax(arg1, arg2) {  // length == 2
@@ -103,22 +137,40 @@ function MathMin(arg1, arg2) {  // length == 2
 }
 
 // ECMA 262 - 15.8.2.13
-function MathPow(x, y) { return %Math_pow(ToNumber(x), ToNumber(y)); }
+function MathPow(x, y) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  if (!IS_NUMBER(y)) y = ToNumber(y);
+  return %Math_pow(x, y);
+}
 
 // ECMA 262 - 15.8.2.14
-function MathRandom() { return %Math_random(); }
+function MathRandom() {
+  return %_RandomPositiveSmi() / 0x40000000;
+}
 
 // ECMA 262 - 15.8.2.15
-function MathRound(x) { return %Math_round(ToNumber(x)); }
+function MathRound(x) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  return %Math_round(x);
+}
 
 // ECMA 262 - 15.8.2.16
-function MathSin(x) { return %Math_sin(ToNumber(x)); }
+function MathSin(x) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  return %Math_sin(x);
+}
 
 // ECMA 262 - 15.8.2.17
-function MathSqrt(x) { return %Math_sqrt(ToNumber(x)); }
+function MathSqrt(x) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  return %Math_sqrt(x);
+}
 
 // ECMA 262 - 15.8.2.18
-function MathTan(x) { return %Math_tan(ToNumber(x)); }
+function MathTan(x) {
+  if (!IS_NUMBER(x)) x = ToNumber(x);
+  return %Math_tan(x);
+}
 
 
 // -------------------------------------------------------------------
index 6e9e1758fc8b366862fc944252fb509efd8065da..316f8c429bfae128e6d1deb2a55e311edc0d77a0 100644 (file)
@@ -4168,24 +4168,6 @@ static Object* Runtime_Math_pow(Arguments args) {
   }
 }
 
-// Returns a number value with positive sign, greater than or equal to
-// 0 but less than 1, chosen randomly.
-static Object* Runtime_Math_random(Arguments args) {
-  NoHandleAllocation ha;
-  ASSERT(args.length() == 0);
-
-  // To get much better precision, we combine the results of two
-  // invocations of random(). The result is computed by normalizing a
-  // double in the range [0, RAND_MAX + 1) obtained by adding the
-  // high-order bits in the range [0, RAND_MAX] with the low-order
-  // bits in the range [0, 1).
-  double lo = static_cast<double>(random()) * (1.0 / (RAND_MAX + 1.0));
-  double hi = static_cast<double>(random());
-  double result = (hi + lo) * (1.0 / (RAND_MAX + 1.0));
-  ASSERT(result >= 0 && result < 1);
-  return Heap::AllocateHeapNumber(result);
-}
-
 
 static Object* Runtime_Math_round(Arguments args) {
   NoHandleAllocation ha;
index a18882dc9101582754a51bf98bb4e1043c72d1a9..15dd9b49175e8bbbd4f269471dd4f83462e64682 100644 (file)
@@ -135,7 +135,6 @@ namespace internal {
   F(Math_floor, 1) \
   F(Math_log, 1) \
   F(Math_pow, 2) \
-  F(Math_random, 0) \
   F(Math_round, 1) \
   F(Math_sin, 1) \
   F(Math_sqrt, 1) \
index faef1579228a78f22754c271a29e1419fb57ffbc..eb497fb2e83b3ba7b7e2445b35a3dd6545c10f21 100644 (file)
@@ -652,6 +652,10 @@ void ExternalReferenceTable::PopulateTable() {
       RUNTIME_ENTRY,
       1,
       "Runtime::PerformGC");
+  Add(ExternalReference::random_positive_smi_function().address(),
+      RUNTIME_ENTRY,
+      2,
+      "V8::RandomPositiveSmi");
 
   // Miscellaneous
   Add(ExternalReference::builtin_passed_function().address(),
index e9d29ce56d692173e295ddb5b001ca5bd5a342a9..72f74aa1d58fc3e0c5d3e8f8579b4bef9c3252e0 100644 (file)
--- a/src/v8.cc
+++ b/src/v8.cc
@@ -138,4 +138,29 @@ void V8::TearDown() {
 }
 
 
+uint32_t V8::Random() {
+  // Random number generator using George Marsaglia's MWC algorithm.
+  static uint32_t hi = 0;
+  static uint32_t lo = 0;
+
+  // Initialize seed using the system random(). If one of the seeds
+  // should ever become zero again, or if random() returns zero, we
+  // avoid getting stuck with zero bits in hi or lo by re-initializing
+  // them on demand.
+  if (hi == 0) hi = random();
+  if (lo == 0) lo = random();
+
+  // Mix the bits.
+  hi = 36969 * (hi & 0xFFFF) + (hi >> 16);
+  lo = 18273 * (lo & 0xFFFF) + (lo >> 16);
+  return (hi << 16) + (lo & 0xFFFF);
+}
+
+
+Smi* V8::RandomPositiveSmi() {
+  uint32_t random = Random();
+  ASSERT(IsPowerOf2(Smi::kMaxValue + 1));
+  return Smi::FromInt(random & Smi::kMaxValue);
+}
+
 } }  // namespace v8::internal
index 8cb3c7da1637818671fb0606f8be2cc38bfaa743..4e906df929c2979122c89578630353474ecc4e13 100644 (file)
--- a/src/v8.h
+++ b/src/v8.h
@@ -80,10 +80,10 @@ class V8 : public AllStatic {
  public:
   // Global actions.
 
-  // If Initialize is called with des == NULL, the
-  // initial state is created from scratch. If a non-null Deserializer
-  // is given, the initial state is created by reading the
-  // deserialized data into an empty heap.
+  // If Initialize is called with des == NULL, the initial state is
+  // created from scratch. If a non-null Deserializer is given, the
+  // initial state is created by reading the deserialized data into an
+  // empty heap.
   static bool Initialize(Deserializer* des);
   static void TearDown();
   static bool IsRunning() { return is_running_; }
@@ -93,6 +93,11 @@ class V8 : public AllStatic {
 
   // Report process out of memory. Implementation found in api.cc.
   static void FatalProcessOutOfMemory(const char* location);
+
+  // Random number generation support. Not cryptographically safe.
+  static uint32_t Random();
+  static Smi* RandomPositiveSmi();
+
  private:
   // True if engine is currently running
   static bool is_running_;