Index: base/field_trial.h |
=================================================================== |
--- base/field_trial.h (revision 10679) |
+++ base/field_trial.h (working copy) |
@@ -6,16 +6,65 @@ |
// 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). |
+// The simplest example is an experiment to see whether one of two options |
+// produces "better" results across our user population. In that scenario, UMA |
+// data is uploaded to aggregate the test results, and this FieldTrial class |
+// manages the state of each such experiment (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). |
+// randomization (generated randomly once, and then persitently reused in the |
+// client during each future run of the program), or by a startup randomization |
+// (generated each time the application starts up, but held constant during the |
+// duration of the process), or by continuous randomization across a run (where |
+// the state can be recalculated again and again, many times during a process). |
+// Only startup randomization is implemented thus far. |
+//------------------------------------------------------------------------------ |
+// Example: Suppose we have an experiment involving memory, such as determining |
+// the impact of memory model command line flags actual memory use. |
+// We assume that we already have a histogram of memory usage, such as: |
+ |
+// HISTOGRAM_COUNTS("Memory.RendererTotal", count); |
+ |
+// Somewhere in main thread initialization code, we'd probably define an |
+// instance of a FieldTrial, with code such as: |
+ |
+// // Note, FieldTrials are reference counted, and persist automagically until |
+// // process teardown, courtesy of their automatic registration in |
+// // FieldTrialList. |
+// trial = new FieldTrial("MemoryExperiment", 1000); |
+// group1 = trial->AppendGroup("_high_mem", 20); // 2% this _high_mem group. |
+// group2 = trial->AppendGroup("_low_mem", 20); // 2% this _low_mem group. |
+// // Take action depending of which group we randomly land in. |
+// switch (trial->group()) { |
+// case group1: |
+// ... do something |
+// break; |
+// case group2: |
+// .... |
+// break; |
+// default: |
+// ... |
+// } |
+ |
+// We then modify any histograms we wish to correlate with our experiment to |
+// have slighly different names, depending on what group the trial instance |
+// happened (randomly) to be assigned to: |
+ |
+// HISTOGRAM_COUNTS(FieldTrial::MakeName("Memory.RendererTotal", |
+// "MemoryExperiment"), count); |
+ |
+// The above code will create 3 distinct histograms, with each run of the |
+// application being assigned to of of teh three groups, and for each group, the |
+// correspondingly named histogram will be populated: |
+ |
+// Memory.RendererTotal // 96% of users still fill this histogram. |
+// Memory.RendererTotal_high_mem // 2% of users will fill this histogram. |
+// Memory.RendererTotal_low_mem // 2% of users will fill this histogram. |
+ |
+//------------------------------------------------------------------------------ |
+ |
#ifndef BASE_FIELD_TRIAL_H_ |
#define BASE_FIELD_TRIAL_H_ |
@@ -26,27 +75,74 @@ |
#include "base/ref_counted.h" |
#include "base/time.h" |
+ |
class FieldTrial : public base::RefCounted<FieldTrial> { |
public: |
- // Constructor for a 2-state (boolean) trial. |
+ static const int kNotParticipating = -1; |
+ |
+ typedef int Probability; // Use scaled up probability. |
+ |
// 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); |
+ // name). |
+ // Group probabilities that are later supplied must sum to less than or equal |
+ // to the total_probability. |
+ FieldTrial(const std::string& name, Probability total_probability); |
- // Return the selected boolean value. |
- bool boolean_value() const { return boolean_value_; } |
- std::wstring name() const { return name_; } |
+ // Establish the name and probability of the next group in this trial. |
+ // Sometimes, based on construction randomization, this call may causes the |
+ // provided group to be *THE* group selected for use in this instance. |
+ int AppendGroup(const std::string& name, Probability group_probability); |
+ // Return the name of the FieldTrial (excluding the group name). |
+ std::string name() const { return name_; } |
+ |
+ // Return the randomly selected group number that was assigned. |
+ // Return kNotParticipating if the instance is not participating in the |
+ // experiment. |
+ int group() const { return group_; } |
+ |
+ // If the field trial is not in an experiment, this returns the empty string. |
+ // if the group's name is empty, a name of "_" concatenated with the group |
+ // number is used as the group name. |
+ std::string group_name() const { return group_name_; } |
+ |
+ // Helper function for the most common use: as an argument to specifiy the |
+ // name of a HISTOGRAM. Use the original histogram name as the name_prefix. |
+ static std::string MakeName(const std::string& name_prefix, |
+ const std::string& trial_name); |
+ |
private: |
- const std::wstring name_; |
- bool boolean_value_; |
+ // The name of the field trial, as can be found via the FieldTrialList. |
+ // This is empty of the trial is not in the experiment. |
+ const std::string name_; |
+ // The maximu sum of all probabilities supplied, which corresponds to 100%. |
+ // This is the scaling factor used to adjust supplied probabilities. |
+ Probability divisor_; |
+ |
+ // The randomly selected probability that is used to select a group (or have |
+ // the instance not participate). It is the product of divisor_ and a random |
+ // number between [0, 1). |
+ Probability random_; |
+ |
+ // Sum of the probabilities of all appended groups. |
+ Probability accumulated_group_probability_; |
+ |
+ int next_group_number_; |
+ |
+ // The pseudo-randomly assigned group number. |
+ // This is kNotParticipating if no group has been assigned. |
+ int group_; |
+ |
+ // A textual name for the randomly selected group, including the Trial name. |
+ // If this Trial is not a member of an group, this string is empty. |
+ std::string group_name_; |
+ |
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. |
@@ -63,12 +159,16 @@ |
// 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); |
+ static FieldTrial* Find(const std::string& name); |
+ static int FindValue(const std::string& name); |
+ |
+ static std::string FindFullName(const std::string& 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 |
+ // data that is gathered before the application has reached sufficient |
// stability (example: most DLL have loaded, etc.) |
static base::Time application_start_time() { |
if (global_) |
@@ -78,7 +178,7 @@ |
} |
private: |
- typedef std::map<std::wstring, FieldTrial*> RegistrationList; |
+ typedef std::map<std::string, FieldTrial*> RegistrationList; |
static FieldTrialList* global_; // The singleton of this class. |