summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorjar@google.com <jar@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-19 17:45:21 +0000
committerjar@google.com <jar@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-19 17:45:21 +0000
commitac262c9fd9775f9d85c42eaf42fccb896ba790ba (patch)
tree666a943e0537d6fdad0bf393ff0dd6939ee13b53 /base
parentaef3555aa83038b26b50cfab04d685e0f6dfc04f (diff)
downloadchromium_src-ac262c9fd9775f9d85c42eaf42fccb896ba790ba.zip
chromium_src-ac262c9fd9775f9d85c42eaf42fccb896ba790ba.tar.gz
chromium_src-ac262c9fd9775f9d85c42eaf42fccb896ba790ba.tar.bz2
Construct a field trial to see if HIGH or MEDIUM memory model "works better"
Includes definition of a FieldTrial class to support this. I have thoughts in my head about how this will eventually extend to be controllable via UMA (as well as being able to run tests defined at compile time, as was done in this example. r=mbelshe, mmentovai Review URL: http://codereview.chromium.org/7638 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3604 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r--base/SConscript2
-rw-r--r--base/build/base.vcproj16
-rw-r--r--base/build/base_unittests.vcproj4
-rw-r--r--base/field_trial.cc62
-rw-r--r--base/field_trial.h92
-rw-r--r--base/field_trial_unittest.cc74
6 files changed, 246 insertions, 4 deletions
diff --git a/base/SConscript b/base/SConscript
index ef462d9..82d652e 100644
--- a/base/SConscript
+++ b/base/SConscript
@@ -35,6 +35,7 @@ input_files = [
'bzip2_error_handler.cc',
'command_line.cc',
'debug_util.cc',
+ 'field_trial.cc',
'file_path.cc',
'file_util.cc',
'histogram.cc',
@@ -257,6 +258,7 @@ test_files = [
'clipboard_unittest.cc',
'command_line_unittest.cc',
'condition_variable_unittest.cc',
+ 'field_trial_unittest.cc',
'file_path_unittest.cc',
'file_util_unittest.cc',
'hmac_unittest.cc',
diff --git a/base/build/base.vcproj b/base/build/base.vcproj
index 76a08e9..0350c33 100644
--- a/base/build/base.vcproj
+++ b/base/build/base.vcproj
@@ -286,6 +286,14 @@
>
</File>
<File
+ RelativePath="..\field_trial.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\field_trial.h"
+ >
+ </File>
+ <File
RelativePath="..\file_path.cc"
>
</File>
@@ -538,10 +546,6 @@
>
</File>
<File
- RelativePath="..\process_win.cc"
- >
- </File>
- <File
RelativePath="..\process.h"
>
</File>
@@ -554,6 +558,10 @@
>
</File>
<File
+ RelativePath="..\process_win.cc"
+ >
+ </File>
+ <File
RelativePath="..\third_party\nspr\prtime.cc"
>
</File>
diff --git a/base/build/base_unittests.vcproj b/base/build/base_unittests.vcproj
index 7df06c9..cf91226 100644
--- a/base/build/base_unittests.vcproj
+++ b/base/build/base_unittests.vcproj
@@ -188,6 +188,10 @@
>
</File>
<File
+ RelativePath="..\field_trial_unittest.cc"
+ >
+ </File>
+ <File
RelativePath="..\file_path_unittest.cc"
>
</File>
diff --git a/base/field_trial.cc b/base/field_trial.cc
new file mode 100644
index 0000000..103e539
--- /dev/null
+++ b/base/field_trial.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+
+#include "base/field_trial.h"
+#include "base/logging.h"
+#include "base/rand_util.h"
+
+//------------------------------------------------------------------------------
+// FieldTrialList methods and members.
+
+// static
+FieldTrialList* FieldTrialList::global_ = NULL;
+
+// static
+int FieldTrialList::constructor_count_ = 0;
+
+FieldTrialList::FieldTrialList()
+ : application_start_time_(Time::Now()) {
+ DCHECK(!constructor_count_);
+ ++constructor_count_;
+ global_ = this;
+}
+
+FieldTrialList::~FieldTrialList() {
+ while (!registered_.empty()) {
+ RegistrationList::iterator it = registered_.begin();
+ it->second->Release();
+ registered_.erase(it->first);
+ }
+ DCHECK(this == global_);
+ global_ = NULL;
+}
+
+// static
+void FieldTrialList::Register(FieldTrial* trial) {
+ DCHECK(global_->CalledOnValidThread());
+ DCHECK(!Find(trial->name()));
+ trial->AddRef();
+ global_->registered_[trial->name()] = trial;
+}
+
+// static
+FieldTrial* FieldTrialList::Find(const std::wstring& name) {
+ DCHECK(global_->CalledOnValidThread());
+ RegistrationList::iterator it = global_->registered_.find(name);
+ if (global_->registered_.end() == it)
+ return NULL;
+ return it->second;
+}
+
+//------------------------------------------------------------------------------
+// FieldTrial methods and members.
+
+FieldTrial::FieldTrial(const std::wstring& name, double probability)
+ : name_(name) {
+ double rand = base::RandDouble();
+ DCHECK(rand >= 0.0 && rand < 1.0);
+ boolean_value_ = rand < probability;
+ FieldTrialList::Register(this);
+}
diff --git a/base/field_trial.h b/base/field_trial.h
new file mode 100644
index 0000000..e70ab1a
--- /dev/null
+++ b/base/field_trial.h
@@ -0,0 +1,92 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// FieldTrial is a class for handling details of statistical experiments
+// performed by actual users in the field (i.e., in a shipped or beta product).
+// All code is called exclusively on the UI thread currently.
+//
+// The simplest example is a test to see whether one of two options produces
+// "better" results across our user population. In that scenario, UMA data
+// is uploaded to show the test results, and this class manages the state of
+// each such test (state == which option was pseudo-randomly selected).
+// States are typically generated randomly, either based on a one time
+// randomization (reused during each run of the program), or by a startup
+// randomization (keeping that tests state constant across a run), or by
+// continuous randomization across a run.
+// Only startup randomization is implemented (thus far).
+
+#ifndef BASE_FIELD_TRIAL_H_
+#define BASE_FIELD_TRIAL_H_
+
+#include <map>
+#include <string>
+
+#include "base/non_thread_safe.h"
+#include "base/ref_counted.h"
+#include "base/time.h"
+
+class FieldTrial : public base::RefCounted<FieldTrial> {
+ public:
+ // Constructor for a 2-state (boolean) trial.
+ // The name is used to register the instance with the FieldTrialList class,
+ // and can be used to find the trial (only one trial can be present for each
+ // name) using the Find() method.
+ // The probability is a number in the range [0, 1], and is the likliehood that
+ // the assigned boolean value will be true.
+ FieldTrial(const std::wstring& name, double probability);
+
+ // Return the selected boolean value.
+ bool boolean_value() const { return boolean_value_; }
+ std::wstring name() const { return name_; }
+
+ private:
+ const std::wstring name_;
+ bool boolean_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(FieldTrial);
+};
+
+// Class with a list of all active field trials. A trial is active if it has
+// been registered, which includes evaluating its state based on its probaility.
+// Only one instance of this class exists.
+class FieldTrialList : NonThreadSafe {
+ public:
+ // This singleton holds the global list of registered FieldTrials.
+ FieldTrialList();
+ // Destructor Release()'s references to all registered FieldTrial instances.
+ ~FieldTrialList();
+
+ // Register() stores a pointer to the given trial in a global map.
+ // This method also AddRef's the indicated trial.
+ static void Register(FieldTrial* trial);
+
+ // The Find() method can be used to test to see if a named Trial was already
+ // registered, or to retrieve a pointer to it from the global map.
+ static FieldTrial* Find(const std::wstring& name);
+
+ // The time of construction of the global map is recorded in a static variable
+ // and is commonly used by experiments to identify the time since the start
+ // of the application. In some experiments it may be useful to discount
+ // data that is gathered before the application has reach sufficient
+ // stability (example: most DLL have loaded, etc.)
+ static Time application_start_time() {
+ return global_->application_start_time_;
+ }
+
+ private:
+ typedef std::map<std::wstring, FieldTrial*> RegistrationList;
+
+ friend class FieldTrialTest;
+ static void ResetConstructorCountForTestingOnly() { constructor_count_ = 0; }
+
+ static FieldTrialList* global_; // The singleton of this class.
+ static int constructor_count_; // Prevent having more than one.
+
+ Time application_start_time_;
+ RegistrationList registered_;
+
+ DISALLOW_COPY_AND_ASSIGN(FieldTrialList);
+};
+
+#endif // BASE_FIELD_TRIAL_H_
diff --git a/base/field_trial_unittest.cc b/base/field_trial_unittest.cc
new file mode 100644
index 0000000..3d8c0e2
--- /dev/null
+++ b/base/field_trial_unittest.cc
@@ -0,0 +1,74 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Test of FieldTrial class
+
+#include "base/field_trial.h"
+
+#include "base/logging.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class FieldTrialTest : public testing::Test {
+ public:
+ FieldTrialTest() : trial_list_() { }
+ ~FieldTrialTest() { FieldTrialList::ResetConstructorCountForTestingOnly(); }
+
+ private:
+ FieldTrialList trial_list_;
+};
+
+// Test registration, and also check that destructors are called for trials
+// (and that Purify doesn't catch us leaking).
+TEST_F(FieldTrialTest, Registration) {
+ const wchar_t* name1 = L"name 1 test";
+ const wchar_t* name2 = L"name 2 test";
+ EXPECT_FALSE(FieldTrialList::Find(name1));
+ EXPECT_FALSE(FieldTrialList::Find(name2));
+
+ FieldTrial* trial1 = new FieldTrial(name1, 0.7);
+
+ EXPECT_EQ(trial1, FieldTrialList::Find(name1));
+ EXPECT_FALSE(FieldTrialList::Find(name2));
+
+ FieldTrial* trial2 = new FieldTrial(name2, 0.7);
+
+ EXPECT_EQ(trial1, FieldTrialList::Find(name1));
+ EXPECT_EQ(trial2, FieldTrialList::Find(name2));
+ // Note: FieldTrialList should delete the objects at shutdown.
+}
+
+TEST_F(FieldTrialTest, AbsoluteProbabilities) {
+ wchar_t always_true[] = L" always true";
+ wchar_t always_false[] = L" always false";
+ for (int i = 1; i < 250; ++i) {
+ // Try lots of names, by changing the first character of the name.
+ always_true[0] = i;
+ always_false[0] = i;
+ FieldTrial* trial_true = new FieldTrial(always_true, 1.0);
+ EXPECT_TRUE(trial_true->boolean_value());
+ FieldTrial* trial_false = new FieldTrial(always_false, 0.0);
+ EXPECT_FALSE(trial_false->boolean_value());
+ }
+}
+
+TEST_F(FieldTrialTest, MiddleProbabalities) {
+ wchar_t name[] = L" same name";
+ bool false_event_seen = false;
+ bool true_event_seen = false;
+ for (int i = 1; i < 250; ++i) {
+ name[0] = i;
+ FieldTrial* trial = new FieldTrial(name, 0.5);
+ if (trial->boolean_value()) {
+ true_event_seen = true;
+ } else {
+ false_event_seen = true;
+ }
+ if (false_event_seen && true_event_seen)
+ return; // Successful test!!!
+ }
+ // Very surprising to get here. Probability should be around 1 in 2 ** 250.
+ // One of the following will fail.
+ EXPECT_TRUE(false_event_seen);
+ EXPECT_TRUE(true_event_seen);
+}