Metric definition for the monitoring API.

- Implements MetricDef, and AbstractMetricDef.
- A metric is defined by its kind, name, description and the description of
its labels.
- Moves CounterCell's implementation from cc file to header to allow inlining.
Change: 128763990
This commit is contained in:
Vinu Rajashekhar 2016-07-28 17:05:02 -08:00 committed by TensorFlower Gardener
parent f1acb3bd82
commit 3aaeb88b29
5 changed files with 163 additions and 43 deletions

View File

@ -156,6 +156,7 @@ cc_library(
"lib/io/table_options.h",
"lib/jpeg/jpeg_mem.h",
"lib/monitoring/counter.h",
"lib/monitoring/metric_def.h",
"lib/random/distribution_sampler.h",
"lib/random/philox_random.h",
"lib/random/simple_philox.h", # TODO(josh11b): make internal

View File

@ -1,31 +0,0 @@
/* Copyright 2016 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/counter.h"
#include "tensorflow/core/platform/logging.h"
namespace tensorflow {
namespace monitoring {
void CounterCell::IncrementBy(const int64 step) {
DCHECK_LE(0, step) << "Must not decrement cumulative metrics.";
value_ += step;
}
int64 CounterCell::value() const { return value_; }
} // namespace monitoring
} // namespace tensorflow

View File

@ -20,6 +20,8 @@ limitations under the License.
#include <atomic>
#include <map>
#include "tensorflow/core/lib/monitoring/metric_def.h"
#include "tensorflow/core/platform/logging.h"
#include "tensorflow/core/platform/macros.h"
#include "tensorflow/core/platform/mutex.h"
#include "tensorflow/core/platform/thread_annotations.h"
@ -71,9 +73,12 @@ class CounterCell {
template <int NumLabels>
class Counter {
public:
Counter() {}
~Counter() {}
explicit Counter(
const MetricDef<MetricKind::CUMULATIVE, int64, NumLabels>& metric_def)
: metric_def_(metric_def) {}
// Retrieves the cell for the specified labels, creating it on demand if
// not already present.
template <typename... Labels>
@ -82,6 +87,10 @@ class Counter {
private:
mutable mutex mu_;
// The metric definition. This will be used to identify the metric when we
// register it for exporting.
const MetricDef<MetricKind::CUMULATIVE, int64, NumLabels> metric_def_;
using LabelArray = std::array<string, NumLabels>;
std::map<LabelArray, CounterCell> cells_ GUARDED_BY(mu_);
@ -92,6 +101,13 @@ class Counter {
// Implementation details follow. API readers may skip.
////
inline void CounterCell::IncrementBy(const int64 step) {
DCHECK_LE(0, step) << "Must not decrement cumulative metrics.";
value_ += step;
}
inline int64 CounterCell::value() const { return value_; }
template <int NumLabels>
template <typename... Labels>
CounterCell* Counter<NumLabels>::GetCell(const Labels&... labels)

View File

@ -19,26 +19,28 @@ limitations under the License.
namespace tensorflow {
namespace monitoring {
namespace {
class LabeledCounterTest : public ::testing::Test {
protected:
LabeledCounterTest() {}
Counter<1> counter_;
Counter<1> counter_with_labels_{{"/tensorflow/test/counter_with_labels_",
"Counter with one label.", "One label"}};
};
TEST_F(LabeledCounterTest, InitializedWithZero) {
EXPECT_EQ(0, counter_.GetCell("Empty")->value());
EXPECT_EQ(0, counter_with_labels_.GetCell("Empty")->value());
}
TEST_F(LabeledCounterTest, GetCell) {
auto* cell = counter_.GetCell("GetCellOp");
auto* cell = counter_with_labels_.GetCell("GetCellOp");
EXPECT_EQ(0, cell->value());
cell->IncrementBy(42);
EXPECT_EQ(42, cell->value());
auto* same_cell = counter_.GetCell("GetCellOp");
auto* same_cell = counter_with_labels_.GetCell("GetCellOp");
EXPECT_EQ(42, same_cell->value());
same_cell->IncrementBy(58);
@ -49,29 +51,31 @@ TEST_F(LabeledCounterTest, GetCell) {
using LabeledCounterDeathTest = LabeledCounterTest;
TEST_F(LabeledCounterDeathTest, DiesOnDecrement) {
EXPECT_DEBUG_DEATH({ counter_.GetCell("DyingOp")->IncrementBy(-1); },
"decrement");
EXPECT_DEBUG_DEATH(
{ counter_with_labels_.GetCell("DyingOp")->IncrementBy(-1); },
"decrement");
}
class UnlabeledCounterTest : public ::testing::Test {
protected:
UnlabeledCounterTest() {}
Counter<0> counter_;
Counter<0> counter_without_labels_{
{"/tensorflow/test/counter0", "Counter without any labels."}};
};
TEST_F(UnlabeledCounterTest, InitializedWithZero) {
EXPECT_EQ(0, counter_.GetCell()->value());
EXPECT_EQ(0, counter_without_labels_.GetCell()->value());
}
TEST_F(UnlabeledCounterTest, GetCell) {
auto* cell = counter_.GetCell();
auto* cell = counter_without_labels_.GetCell();
EXPECT_EQ(0, cell->value());
cell->IncrementBy(42);
EXPECT_EQ(42, cell->value());
auto* same_cell = counter_.GetCell();
auto* same_cell = counter_without_labels_.GetCell();
EXPECT_EQ(42, same_cell->value());
same_cell->IncrementBy(58);
@ -82,8 +86,10 @@ TEST_F(UnlabeledCounterTest, GetCell) {
using UnlabeledCounterDeathTest = UnlabeledCounterTest;
TEST_F(UnlabeledCounterDeathTest, DiesOnDecrement) {
EXPECT_DEBUG_DEATH({ counter_.GetCell()->IncrementBy(-1); }, "decrement");
EXPECT_DEBUG_DEATH({ counter_without_labels_.GetCell()->IncrementBy(-1); },
"decrement");
}
} // namespace
} // namespace monitoring
} // namespace tensorflow

View File

@ -0,0 +1,128 @@
/* Copyright 2016 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_METRIC_DEF_H_
#define THIRD_PARTY_TENSORFLOW_CORE_LIB_MONITORING_METRIC_DEF_H_
#include <array>
#include <vector>
#include "tensorflow/core/lib/core/stringpiece.h"
namespace tensorflow {
namespace monitoring {
// Everything in the internal namespace is implementation details. Do not depend
// on this.
namespace internal {
// Ensures that the string is a compile-time string literal.
class StringLiteral {
public:
// We allow implicit conversions here on purpose.
template <int N>
StringLiteral(const char (&data)[N]) : literal_(data, N) {}
// This ctor will be called for non-literals, causing compile-time failure.
template <typename NotStringLiteral>
StringLiteral(const NotStringLiteral& not_string_literal) = delete;
// Implicit conversion to StringPiece.
operator StringPiece() const { return literal_; }
private:
const StringPiece literal_;
};
} // namespace internal
// 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.
//
// 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 MetricKind { GAUGE, CUMULATIVE };
// Abstract base class for a metric definition.
//
// Unlike MetricDef, this class is non-templatized and allows storing and
// accessing metric definitions without the full type information.
//
// Everything except the value type of a metric is stored here. Please read
// MetricDef class comments for more details.
class AbstractMetricDef {
public:
MetricKind kind() const { return kind_; }
StringPiece name() const { return name_; }
StringPiece description() const { return description_; }
const std::vector<StringPiece> label_descriptions() const {
return label_descriptions_;
}
private:
template <MetricKind kind, typename Value, int NumLabels>
friend class MetricDef;
AbstractMetricDef(
const MetricKind kind, const internal::StringLiteral name,
const internal::StringLiteral description,
const std::vector<internal::StringLiteral>& label_descriptions)
: kind_(kind),
name_(name),
description_(description),
label_descriptions_(
{label_descriptions.begin(), label_descriptions.end()}) {}
const MetricKind kind_;
const StringPiece name_;
const StringPiece description_;
const std::vector<StringPiece> label_descriptions_;
};
// Metric definition.
//
// A metric is defined by its kind, value-type, name, description and the
// description of its labels.
//
// NOTE: We allow only string literals for the name, description and label
// descriptions because these should be fixed at compile-time and shouldn't be
// dynamic.
template <MetricKind metric_kind, typename Value, int NumLabels>
class MetricDef : public AbstractMetricDef {
public:
using value_type = Value;
template <typename... LabelDesc>
MetricDef(const internal::StringLiteral name,
const internal::StringLiteral description,
const LabelDesc&... label_descriptions)
: AbstractMetricDef(metric_kind, name, description,
{label_descriptions...}) {
static_assert(sizeof...(LabelDesc) == NumLabels,
"Mismatch between Counter<NumLabels> and number of label "
"descriptions.");
}
};
} // namespace monitoring
} // namespace tensorflow
#endif // THIRD_PARTY_TENSORFLOW_CORE_LIB_MONITORING_METRIC_DEF_H_