pw_metric: Add size report
This adds a size report for pw_metric, not including the RPC service.
The report shows the impact of having a few metrics, of dumping, and of
adding more metrics.
At this time, the flash usage on Cortex-M4 is unexpectedly large due to
the creation of large static constructors. This will require followup
CLs to address.
This CL also moves a few inlined methods from the .h file into the .cc
to reduce code size.
Change-Id: Ie0cb041a6e1e5e77c921e9078adea2eaabccdd43
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/16300
Commit-Queue: Keir Mierle <keir@google.com>
Reviewed-by: Alexei Frolov <frolv@google.com>
diff --git a/pw_metric/BUILD.gn b/pw_metric/BUILD.gn
index 9a0ff2b..8829e8d 100644
--- a/pw_metric/BUILD.gn
+++ b/pw_metric/BUILD.gn
@@ -15,6 +15,7 @@
# gn-format disable
import("//build_overrides/pigweed.gni")
+import("$dir_pw_bloat/bloat.gni")
import("$dir_pw_build/target_types.gni")
import("$dir_pw_docgen/docs.gni")
import("$dir_pw_protobuf_compiler/proto.gni")
@@ -86,10 +87,6 @@
################################################################################
-pw_doc_group("docs") {
- sources = [ "docs.rst" ]
-}
-
pw_test_group("tests") {
tests = [
":metric_test",
@@ -109,3 +106,34 @@
sources = [ "global_test.cc" ]
deps = [ ":global" ]
}
+
+pw_size_report("metric_size_report") {
+ title = "Typical pw_metric use (no RPC service)"
+
+ # To see all the symbols, uncomment the following:
+ # Note: The size report RST table won't be generated when full_report = true.
+ #full_report = true
+
+ binaries = [
+ {
+ target = "size_report:one_metric"
+ base = "size_report:base"
+ label = "1 metric and 1 group no dump or export"
+ },
+ {
+ target = "size_report:dump"
+ base = "size_report:base"
+ label = "(+) dump group and metrics to log"
+ },
+ {
+ target = "size_report:more_metrics"
+ base = "size_report:dump"
+ label = "(+) 1 group (+) 4 metrics"
+ },
+ ]
+}
+
+pw_doc_group("docs") {
+ sources = [ "docs.rst" ]
+ report_deps = [ ":metric_size_report" ]
+}
diff --git a/pw_metric/docs.rst b/pw_metric/docs.rst
index 23ef778..7a736ce 100644
--- a/pw_metric/docs.rst
+++ b/pw_metric/docs.rst
@@ -711,6 +711,20 @@
pumping the metrics into the streaming response. This gives flow control to
the application.
+-----------
+Size report
+-----------
+The below size report shows the cost in code and memory for a few examples of
+metrics. This does not include the RPC service.
+
+.. include:: metric_size_report
+
+.. attention::
+
+ At time of writing, **the above sizes show an unexpectedly large flash
+ impact**. We are investigating why GCC is inserting large global static
+ constructors per group, when all the logic should be reused across objects.
+
----------------
Design tradeoffs
----------------
diff --git a/pw_metric/metric.cc b/pw_metric/metric.cc
index 9f0c990..5e99329 100644
--- a/pw_metric/metric.cc
+++ b/pw_metric/metric.cc
@@ -59,6 +59,31 @@
metrics.push_front(*this);
}
+float Metric::as_float() const {
+ PW_DCHECK(is_float());
+ return float_;
+}
+
+uint32_t Metric::as_int() const {
+ PW_DCHECK(is_int());
+ return uint_;
+}
+
+void Metric::Increment(uint32_t amount) {
+ PW_DCHECK(is_int());
+ uint_ += amount;
+}
+
+void Metric::SetInt(uint32_t value) {
+ PW_DCHECK(is_int());
+ uint_ = value;
+}
+
+void Metric::SetFloat(float value) {
+ PW_DCHECK(is_float());
+ float_ = value;
+}
+
void Metric::Dump(int level) {
Base64EncodedToken encoded_name(name());
const char* indent = Indent(level);
@@ -78,6 +103,12 @@
}
}
+Group::Group(Token name) : name_(name) {}
+
+Group::Group(Token name, IntrusiveList<Group>& groups) : name_(name) {
+ groups.push_front(*this);
+}
+
void Group::Dump(int level) {
Base64EncodedToken encoded_name(name());
const char* indent = Indent(level);
diff --git a/pw_metric/public/pw_metric/metric.h b/pw_metric/public/pw_metric/metric.h
index c217587..9c45ea0 100644
--- a/pw_metric/public/pw_metric/metric.h
+++ b/pw_metric/public/pw_metric/metric.h
@@ -46,15 +46,8 @@
bool is_float() const { return (name_and_type_ & kTypeMask) == kTypeFloat; }
bool is_int() const { return (name_and_type_ & kTypeMask) == kTypeInt; }
- float as_float() const {
- PW_DCHECK(is_float());
- return float_;
- }
-
- uint32_t as_int() const {
- PW_DCHECK(is_int());
- return uint_;
- }
+ float as_float() const;
+ uint32_t as_int() const;
// Dump a metric or metrics to logs. Level determines the indentation
// indent_level up to a maximum of 4. Example output:
@@ -83,20 +76,11 @@
// Hide mutation methods, and only offer write access through the specialized
// TypedMetric below. This makes it impossible to call metric.Increment() on
// a float metric at compile time.
- void Increment(uint32_t amount = 1) {
- PW_DCHECK(is_int());
- uint_ += amount;
- }
+ void Increment(uint32_t amount = 1);
- void SetInt(uint32_t value) {
- PW_DCHECK(is_int());
- uint_ = value;
- }
+ void SetInt(uint32_t value);
- void SetFloat(float value) {
- PW_DCHECK(is_float());
- float_ = value;
- }
+ void SetFloat(float value);
private:
// The name of this metric as a token; from PW_TOKENIZE_STRING("my_metric").
@@ -163,10 +147,8 @@
// Size: 16 bytes/128 bits - next, name, metrics, children.
class Group : public IntrusiveList<Group>::Item {
public:
- Group(Token name) : name_(name) {}
- Group(Token name, IntrusiveList<Group>& groups) : name_(name) {
- groups.push_front(*this);
- }
+ Group(Token name);
+ Group(Token name, IntrusiveList<Group>& groups);
Token name() const { return name_; }
diff --git a/pw_metric/size_report/BUILD b/pw_metric/size_report/BUILD
new file mode 100644
index 0000000..f7693a9
--- /dev/null
+++ b/pw_metric/size_report/BUILD
@@ -0,0 +1,65 @@
+# Copyright 2020 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+load(
+ "//pw_build:pigweed.bzl",
+ "pw_cc_binary",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+licenses(["notice"]) # Apache License 2.0
+
+pw_cc_binary(
+ name = "base",
+ srcs = ["base.cc"],
+ deps = [
+ "//pw_bloat:bloat_this_binary",
+ "//pw_log",
+ "//pw_assert",
+ ],
+)
+
+pw_cc_binary(
+ name = "one_metric",
+ srcs = ["one_metric.cc"],
+ deps = [
+ "//pw_bloat:bloat_this_binary",
+ "//pw_metric",
+ "//pw_log",
+ "//pw_assert",
+ ],
+)
+
+pw_cc_binary(
+ name = "dump",
+ srcs = ["dump.cc"],
+ deps = [
+ "//pw_bloat:bloat_this_binary",
+ "//pw_metric",
+ "//pw_log",
+ "//pw_assert",
+ ],
+)
+
+pw_cc_binary(
+ name = "more_metrics",
+ srcs = ["more_metrics.cc"],
+ deps = [
+ "//pw_bloat:bloat_this_binary",
+ "//pw_metric",
+ "//pw_log",
+ "//pw_assert",
+ ],
+)
diff --git a/pw_metric/size_report/BUILD.gn b/pw_metric/size_report/BUILD.gn
new file mode 100644
index 0000000..90d7ef0
--- /dev/null
+++ b/pw_metric/size_report/BUILD.gn
@@ -0,0 +1,56 @@
+# Copyright 2020 The Pigweed Authors
+#
+# 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
+#
+# https://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.
+
+# gn-format disable
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/target_types.gni")
+pw_executable("base") {
+ sources = [ "base.cc" ]
+ deps = [
+ "$dir_pw_bloat:bloat_this_binary",
+ dir_pw_assert,
+ dir_pw_log,
+ ]
+}
+
+pw_executable("one_metric") {
+ sources = [ "one_metric.cc" ]
+ deps = [
+ "$dir_pw_bloat:bloat_this_binary",
+ "..",
+ dir_pw_assert,
+ dir_pw_log,
+ ]
+}
+
+pw_executable("dump") {
+ sources = [ "dump.cc" ]
+ deps = [
+ "$dir_pw_bloat:bloat_this_binary",
+ "..",
+ dir_pw_assert,
+ dir_pw_log,
+ ]
+}
+
+pw_executable("more_metrics") {
+ sources = [ "more_metrics.cc" ]
+ deps = [
+ "$dir_pw_bloat:bloat_this_binary",
+ "..",
+ dir_pw_assert,
+ dir_pw_log,
+ ]
+}
diff --git a/pw_metric/size_report/base.cc b/pw_metric/size_report/base.cc
new file mode 100644
index 0000000..8fb1abc
--- /dev/null
+++ b/pw_metric/size_report/base.cc
@@ -0,0 +1,31 @@
+// Copyright 2019 The Pigweed Authors
+//
+// 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
+//
+// https://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 "pw_assert/assert.h"
+#include "pw_bloat/bloat_this_binary.h"
+#include "pw_log/log.h"
+
+int volatile* unoptimizable;
+
+int main() {
+ pw::bloat::BloatThisBinary();
+
+ // Ensure we are paying the cost for log and assert.
+ PW_CHECK_INT_GE(unoptimizable, 0, "Ensure this CHECK logic stays");
+ PW_LOG_INFO("We care about optimizing: %d", *unoptimizable);
+ // This matches the log preventing optimizing the "m" metric in one_metric.cc.
+ PW_LOG_INFO("some_metric: %d", *unoptimizable);
+
+ return *unoptimizable;
+}
diff --git a/pw_metric/size_report/dump.cc b/pw_metric/size_report/dump.cc
new file mode 100644
index 0000000..b7ec068
--- /dev/null
+++ b/pw_metric/size_report/dump.cc
@@ -0,0 +1,45 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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
+//
+// https://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 "pw_assert/assert.h"
+#include "pw_bloat/bloat_this_binary.h"
+#include "pw_log/log.h"
+#include "pw_metric/metric.h"
+
+PW_METRIC_GROUP(group_foo, "some_group");
+PW_METRIC(group_foo, metric_x, "some_metric", 14u);
+
+int volatile* unoptimizable;
+
+int main() {
+ pw::bloat::BloatThisBinary();
+
+ if (*unoptimizable) {
+ metric_x.Increment();
+ }
+ metric_x.Increment();
+
+ // Ensure log and assert aren't optimized out.
+ PW_CHECK_INT_GE(unoptimizable, 0, "Ensure this CHECK logic stays");
+ PW_LOG_INFO("Ensure logs are pulled in: %d", *unoptimizable);
+
+ // Ensure metric_x isn't optimized out.
+ PW_LOG_INFO("metric_x: %d", static_cast<int>(metric_x.value()));
+
+ // Trigger pulling in the dump code. Dump twice to cover cost in more_metrics.
+ group_foo.Dump();
+ group_foo.Dump();
+
+ return *unoptimizable;
+}
diff --git a/pw_metric/size_report/more_metrics.cc b/pw_metric/size_report/more_metrics.cc
new file mode 100644
index 0000000..883a8ca
--- /dev/null
+++ b/pw_metric/size_report/more_metrics.cc
@@ -0,0 +1,65 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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
+//
+// https://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 "pw_assert/assert.h"
+#include "pw_bloat/bloat_this_binary.h"
+#include "pw_log/log.h"
+#include "pw_metric/metric.h"
+
+PW_METRIC_GROUP(group_bar, "bar");
+PW_METRIC(group_bar, metric_a, "a", 14u);
+PW_METRIC(group_bar, metric_b, "b", 14u);
+PW_METRIC(group_bar, metric_c, "c", 14u);
+
+PW_METRIC_GROUP(group_foo, "foo");
+PW_METRIC(group_foo, metric_x, "x", 14u);
+PW_METRIC(group_foo, metric_y, "y", 14u);
+
+int volatile* unoptimizable;
+
+int main() {
+ pw::bloat::BloatThisBinary();
+
+ if (*unoptimizable) {
+ // Bar
+ metric_a.Increment();
+ metric_b.Increment();
+ metric_c.Increment();
+
+ // Foo
+ metric_x.Increment();
+ metric_y.Increment();
+ }
+ // Bar
+ metric_a.Increment();
+ metric_b.Increment();
+ metric_c.Increment();
+
+ // Foo
+ metric_x.Increment();
+ metric_y.Increment();
+
+ // Ensure log and assert aren't optimized out.
+ PW_CHECK_INT_GE(unoptimizable, 0, "Ensure this CHECK logic stays");
+ PW_LOG_INFO("Ensure logs are pulled in: %d", *unoptimizable);
+
+ // Ensure metric_x isn't optimized out.
+ PW_LOG_INFO("metric_x: %d", static_cast<int>(metric_x.value()));
+
+ // Trigger pulling in the dump code.
+ group_foo.Dump();
+ group_bar.Dump();
+
+ return *unoptimizable;
+}
diff --git a/pw_metric/size_report/one_metric.cc b/pw_metric/size_report/one_metric.cc
new file mode 100644
index 0000000..4f08d07
--- /dev/null
+++ b/pw_metric/size_report/one_metric.cc
@@ -0,0 +1,42 @@
+// Copyright 2020 The Pigweed Authors
+//
+// 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
+//
+// https://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 "pw_assert/assert.h"
+#include "pw_bloat/bloat_this_binary.h"
+#include "pw_log/log.h"
+#include "pw_metric/metric.h"
+
+PW_METRIC_GROUP(group_foo, "foo");
+PW_METRIC(group_foo, metric_x, "x", 14u);
+
+int volatile* unoptimizable;
+
+int main() {
+ PW_METRIC_GROUP(inner, "some_group");
+ pw::bloat::BloatThisBinary();
+
+ if (*unoptimizable) {
+ metric_x.Increment();
+ }
+ metric_x.Increment();
+
+ // Ensure log and assert aren't optimized out.
+ PW_CHECK_INT_GE(unoptimizable, 0, "Ensure this CHECK logic stays");
+ PW_LOG_INFO("Ensure logs are pulled in: %d", *unoptimizable);
+
+ // Ensure metric_x isn't optimized out.
+ PW_LOG_INFO("metric_x: %d", static_cast<int>(metric_x.value()));
+
+ return *unoptimizable;
+}