Commit 82322eee authored by Michael Crouse's avatar Michael Crouse Committed by Commit Bot

Add UKM for model features used by the prediction manager.

This change records any requested/used model feature supported by the client
and requested to be used by a prediction model.

Bug: 1052435
Change-Id: I16e8e7b9cab39b173bdc0be5e329fb181853d656
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2057121
Commit-Queue: Michael Crouse <mcrouse@chromium.org>
Reviewed-by: default avatarRobert Kaplow <rkaplow@chromium.org>
Reviewed-by: default avatarTarun Bansal <tbansal@chromium.org>
Reviewed-by: default avatarSophie Chang <sophiechang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#742747}
parent 85a333ec
......@@ -11,6 +11,7 @@
#include "chrome/browser/optimization_guide/optimization_guide_web_contents_observer.h"
#include "components/optimization_guide/hints_processing_util.h"
#include "content/public/browser/navigation_handle.h"
#include "net/nqe/effective_connection_type.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_source.h"
......@@ -192,6 +193,62 @@ void OptimizationGuideNavigationData::RecordOptimizationGuideUKM() const {
}
}
for (const auto model_feature : prediction_model_features_) {
switch (model_feature.first) {
case optimization_guide::proto::CLIENT_MODEL_FEATURE_UNKNOWN: {
continue;
}
case optimization_guide::proto::
CLIENT_MODEL_FEATURE_EFFECTIVE_CONNECTION_TYPE: {
builder.SetPredictionModelFeatureEffectiveConnectionType(
static_cast<net::EffectiveConnectionType>(model_feature.second));
did_record_metric = true;
continue;
}
case optimization_guide::proto::CLIENT_MODEL_FEATURE_PAGE_TRANSITION: {
builder.SetPredictionModelFeaturePageTransition(
static_cast<ui::PageTransition>(model_feature.second));
did_record_metric = true;
continue;
}
case optimization_guide::proto::
CLIENT_MODEL_FEATURE_SITE_ENGAGEMENT_SCORE: {
builder.SetPredictionModelFeatureSiteEngagementScore(
static_cast<int>(std::roundf(model_feature.second / 10.0) * 10));
did_record_metric = true;
continue;
}
case optimization_guide::proto::
CLIENT_MODEL_FEATURE_SAME_ORIGIN_NAVIGATION: {
builder.SetPredictionModelFeatureIsSameOriginNavigation(
static_cast<int>(model_feature.second));
did_record_metric = true;
continue;
}
case optimization_guide::proto::
CLIENT_MODEL_FEATURE_FIRST_CONTENTFUL_PAINT_SESSION_MEAN: {
builder.SetPredictionModelFeatureNavigationToFCPSessionMean(
static_cast<int>(model_feature.second));
did_record_metric = true;
continue;
}
case optimization_guide::proto::
CLIENT_MODEL_FEATURE_FIRST_CONTENTFUL_PAINT_SESSION_STANDARD_DEVIATION: {
builder.SetPredictionModelFeatureNavigationToFCPSessionStdDev(
static_cast<int>(model_feature.second));
did_record_metric = true;
continue;
}
case optimization_guide::proto::
CLIENT_MODEL_FEATURE_FIRST_CONTENTFUL_PAINT_PREVIOUS_PAGE_LOAD: {
builder.SetPredictionModelFeaturePreviousPageLoadNavigationToFCP(
static_cast<int>(model_feature.second));
did_record_metric = true;
continue;
}
}
}
// Record hint metrics.
if (serialized_hint_version_string_.has_value() &&
!serialized_hint_version_string_.value().empty()) {
......@@ -324,3 +381,17 @@ void OptimizationGuideNavigationData::
optimization_target_model_prediction_scores_[optimization_target] =
model_prediction_score;
}
void OptimizationGuideNavigationData::SetValueForModelFeature(
optimization_guide::proto::ClientModelFeature model_feature,
float value) {
prediction_model_features_[model_feature] = value;
}
base::Optional<float>
OptimizationGuideNavigationData::GetValueForModelFeatureForTesting(
optimization_guide::proto::ClientModelFeature model_feature) {
auto it = prediction_model_features_.find(model_feature);
if (it == prediction_model_features_.end())
return base::nullopt;
return it->second;
}
......@@ -83,6 +83,14 @@ class OptimizationGuideNavigationData {
optimization_guide::proto::OptimizationTarget optimization_target,
double model_prediction_score);
// Returns the value of the model feature if it has been provided.
base::Optional<float> GetValueForModelFeatureForTesting(
optimization_guide::proto::ClientModelFeature model_feature);
// Sets the value provided to the model for a particular model feature.
void SetValueForModelFeature(
optimization_guide::proto::ClientModelFeature model_feature,
float value);
// Whether the hint cache had a hint for the navigation before commit.
base::Optional<bool> has_hint_before_commit() const {
return has_hint_before_commit_;
......@@ -195,6 +203,10 @@ class OptimizationGuideNavigationData {
base::flat_map<optimization_guide::proto::OptimizationTarget, double>
optimization_target_model_prediction_scores_;
// The features used to make a prediction for any target.
base::flat_map<optimization_guide::proto::ClientModelFeature, float>
prediction_model_features_;
// Whether the hint cache had a hint for the navigation before commit.
base::Optional<bool> has_hint_before_commit_;
......
......@@ -20,6 +20,13 @@ using testing::AnyOf;
using testing::HasSubstr;
using testing::Not;
typedef struct {
optimization_guide::proto::ClientModelFeature feature;
base::StringPiece ukm_metric_name;
float feature_value;
int expected_value;
} ClientHostModelFeaturesTestCase;
TEST(OptimizationGuideNavigationDataTest, RecordMetricsNoDataNoCommit) {
base::test::TaskEnvironment env;
......@@ -976,3 +983,57 @@ TEST(OptimizationGuideNavigationDataTest, DeepCopy) {
*data_copy.GetModelPredictionScoreForOptimizationTarget(
optimization_guide::proto::OPTIMIZATION_TARGET_PAINFUL_PAGE_LOAD));
}
TEST(OptimizationGuideNavigationDataTest,
RecordMetricsPredictionModelHostModelFeatures) {
base::test::TaskEnvironment env;
ClientHostModelFeaturesTestCase test_cases[] = {
{optimization_guide::proto::
CLIENT_MODEL_FEATURE_FIRST_CONTENTFUL_PAINT_SESSION_MEAN,
ukm::builders::OptimizationGuide::
kPredictionModelFeatureNavigationToFCPSessionMeanName,
2.0, 2},
{optimization_guide::proto::
CLIENT_MODEL_FEATURE_FIRST_CONTENTFUL_PAINT_SESSION_STANDARD_DEVIATION,
ukm::builders::OptimizationGuide::
kPredictionModelFeatureNavigationToFCPSessionStdDevName,
3.0, 3},
{optimization_guide::proto::CLIENT_MODEL_FEATURE_PAGE_TRANSITION,
ukm::builders::OptimizationGuide::
kPredictionModelFeaturePageTransitionName,
20.0, 20},
{optimization_guide::proto::CLIENT_MODEL_FEATURE_SAME_ORIGIN_NAVIGATION,
ukm::builders::OptimizationGuide::
kPredictionModelFeatureIsSameOriginNavigationName,
1.0, 1},
{optimization_guide::proto::CLIENT_MODEL_FEATURE_SITE_ENGAGEMENT_SCORE,
ukm::builders::OptimizationGuide::
kPredictionModelFeatureSiteEngagementScoreName,
5.5, 10},
{optimization_guide::proto::
CLIENT_MODEL_FEATURE_EFFECTIVE_CONNECTION_TYPE,
ukm::builders::OptimizationGuide::
kPredictionModelFeatureEffectiveConnectionTypeName,
3.0, 3},
{optimization_guide::proto::
CLIENT_MODEL_FEATURE_FIRST_CONTENTFUL_PAINT_PREVIOUS_PAGE_LOAD,
ukm::builders::OptimizationGuide::
kPredictionModelFeaturePreviousPageLoadNavigationToFCPName,
200.0, 200},
};
for (const auto& test_case : test_cases) {
ukm::TestAutoSetUkmRecorder ukm_recorder;
OptimizationGuideNavigationData data(/*navigation_id=*/1);
data.SetValueForModelFeature(test_case.feature, test_case.feature_value);
data.RecordMetrics(/*has_committed=*/false);
auto entries = ukm_recorder.GetEntriesByName(
ukm::builders::OptimizationGuide::kEntryName);
EXPECT_EQ(1u, entries.size());
auto* entry = entries[0];
EXPECT_TRUE(ukm_recorder.EntryHasMetric(entry, test_case.ukm_metric_name));
ukm_recorder.ExpectEntryMetric(entry, test_case.ukm_metric_name,
test_case.expected_value);
}
}
......@@ -271,15 +271,19 @@ base::Optional<float> PredictionManager::GetValueForClientFeature(
if (!proto::ClientModelFeature_Parse(model_feature, &client_model_feature))
return base::nullopt;
base::Optional<float> value;
switch (client_model_feature) {
case proto::CLIENT_MODEL_FEATURE_UNKNOWN: {
return base::nullopt;
}
case proto::CLIENT_MODEL_FEATURE_EFFECTIVE_CONNECTION_TYPE: {
return static_cast<float>(current_effective_connection_type_);
value = static_cast<float>(current_effective_connection_type_);
break;
}
case proto::CLIENT_MODEL_FEATURE_PAGE_TRANSITION: {
return static_cast<float>(navigation_handle->GetPageTransition());
value = static_cast<float>(navigation_handle->GetPageTransition());
break;
}
case proto::CLIENT_MODEL_FEATURE_SITE_ENGAGEMENT_SCORE: {
Profile* profile = Profile::FromBrowserContext(
......@@ -287,8 +291,9 @@ base::Optional<float> PredictionManager::GetValueForClientFeature(
SiteEngagementService* engagement_service =
SiteEngagementService::Get(profile);
// Precision loss is acceptable/expected for prediction models.
return static_cast<float>(
value = static_cast<float>(
engagement_service->GetScore(navigation_handle->GetURL()));
break;
}
case proto::CLIENT_MODEL_FEATURE_SAME_ORIGIN_NAVIGATION: {
OptimizationGuideNavigationData* nav_data =
......@@ -300,32 +305,43 @@ base::Optional<float> PredictionManager::GetValueForClientFeature(
LOCAL_HISTOGRAM_BOOLEAN(
"OptimizationGuide.PredictionManager.IsSameOrigin", is_same_origin);
return static_cast<float>(is_same_origin);
value = static_cast<float>(is_same_origin);
break;
}
case proto::CLIENT_MODEL_FEATURE_FIRST_CONTENTFUL_PAINT_SESSION_MEAN: {
if (session_fcp_.GetNumberOfSamples() == 0) {
return static_cast<float>(
pref_service_->GetDouble(prefs::kSessionStatisticFCPMean));
}
return session_fcp_.GetMean();
value = session_fcp_.GetNumberOfSamples() == 0
? static_cast<float>(pref_service_->GetDouble(
prefs::kSessionStatisticFCPMean))
: session_fcp_.GetMean();
break;
}
case proto::
CLIENT_MODEL_FEATURE_FIRST_CONTENTFUL_PAINT_SESSION_STANDARD_DEVIATION: {
if (session_fcp_.GetNumberOfSamples() == 0) {
return static_cast<float>(
pref_service_->GetDouble(prefs::kSessionStatisticFCPStdDev));
}
return session_fcp_.GetStdDev();
value = session_fcp_.GetNumberOfSamples() == 0
? static_cast<float>(pref_service_->GetDouble(
prefs::kSessionStatisticFCPStdDev))
: session_fcp_.GetStdDev();
break;
}
case proto::
CLIENT_MODEL_FEATURE_FIRST_CONTENTFUL_PAINT_PREVIOUS_PAGE_LOAD: {
return previous_load_fcp_ms_.value_or(static_cast<float>(
value = previous_load_fcp_ms_.value_or(static_cast<float>(
pref_service_->GetDouble(prefs::kSessionStatisticFCPMean)));
break;
}
default: {
return base::nullopt;
}
}
OptimizationGuideNavigationData* navigation_data =
OptimizationGuideNavigationData::GetFromNavigationHandle(
navigation_handle);
if (value && navigation_data) {
navigation_data->SetValueForModelFeature(client_model_feature, *value);
return value;
}
return base::nullopt;
}
base::flat_map<std::string, float> PredictionManager::BuildFeatureMap(
......
......@@ -468,6 +468,10 @@ class PredictionManagerTest
return GetParam() == proto::CLIENT_MODEL_FEATURE_SAME_ORIGIN_NAVIGATION;
}
bool IsUnknownFeature() {
return GetParam() == proto::CLIENT_MODEL_FEATURE_UNKNOWN;
}
void TearDown() override {
optimization_guide::ProtoDatabaseProviderTestBase::TearDown();
}
......@@ -1499,6 +1503,16 @@ TEST_P(PredictionManagerTest, ClientFeature) {
EXPECT_TRUE(test_prediction_model);
EXPECT_TRUE(test_prediction_model->WasModelEvaluated());
OptimizationGuideNavigationData* navigation_data =
OptimizationGuideNavigationData::GetFromNavigationHandle(
navigation_handle.get());
EXPECT_TRUE(navigation_data);
if (IsUnknownFeature()) {
EXPECT_FALSE(
navigation_data->GetValueForModelFeatureForTesting(GetParam()));
} else {
EXPECT_TRUE(navigation_data->GetValueForModelFeatureForTesting(GetParam()));
}
}
INSTANTIATE_TEST_SUITE_P(ClientFeature,
......
......@@ -6566,6 +6566,49 @@ be describing additional metrics about the same event.
evaluated for the page load.
</summary>
</metric>
<metric name="PredictionModelFeatureEffectiveConnectionType">
<summary>
The |net::EffectiveConnectionType| at the time a prediction was requested.
Recorded once per page load.
</summary>
</metric>
<metric name="PredictionModelFeatureIsSameOriginNavigation">
<summary>
Whether the current navigatation is the same origin as the previous one at
the time a prediction was requested. Recorded once per page load.
</summary>
</metric>
<metric name="PredictionModelFeatureNavigationToFCPSessionMean">
<summary>
The average navigation to first contentful paint for the current session
at the time a prediction was requested. Recorded once per page load.
</summary>
</metric>
<metric name="PredictionModelFeatureNavigationToFCPSessionStdDev">
<summary>
The standard deviation of navigation to first contentful paint for page
loads in the current session at the time a prediction was requested.
Recorded once per page load.
</summary>
</metric>
<metric name="PredictionModelFeaturePageTransition">
<summary>
The |ui::PageTransition| of the current navigation at the time a
prediction was requested. Recorded once per page load.
</summary>
</metric>
<metric name="PredictionModelFeaturePreviousPageLoadNavigationToFCP">
<summary>
The navigation to to first contentful paint for the previous page load at
the time a prediction was requested. Recorded once per page load.
</summary>
</metric>
<metric name="PredictionModelFeatureSiteEngagementScore">
<summary>
The site engagement score of the host of the current navigation at the
time a prediction was requested. Recorded once per page load.
</summary>
</metric>
</event>
<event name="PageDomainInfo">
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment