mem_usage.h 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #ifndef CARBON_TOOLCHAIN_BASE_MEM_USAGE_H_
  5. #define CARBON_TOOLCHAIN_BASE_MEM_USAGE_H_
  6. #include <cstdint>
  7. #include "common/map.h"
  8. #include "common/set.h"
  9. #include "llvm/ADT/STLFunctionalExtras.h"
  10. #include "llvm/ADT/SmallVector.h"
  11. #include "llvm/ADT/StringRef.h"
  12. #include "llvm/Support/FormatVariadic.h"
  13. #include "toolchain/base/yaml.h"
  14. namespace Carbon {
  15. // Helps track memory usage for a compile.
  16. //
  17. // Users will mix `Add` and `Collect` calls, using `ConcatLabel` to label
  18. // allocation sources. Typically we'll collect stats for growable, potentially
  19. // large data types (such as `SmallVector`), ignoring small fixed-size members
  20. // (such as pointers or `int32_t`).
  21. //
  22. // For example:
  23. //
  24. // auto CollectMemUsage(MemUsage& mem_usage, llvm::StringRef label) const
  25. // -> void {
  26. // // Explicit tracking.
  27. // mem_usage.Add(MemUsage::ConcatLabel(label, "data_"), data_.used_bytes(),
  28. // data_.reserved_bytes());
  29. // // Common library types like `Map` and `llvm::SmallVector` have
  30. // // type-specific support.
  31. // mem_usage.Add(MemUsage::Concat(label, "array_"), array_);
  32. // // Implementing `CollectMemUsage` allows use with the same interface.
  33. // mem_usage.Collect(MemUsage::Concat(label, "obj_"), obj_);
  34. // }
  35. class MemUsage {
  36. public:
  37. // Adds tracking for used and reserved bytes, paired with the given label.
  38. auto Add(std::string label, int64_t used_bytes, int64_t reserved_bytes)
  39. -> void {
  40. mem_usage_.push_back({.label = std::move(label),
  41. .used_bytes = used_bytes,
  42. .reserved_bytes = reserved_bytes});
  43. }
  44. // Adds usage tracking for an allocator.
  45. auto Add(std::string label, const llvm::BumpPtrAllocator& allocator) -> void {
  46. Add(std::move(label), allocator.getBytesAllocated(),
  47. allocator.getTotalMemory());
  48. }
  49. // Adds usage tracking for a map.
  50. template <typename KeyT, typename ValueT, ssize_t SmallSize,
  51. typename KeyContextT>
  52. auto Add(std::string label, Map<KeyT, ValueT, SmallSize, KeyContextT> map,
  53. KeyContextT key_context = KeyContextT()) -> void {
  54. // These don't track used bytes, so we set the same value for used and
  55. // reserved bytes.
  56. auto bytes = map.ComputeMetrics(key_context).storage_bytes;
  57. Add(std::move(label), bytes, bytes);
  58. }
  59. // Adds usage tracking for a set.
  60. template <typename KeyT, ssize_t SmallSize, typename KeyContextT>
  61. auto Add(std::string label, Set<KeyT, SmallSize, KeyContextT> set,
  62. KeyContextT key_context = KeyContextT()) -> void {
  63. // These don't track used bytes, so we set the same value for used and
  64. // reserved bytes.
  65. auto bytes = set.ComputeMetrics(key_context).storage_bytes;
  66. Add(std::move(label), bytes, bytes);
  67. }
  68. // Adds memory usage of an array's data. This ignores the possible overhead of
  69. // a SmallVector's in-place storage; if it's used, it's going to be tiny
  70. // relative to scaling memory costs.
  71. //
  72. // This uses SmallVector in order to get proper inference for T, which
  73. // ArrayRef misses.
  74. template <typename T, unsigned N>
  75. auto Add(std::string label, const llvm::SmallVector<T, N>& array) -> void {
  76. Add(std::move(label), array.size_in_bytes(), array.capacity_in_bytes());
  77. }
  78. // Adds memory usage for an object that provides `CollectMemUsage`.
  79. //
  80. // The expected signature of `CollectMemUsage` is above, in MemUsage class
  81. // comments.
  82. template <typename T>
  83. auto Collect(llvm::StringRef label, const T& arg) -> void {
  84. arg.CollectMemUsage(*this, label);
  85. }
  86. // Constructs a label for memory usage, handling the `.` concatenation.
  87. // We don't expect much depth in labels per-call.
  88. static auto ConcatLabel(llvm::StringRef label, llvm::StringRef child_label)
  89. -> std::string {
  90. return llvm::formatv("{0}.{1}", label, child_label);
  91. }
  92. static auto ConcatLabel(llvm::StringRef label, llvm::StringRef child_label1,
  93. llvm::StringRef child_label2) -> std::string {
  94. return llvm::formatv("{0}.{1}.{2}", label, child_label1, child_label2);
  95. }
  96. auto OutputYaml(llvm::StringRef filename) const -> Yaml::OutputMapping {
  97. // Explicitly copy the filename.
  98. return Yaml::OutputMapping([&, filename](Yaml::OutputMapping::Map map) {
  99. map.Add("filename", filename);
  100. int64_t total_used = 0;
  101. int64_t total_reserved = 0;
  102. for (const auto& entry : mem_usage_) {
  103. total_used += entry.used_bytes;
  104. total_reserved += entry.reserved_bytes;
  105. map.Add(entry.label,
  106. Yaml::OutputMapping([&](Yaml::OutputMapping::Map byte_map) {
  107. byte_map.Add("used_bytes", entry.used_bytes);
  108. byte_map.Add("reserved_bytes", entry.reserved_bytes);
  109. }));
  110. }
  111. map.Add("Total",
  112. Yaml::OutputMapping([&](Yaml::OutputMapping::Map byte_map) {
  113. byte_map.Add("used_bytes", total_used);
  114. byte_map.Add("reserved_bytes", total_reserved);
  115. }));
  116. });
  117. }
  118. private:
  119. // Memory usage for a specific label.
  120. struct Entry {
  121. std::string label;
  122. int64_t used_bytes;
  123. int64_t reserved_bytes;
  124. };
  125. // The accumulated data on memory usage.
  126. llvm::SmallVector<Entry> mem_usage_;
  127. };
  128. } // namespace Carbon
  129. #endif // CARBON_TOOLCHAIN_BASE_MEM_USAGE_H_