diff options
-rw-r--r-- | tensorflow/core/BUILD | 3 | ||||
-rw-r--r-- | tensorflow/core/lib/monitoring/collected_metrics.h | 1 | ||||
-rw-r--r-- | tensorflow/core/lib/monitoring/collection_registry.h | 6 | ||||
-rw-r--r-- | tensorflow/core/lib/monitoring/collection_registry_test.cc | 91 | ||||
-rw-r--r-- | tensorflow/core/lib/monitoring/counter.h | 2 | ||||
-rw-r--r-- | tensorflow/core/lib/monitoring/gauge.h | 215 | ||||
-rw-r--r-- | tensorflow/core/lib/monitoring/gauge_test.cc | 92 | ||||
-rw-r--r-- | tensorflow/core/lib/monitoring/metric_def.h | 12 | ||||
-rw-r--r-- | tensorflow/core/lib/monitoring/mobile_gauge.h | 72 | ||||
-rw-r--r-- | tensorflow/core/lib/monitoring/sampler.h | 5 |
10 files changed, 493 insertions, 6 deletions
diff --git a/tensorflow/core/BUILD b/tensorflow/core/BUILD index 18d69fceb3..01ddbfc2d4 100644 --- a/tensorflow/core/BUILD +++ b/tensorflow/core/BUILD @@ -320,6 +320,7 @@ cc_library( "lib/io/table_options.h", "lib/math/math_util.h", "lib/monitoring/counter.h", + "lib/monitoring/gauge.h", "lib/monitoring/sampler.h", "lib/random/distribution_sampler.h", "lib/random/philox_random.h", @@ -1393,6 +1394,7 @@ LIB_INTERNAL_PUBLIC_HEADERS = tf_additional_lib_hdrs() + [ "lib/monitoring/collection_registry.h", "lib/monitoring/metric_def.h", "lib/monitoring/mobile_counter.h", + "lib/monitoring/mobile_gauge.h", "lib/monitoring/mobile_sampler.h", "lib/png/png_io.h", "lib/random/random.h", @@ -2369,6 +2371,7 @@ tf_cc_tests( "lib/math/math_util_test.cc", "lib/monitoring/collection_registry_test.cc", "lib/monitoring/counter_test.cc", + "lib/monitoring/gauge_test.cc", "lib/monitoring/metric_def_test.cc", "lib/monitoring/sampler_test.cc", "lib/random/distribution_sampler_test.cc", diff --git a/tensorflow/core/lib/monitoring/collected_metrics.h b/tensorflow/core/lib/monitoring/collected_metrics.h index 3dde55342e..fbef25619f 100644 --- a/tensorflow/core/lib/monitoring/collected_metrics.h +++ b/tensorflow/core/lib/monitoring/collected_metrics.h @@ -87,6 +87,7 @@ struct Point { // The actual metric value, dependent on the value_type enum. ValueType value_type; int64 int64_value; + string string_value; HistogramProto histogram_value; // start_timestamp and end_timestamp indicate the time period over which this diff --git a/tensorflow/core/lib/monitoring/collection_registry.h b/tensorflow/core/lib/monitoring/collection_registry.h index 2eff468436..030f8e360a 100644 --- a/tensorflow/core/lib/monitoring/collection_registry.h +++ b/tensorflow/core/lib/monitoring/collection_registry.h @@ -219,6 +219,12 @@ inline void CollectValue(const int64& value, Point* const point) { } template <> +inline void CollectValue(const string& value, Point* const point) { + point->value_type = ValueType::kString; + point->string_value = value; +} + +template <> inline void CollectValue(const HistogramProto& value, Point* const point) { point->value_type = ValueType::kHistogram; // This is inefficient. If and when we hit snags, we can change the API to do diff --git a/tensorflow/core/lib/monitoring/collection_registry_test.cc b/tensorflow/core/lib/monitoring/collection_registry_test.cc index 5b9c100690..ca25f508da 100644 --- a/tensorflow/core/lib/monitoring/collection_registry_test.cc +++ b/tensorflow/core/lib/monitoring/collection_registry_test.cc @@ -16,6 +16,7 @@ limitations under the License. #include "tensorflow/core/lib/monitoring/collection_registry.h" #include "tensorflow/core/lib/monitoring/counter.h" +#include "tensorflow/core/lib/monitoring/gauge.h" #include "tensorflow/core/lib/monitoring/sampler.h" #include "tensorflow/core/lib/strings/strcat.h" #include "tensorflow/core/platform/protobuf.h" @@ -176,6 +177,96 @@ TEST(CollectMetricsTest, Counter) { } } +TEST(CollectMetricsTest, Gauge) { + auto string_gauge_with_labels = + std::unique_ptr<Gauge<string, 2>>(Gauge<string, 2>::New( + "/tensorflow/test/string_gauge_with_labels", + "String gauge with labels.", "MyLabel0", "MyLabel1")); + auto inteter_gauge_without_labels = std::unique_ptr<Gauge<int64, 0>>( + Gauge<int64, 0>::New("/tensorflow/test/integer_gauge_without_labels", + "Integer gauge without labels.")); + + string_gauge_with_labels->GetCell("Label00", "Label10")->Set("test1"); + string_gauge_with_labels->GetCell("Label01", "Label11")->Set("test2"); + inteter_gauge_without_labels->GetCell()->Set(7); + + for (const bool collect_metric_descriptors : {true, false}) { + SCOPED_TRACE(strings::StrCat("collect_metric_descriptors: ", + collect_metric_descriptors)); + + auto* collection_registry = CollectionRegistry::Default(); + CollectionRegistry::CollectMetricsOptions options; + options.collect_metric_descriptors = collect_metric_descriptors; + const std::unique_ptr<CollectedMetrics> collected_metrics = + collection_registry->CollectMetrics(options); + + if (collect_metric_descriptors) { + ASSERT_EQ(2, collected_metrics->metric_descriptor_map.size()); + + const MetricDescriptor& ld = *collected_metrics->metric_descriptor_map.at( + "/tensorflow/test/string_gauge_with_labels"); + EXPECT_EQ("/tensorflow/test/string_gauge_with_labels", ld.name); + EXPECT_EQ("String gauge with labels.", ld.description); + ASSERT_EQ(2, ld.label_names.size()); + EXPECT_EQ("MyLabel0", ld.label_names[0]); + EXPECT_EQ("MyLabel1", ld.label_names[1]); + EXPECT_EQ(MetricKind::kGauge, ld.metric_kind); + EXPECT_EQ(ValueType::kString, ld.value_type); + + const MetricDescriptor& ud = *collected_metrics->metric_descriptor_map.at( + "/tensorflow/test/integer_gauge_without_labels"); + EXPECT_EQ("/tensorflow/test/integer_gauge_without_labels", ud.name); + EXPECT_EQ("Integer gauge without labels.", ud.description); + ASSERT_EQ(0, ud.label_names.size()); + EXPECT_EQ(MetricKind::kGauge, ud.metric_kind); + EXPECT_EQ(ValueType::kInt64, ud.value_type); + } else { + EXPECT_EQ(0, collected_metrics->metric_descriptor_map.size()); + } + + ASSERT_EQ(2, collected_metrics->point_set_map.size()); + + const PointSet& lps = *collected_metrics->point_set_map.at( + "/tensorflow/test/string_gauge_with_labels"); + EXPECT_EQ("/tensorflow/test/string_gauge_with_labels", lps.metric_name); + ASSERT_EQ(2, lps.points.size()); + ASSERT_EQ(2, lps.points[0]->labels.size()); + EXPECT_EQ("MyLabel0", lps.points[0]->labels[0].name); + EXPECT_EQ("Label00", lps.points[0]->labels[0].value); + EXPECT_EQ("MyLabel1", lps.points[0]->labels[1].name); + EXPECT_EQ("Label10", lps.points[0]->labels[1].value); + EXPECT_EQ(ValueType::kString, lps.points[0]->value_type); + EXPECT_EQ("test1", lps.points[0]->string_value); + EXPECT_LT(0, lps.points[0]->start_timestamp_millis); + EXPECT_LT(0, lps.points[0]->end_timestamp_millis); + EXPECT_GE(lps.points[0]->end_timestamp_millis, + lps.points[0]->start_timestamp_millis); + ASSERT_EQ(2, lps.points[1]->labels.size()); + EXPECT_EQ("MyLabel0", lps.points[1]->labels[0].name); + EXPECT_EQ("Label01", lps.points[1]->labels[0].value); + EXPECT_EQ("MyLabel1", lps.points[1]->labels[1].name); + EXPECT_EQ("Label11", lps.points[1]->labels[1].value); + EXPECT_EQ(ValueType::kString, lps.points[1]->value_type); + EXPECT_EQ("test2", lps.points[1]->string_value); + EXPECT_LT(0, lps.points[1]->start_timestamp_millis); + EXPECT_LT(0, lps.points[1]->end_timestamp_millis); + EXPECT_GE(lps.points[1]->end_timestamp_millis, + lps.points[1]->start_timestamp_millis); + + const PointSet& ups = *collected_metrics->point_set_map.at( + "/tensorflow/test/integer_gauge_without_labels"); + EXPECT_EQ("/tensorflow/test/integer_gauge_without_labels", ups.metric_name); + ASSERT_EQ(1, ups.points.size()); + EXPECT_EQ(0, ups.points[0]->labels.size()); + EXPECT_EQ(ValueType::kInt64, ups.points[0]->value_type); + EXPECT_EQ(7, ups.points[0]->int64_value); + EXPECT_LT(0, ups.points[0]->start_timestamp_millis); + EXPECT_LT(0, ups.points[0]->end_timestamp_millis); + EXPECT_GE(ups.points[0]->end_timestamp_millis, + ups.points[0]->start_timestamp_millis); + } +} + void EqHistograms(const Histogram& expected, const HistogramProto& actual_proto) { Histogram actual; diff --git a/tensorflow/core/lib/monitoring/counter.h b/tensorflow/core/lib/monitoring/counter.h index 4b84e9d928..7240348a9b 100644 --- a/tensorflow/core/lib/monitoring/counter.h +++ b/tensorflow/core/lib/monitoring/counter.h @@ -48,7 +48,7 @@ namespace monitoring { // This class is thread-safe. class CounterCell { public: - CounterCell(const int64 value) : value_(value) {} + CounterCell(int64 value) : value_(value) {} ~CounterCell() {} // Atomically increments the value by step. diff --git a/tensorflow/core/lib/monitoring/gauge.h b/tensorflow/core/lib/monitoring/gauge.h new file mode 100644 index 0000000000..75471cfb22 --- /dev/null +++ b/tensorflow/core/lib/monitoring/gauge.h @@ -0,0 +1,215 @@ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#ifndef THIRD_PARTY_TENSORFLOW_CORE_LIB_MONITORING_GAUGE_H_ +#define THIRD_PARTY_TENSORFLOW_CORE_LIB_MONITORING_GAUGE_H_ + +// We replace this implementation with a null implementation for mobile +// platforms. +#include "tensorflow/core/platform/platform.h" +#ifdef IS_MOBILE_PLATFORM +#include "tensorflow/core/lib/monitoring/mobile_gauge.h" +#else + +#include <array> +#include <atomic> +#include <map> + +#include "tensorflow/core/lib/monitoring/collection_registry.h" +#include "tensorflow/core/lib/monitoring/metric_def.h" +#include "tensorflow/core/platform/macros.h" +#include "tensorflow/core/platform/mutex.h" +#include "tensorflow/core/platform/thread_annotations.h" +#include "tensorflow/core/platform/types.h" + +namespace tensorflow { +namespace monitoring { + +// GaugeCell stores each value of a gauge. +// +// A cell can be passed off to a module which may repeatedly update it without +// needing further map-indexing computations. This improves both encapsulation +// (separate modules can own a cell each, without needing to know about the map +// to which both cells belong) and performance (since map indexing and +// associated locking are both avoided). +// +// This class is thread-safe. +template <typename T> +class GaugeCell { + public: + explicit GaugeCell(const T& value) : value_(value) {} + ~GaugeCell() {} + + // Atomically sets the value. + void Set(const T& value) LOCKS_EXCLUDED(mu_); + + // Retrieves the current value. + T value() const LOCKS_EXCLUDED(mu_); + + private: + T value_ GUARDED_BY(mu_); + mutable mutex mu_; + + TF_DISALLOW_COPY_AND_ASSIGN(GaugeCell); +}; + +// Explicit specialization of GaugeCell<int64>. Compared to the primary +// template, it uses atomic values as opposed to mutex. This class is +// thread-safe. +template <> +class GaugeCell<int64> { + public: + explicit GaugeCell(int64 value) : value_(value) {} + ~GaugeCell() {} + + // Atomically sets the value. + void Set(int64 value); + + // Retrieves the current value. + int64 value() const; + + private: + std::atomic<int64> value_; + + TF_DISALLOW_COPY_AND_ASSIGN(GaugeCell); +}; + +// A stateful class for updating a gauge-like metric. Allowed ValueType are +// int64 and string. +// +// This class encapsulates a set of values (or a single value for a label-less +// metric). Each value is identified by a tuple of labels. The class allows the +// user to set each value. +// +// Gauge allocates storage and maintains a cell for each value. You can +// retrieve an individual cell using a label-tuple and update it separately. +// This improves performance since operations related to retrieval, like +// map-indexing and locking, are avoided. +// +// This class is thread-safe. +template <typename ValueType, int NumLabels> +class Gauge { + public: + ~Gauge() { + // Deleted here, before the metric_def is destroyed. + registration_handle_.reset(); + } + + // Creates the metric based on the metric-definition arguments. + // + // Example: + // + // auto* string_gauge_with_label = Gauge<string,1>::New( + // "/tensorflow/string_gauge_with_label", + // "String gauge with one label.", "MyLabelName"); + // + // auto* integer_gauge = Gauge<int64, 0>::New("/tensorflow/integer_gauge", + // "Integer gauge") + template <typename... MetricDefArgs> + static Gauge* New(MetricDefArgs&&... metric_def_args); + + // Retrieves the cell for the specified labels, creating it on demand if not + // already present. + template <typename... Labels> + GaugeCell<ValueType>* GetCell(const Labels&... labels) LOCKS_EXCLUDED(mu_); + + private: + explicit Gauge( + const MetricDef<MetricKind::kGauge, ValueType, NumLabels>& metric_def) + : metric_def_(metric_def), + registration_handle_(CollectionRegistry::Default()->Register( + &metric_def_, [&](MetricCollectorGetter getter) { + auto metric_collector = getter.Get(&metric_def_); + + mutex_lock l(mu_); + for (const auto& cell : cells_) { + metric_collector.CollectValue(cell.first, cell.second.value()); + } + })) {} + + mutable mutex mu_; + + // The metric definition. This will be used to identify the metric when we + // register it for collection. + const MetricDef<MetricKind::kGauge, ValueType, NumLabels> metric_def_; + + std::unique_ptr<CollectionRegistry::RegistrationHandle> registration_handle_; + + using LabelArray = std::array<string, NumLabels>; + std::map<LabelArray, GaugeCell<ValueType> > cells_ GUARDED_BY(mu_); + + TF_DISALLOW_COPY_AND_ASSIGN(Gauge); +}; + +//// +// Implementation details follow. API readers may skip. +//// +template <typename T> +void GaugeCell<T>::Set(const T& value) { + mutex_lock l(mu_); + value_ = value; +} + +template <typename T> +T GaugeCell<T>::value() const { + mutex_lock l(mu_); + return value_; +} + +inline void GaugeCell<int64>::Set(int64 value) { value_ = value; } + +inline int64 GaugeCell<int64>::value() const { return value_; } + +template <typename ValueType, int NumLabels> +template <typename... MetricDefArgs> +Gauge<ValueType, NumLabels>* Gauge<ValueType, NumLabels>::New( + MetricDefArgs&&... metric_def_args) { + static_assert(std::is_same<ValueType, int64>::value || + std::is_same<ValueType, string>::value, + "Gauge only allows int64 and string types."); + return new Gauge<ValueType, NumLabels>( + MetricDef<MetricKind::kGauge, ValueType, NumLabels>( + std::forward<MetricDefArgs>(metric_def_args)...)); +} + +template <typename ValueType, int NumLabels> +template <typename... Labels> +GaugeCell<ValueType>* Gauge<ValueType, NumLabels>::GetCell( + const Labels&... labels) LOCKS_EXCLUDED(mu_) { + // Provides a more informative error message than the one during array + // construction below. + static_assert( + sizeof...(Labels) == NumLabels, + "Mismatch between Gauge<ValueType, NumLabels> and number of labels " + "provided in GetCell(...)."); + + const LabelArray& label_array = {{labels...}}; + mutex_lock l(mu_); + const auto found_it = cells_.find(label_array); + if (found_it != cells_.end()) { + return &(found_it->second); + } + return &(cells_ + .emplace(std::piecewise_construct, + std::forward_as_tuple(label_array), + std::forward_as_tuple(ValueType())) + .first->second); +} + +} // namespace monitoring +} // namespace tensorflow + +#endif // IS_MOBILE_PLATFORM +#endif // THIRD_PARTY_TENSORFLOW_CORE_LIB_MONITORING_GAUGE_H_ diff --git a/tensorflow/core/lib/monitoring/gauge_test.cc b/tensorflow/core/lib/monitoring/gauge_test.cc new file mode 100644 index 0000000000..f98cfe2a3b --- /dev/null +++ b/tensorflow/core/lib/monitoring/gauge_test.cc @@ -0,0 +1,92 @@ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +#include "tensorflow/core/lib/monitoring/gauge.h" + +#include "tensorflow/core/platform/test.h" + +namespace tensorflow { +namespace monitoring { +namespace { + +auto* gauge_with_labels = Gauge<int64, 1>::New( + "/tensorflow/test/gauge_with_labels", "Gauge with one label.", "MyLabel"); + +TEST(LabeledGaugeTest, InitializedWithZero) { + EXPECT_EQ(0, gauge_with_labels->GetCell("Empty")->value()); +} + +TEST(LabeledGaugeTest, GetCell) { + auto* cell = gauge_with_labels->GetCell("GetCellOp"); + EXPECT_EQ(0, cell->value()); + + cell->Set(1); + EXPECT_EQ(1, cell->value()); + + auto* same_cell = gauge_with_labels->GetCell("GetCellOp"); + EXPECT_EQ(1, same_cell->value()); + + same_cell->Set(10); + EXPECT_EQ(10, cell->value()); + EXPECT_EQ(10, same_cell->value()); +} + +auto* gauge_without_labels = Gauge<int64, 0>::New( + "/tensorflow/test/gauge_without_labels", "Gauge without any labels."); + +TEST(UnlabeledGaugeTest, InitializedWithZero) { + EXPECT_EQ(0, gauge_without_labels->GetCell()->value()); +} + +TEST(UnlabeledGaugeTest, GetCell) { + auto* cell = gauge_without_labels->GetCell(); + EXPECT_EQ(0, cell->value()); + + cell->Set(1); + EXPECT_EQ(1, cell->value()); + + auto* same_cell = gauge_without_labels->GetCell(); + EXPECT_EQ(1, same_cell->value()); + + same_cell->Set(10); + EXPECT_EQ(10, cell->value()); + EXPECT_EQ(10, same_cell->value()); +} + +auto* string_gauge = Gauge<string, 0>::New("/tensorflow/test/string_gauge", + "Gauge of string value."); + +TEST(GaugeOfStringValue, InitializedWithEmptyString) { + EXPECT_EQ("", string_gauge->GetCell()->value()); +} + +TEST(GaugeOfStringValue, GetCell) { + auto* cell = string_gauge->GetCell(); + EXPECT_EQ("", cell->value()); + + cell->Set("foo"); + EXPECT_EQ("foo", cell->value()); + + auto* same_cell = string_gauge->GetCell(); + EXPECT_EQ("foo", cell->value()); + + same_cell->Set("bar"); + EXPECT_EQ("bar", cell->value()); + EXPECT_EQ("bar", same_cell->value()); +} + +} // namespace +} // namespace monitoring +} // namespace tensorflow diff --git a/tensorflow/core/lib/monitoring/metric_def.h b/tensorflow/core/lib/monitoring/metric_def.h index 116a73823d..3459c2ab82 100644 --- a/tensorflow/core/lib/monitoring/metric_def.h +++ b/tensorflow/core/lib/monitoring/metric_def.h @@ -28,15 +28,16 @@ namespace monitoring { // The different metric kinds available. // // Gauge indicates that the metric's values are instantaneous measurements of a -// (typically) continuously varying quantity. Examples: a process's current heap -// size, a queue's current length. +// (typically) continuously varying quantity or a string value. Examples: a +// process's current heap size, a queue's current length, the name of the binary +// used by a process. // // Cumulative indicates that the metric's values represent non-negative changes // over specified time periods. Example: the number of rpc calls to a service. enum class MetricKind : int { kGauge = 0, kCumulative }; // The type of the metric values. -enum class ValueType : int { kInt64 = 0, kHistogram }; +enum class ValueType : int { kInt64 = 0, kHistogram, kString }; // Everything in the internal namespace is implementation details. Do not depend // on this. @@ -73,6 +74,11 @@ inline ValueType GetValueType<HistogramProto>() { return ValueType::kHistogram; } +template <> +inline ValueType GetValueType<string>() { + return ValueType::kString; +} + } // namespace internal // Abstract base class for a metric definition. diff --git a/tensorflow/core/lib/monitoring/mobile_gauge.h b/tensorflow/core/lib/monitoring/mobile_gauge.h new file mode 100644 index 0000000000..ac13ad35c0 --- /dev/null +++ b/tensorflow/core/lib/monitoring/mobile_gauge.h @@ -0,0 +1,72 @@ +/* Copyright 2017 The TensorFlow Authors. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +==============================================================================*/ + +// Null implementation of the Gauge metric for mobile platforms. + +#ifndef THIRD_PARTY_TENSORFLOW_CORE_LIB_MONITORING_MOBILE_GAUGE_H_ +#define THIRD_PARTY_TENSORFLOW_CORE_LIB_MONITORING_MOBILE_GAUGE_H_ + +#include "tensorflow/core/platform/macros.h" +#include "tensorflow/core/platform/types.h" + +namespace tensorflow { +namespace monitoring { + +// GaugeCell which has a null implementation. +template <typename T> +class GaugeCell { + public: + public: + GaugeCell() {} + ~GaugeCell() {} + + void Set(const T& value) {} + T value() const { return T(); } + + private: + TF_DISALLOW_COPY_AND_ASSIGN(GaugeCell); +}; + +// Gauge which has a null implementation. +template <typename ValueType, int NumLabels> +class Gauge { + public: + ~Gauge() {} + + template <typename... MetricDefArgs> + static Gauge* New(MetricDefArgs&&... metric_def_args) { + static_assert(std::is_same<ValueType, int64>::value || + std::is_same<ValueType, string>::value, + "Gauge only allows int64 and string types."); + return new Gauge(); + } + + template <typename... Labels> + GaugeCell<ValueType>* GetCell(const Labels&... labels) { + return &default_gauge_cell_; + } + + private: + Gauge() {} + + GaugeCell<ValueType> default_gauge_cell_; + + TF_DISALLOW_COPY_AND_ASSIGN(Gauge); +}; + +} // namespace monitoring +} // namespace tensorflow + +#endif // THIRD_PARTY_TENSORFLOW_CORE_LIB_MONITORING_MOBILE_GAUGE_H_ diff --git a/tensorflow/core/lib/monitoring/sampler.h b/tensorflow/core/lib/monitoring/sampler.h index 5a4d49d5d4..c7a05428e2 100644 --- a/tensorflow/core/lib/monitoring/sampler.h +++ b/tensorflow/core/lib/monitoring/sampler.h @@ -159,9 +159,10 @@ class Sampler { // Registration handle with the CollectionRegistry. std::unique_ptr<CollectionRegistry::RegistrationHandle> registration_handle_; - // We use a std::map here because we give out pointers to the SamplerCells, - // which need to remain valid even after more cells. using LabelArray = std::array<string, NumLabels>; + // we need a container here that guarantees pointer stability of the value, + // namely, the pointer of the value should remain valid even after more cells + // are inserted. std::map<LabelArray, SamplerCell> cells_ GUARDED_BY(mu_); TF_DISALLOW_COPY_AND_ASSIGN(Sampler); |