Generate less code in v8::internal::Counters constructor

This saves 72 KiB (approximately 0.1%) of the Chrome APK size of for ARM/Android.

In Counters, each similar group of counters generates a compact data structure,
which a loop then iterates over, rather than having the full loop unrolled
(though the compiler will automatically unroll small ones).

In RuntimeCallStats, the compiler was not being clever enough to avoid
initializing count_ and time_ to zero individually, even after the initialization
of names was moved into a loop. As a result, RuntimeCallCounter was modified
to have a non-initializing constructor for exclusive use by RuntimeCallStats,
which explicitly initializes the counters in a loop. Since v8::base::TimeDelta
does not support an uninitialized state, time_ was changed to be stored as
int64_t microseconds internally, which generates the same code (it's the same
representation as TimeDelta).

BUG=v8:6119

Review-Url: https://codereview.chromium.org/2759033002
Cr-Commit-Position: refs/heads/master@{#43996}
This commit is contained in:
jbroman 2017-03-21 12:31:14 -07:00 committed by Commit bot
parent 3d3dafee71
commit 53562fd9fb
2 changed files with 162 additions and 81 deletions

View File

@ -61,84 +61,142 @@ void HistogramTimer::Stop() {
Counters::Counters(Isolate* isolate) { Counters::Counters(Isolate* isolate) {
static const struct {
Histogram Counters::*member;
const char* caption;
int min;
int max;
int num_buckets;
} kHistograms[] = {
#define HR(name, caption, min, max, num_buckets) \ #define HR(name, caption, min, max, num_buckets) \
name##_ = Histogram(#caption, min, max, num_buckets, isolate); {&Counters::name##_, #caption, min, max, num_buckets},
HISTOGRAM_RANGE_LIST(HR) HISTOGRAM_RANGE_LIST(HR)
#undef HR #undef HR
};
for (const auto& histogram : kHistograms) {
this->*histogram.member =
Histogram(histogram.caption, histogram.min, histogram.max,
histogram.num_buckets, isolate);
}
static const struct {
HistogramTimer Counters::*member;
const char* caption;
int max;
HistogramTimer::Resolution res;
} kHistogramTimers[] = {
#define HT(name, caption, max, res) \ #define HT(name, caption, max, res) \
name##_ = HistogramTimer(#caption, 0, max, HistogramTimer::res, 50, isolate); {&Counters::name##_, #caption, max, HistogramTimer::res},
HISTOGRAM_TIMER_LIST(HT) HISTOGRAM_TIMER_LIST(HT)
#undef HT #undef HT
};
for (const auto& timer : kHistogramTimers) {
this->*timer.member =
HistogramTimer(timer.caption, 0, timer.max, timer.res, 50, isolate);
}
#define AHT(name, caption) \ static const struct {
name##_ = AggregatableHistogramTimer(#caption, 0, 10000000, 50, isolate); AggregatableHistogramTimer Counters::*member;
const char* caption;
} kAggregatableHistogramTimers[] = {
#define AHT(name, caption) {&Counters::name##_, #caption},
AGGREGATABLE_HISTOGRAM_TIMER_LIST(AHT) AGGREGATABLE_HISTOGRAM_TIMER_LIST(AHT)
#undef AHT #undef AHT
};
for (const auto& aht : kAggregatableHistogramTimers) {
this->*aht.member =
AggregatableHistogramTimer(aht.caption, 0, 10000000, 50, isolate);
}
#define HP(name, caption) \ static const struct {
name##_ = Histogram(#caption, 0, 101, 100, isolate); Histogram Counters::*member;
const char* caption;
} kHistogramPercentages[] = {
#define HP(name, caption) {&Counters::name##_, #caption},
HISTOGRAM_PERCENTAGE_LIST(HP) HISTOGRAM_PERCENTAGE_LIST(HP)
#undef HP #undef HP
};
for (const auto& percentage : kHistogramPercentages) {
this->*percentage.member =
Histogram(percentage.caption, 0, 101, 100, isolate);
}
// Exponential histogram assigns bucket limits to points
// Exponential histogram assigns bucket limits to points // p[1], p[2], ... p[n] such that p[i+1] / p[i] = constant.
// p[1], p[2], ... p[n] such that p[i+1] / p[i] = constant. // The constant factor is equal to the n-th root of (high / low),
// The constant factor is equal to the n-th root of (high / low), // where the n is the number of buckets, the low is the lower limit,
// where the n is the number of buckets, the low is the lower limit, // the high is the upper limit.
// the high is the upper limit. // For n = 50, low = 1000, high = 500000: the factor = 1.13.
// For n = 50, low = 1000, high = 500000: the factor = 1.13. static const struct {
#define HM(name, caption) \ Histogram Counters::*member;
name##_ = Histogram(#caption, 1000, 500000, 50, isolate); const char* caption;
} kLegacyMemoryHistograms[] = {
#define HM(name, caption) {&Counters::name##_, #caption},
HISTOGRAM_LEGACY_MEMORY_LIST(HM) HISTOGRAM_LEGACY_MEMORY_LIST(HM)
#undef HM #undef HM
// For n = 100, low = 4000, high = 2000000: the factor = 1.06. };
for (const auto& histogram : kLegacyMemoryHistograms) {
this->*histogram.member =
Histogram(histogram.caption, 1000, 500000, 50, isolate);
}
// For n = 100, low = 4000, high = 2000000: the factor = 1.06.
static const struct {
Histogram Counters::*member;
AggregatedMemoryHistogram<Histogram> Counters::*aggregated;
const char* caption;
} kMemoryHistograms[] = {
#define HM(name, caption) \ #define HM(name, caption) \
name##_ = Histogram(#caption, 4000, 2000000, 100, isolate); {&Counters::name##_, &Counters::aggregated_##name##_, #caption},
HISTOGRAM_MEMORY_LIST(HM) HISTOGRAM_MEMORY_LIST(HM)
#undef HM #undef HM
};
for (const auto& histogram : kMemoryHistograms) {
this->*histogram.member =
Histogram(histogram.caption, 4000, 2000000, 100, isolate);
this->*histogram.aggregated =
AggregatedMemoryHistogram<Histogram>(&(this->*histogram.member));
}
#define HM(name, caption) \ // clang-format off
aggregated_##name##_ = AggregatedMemoryHistogram<Histogram>(&name##_); static const struct {
HISTOGRAM_MEMORY_LIST(HM) StatsCounter Counters::*member;
#undef HM const char* caption;
} kStatsCounters[] = {
#define SC(name, caption) \ #define SC(name, caption) {&Counters::name##_, "c:" #caption},
name##_ = StatsCounter(isolate, "c:" #caption); STATS_COUNTER_LIST_1(SC) STATS_COUNTER_LIST_2(SC)
STATS_COUNTER_LIST_1(SC)
STATS_COUNTER_LIST_2(SC)
#undef SC #undef SC
#define SC(name) \ #define SC(name) \
count_of_##name##_ = StatsCounter(isolate, "c:" "V8.CountOf_" #name); \ {&Counters::count_of_##name##_, "c:" "V8.CountOf_" #name}, \
size_of_##name##_ = StatsCounter(isolate, "c:" "V8.SizeOf_" #name); {&Counters::size_of_##name##_, "c:" "V8.SizeOf_" #name},
INSTANCE_TYPE_LIST(SC) INSTANCE_TYPE_LIST(SC)
#undef SC #undef SC
#define SC(name) \ #define SC(name) \
count_of_CODE_TYPE_##name##_ = \ {&Counters::count_of_CODE_TYPE_##name##_, \
StatsCounter(isolate, "c:" "V8.CountOf_CODE_TYPE-" #name); \ "c:" "V8.CountOf_CODE_TYPE-" #name}, \
size_of_CODE_TYPE_##name##_ = \ {&Counters::size_of_CODE_TYPE_##name##_, \
StatsCounter(isolate, "c:" "V8.SizeOf_CODE_TYPE-" #name); "c:" "V8.SizeOf_CODE_TYPE-" #name},
CODE_KIND_LIST(SC) CODE_KIND_LIST(SC)
#undef SC #undef SC
#define SC(name) \ #define SC(name) \
count_of_FIXED_ARRAY_##name##_ = \ {&Counters::count_of_FIXED_ARRAY_##name##_, \
StatsCounter(isolate, "c:" "V8.CountOf_FIXED_ARRAY-" #name); \ "c:" "V8.CountOf_FIXED_ARRAY-" #name}, \
size_of_FIXED_ARRAY_##name##_ = \ {&Counters::size_of_FIXED_ARRAY_##name##_, \
StatsCounter(isolate, "c:" "V8.SizeOf_FIXED_ARRAY-" #name); "c:" "V8.SizeOf_FIXED_ARRAY-" #name},
FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(SC) FIXED_ARRAY_SUB_INSTANCE_TYPE_LIST(SC)
#undef SC #undef SC
#define SC(name) \ #define SC(name) \
count_of_CODE_AGE_##name##_ = \ {&Counters::count_of_CODE_AGE_##name##_, \
StatsCounter(isolate, "c:" "V8.CountOf_CODE_AGE-" #name); \ "c:" "V8.CountOf_CODE_AGE-" #name}, \
size_of_CODE_AGE_##name##_ = \ {&Counters::size_of_CODE_AGE_##name##_, \
StatsCounter(isolate, "c:" "V8.SizeOf_CODE_AGE-" #name); "c:" "V8.SizeOf_CODE_AGE-" #name},
CODE_AGE_LIST_COMPLETE(SC) CODE_AGE_LIST_COMPLETE(SC)
#undef SC #undef SC
};
// clang-format on
for (const auto& counter : kStatsCounters) {
this->*counter.member = StatsCounter(isolate, counter.caption);
}
} }
@ -275,19 +333,19 @@ class RuntimeCallStatEntries {
void RuntimeCallCounter::Reset() { void RuntimeCallCounter::Reset() {
count_ = 0; count_ = 0;
time_ = base::TimeDelta(); time_ = 0;
} }
void RuntimeCallCounter::Dump(v8::tracing::TracedValue* value) { void RuntimeCallCounter::Dump(v8::tracing::TracedValue* value) {
value->BeginArray(name_); value->BeginArray(name_);
value->AppendDouble(count_); value->AppendDouble(count_);
value->AppendDouble(time_.InMicroseconds()); value->AppendDouble(time_);
value->EndArray(); value->EndArray();
} }
void RuntimeCallCounter::Add(RuntimeCallCounter* other) { void RuntimeCallCounter::Add(RuntimeCallCounter* other) {
count_ += other->count(); count_ += other->count();
time_ += other->time(); time_ += other->time().InMicroseconds();
} }
void RuntimeCallTimer::Snapshot() { void RuntimeCallTimer::Snapshot() {
@ -303,6 +361,29 @@ void RuntimeCallTimer::Snapshot() {
Resume(now); Resume(now);
} }
RuntimeCallStats::RuntimeCallStats() : in_use_(false) {
static const char* const kNames[] = {
#define CALL_RUNTIME_COUNTER(name) #name,
FOR_EACH_MANUAL_COUNTER(CALL_RUNTIME_COUNTER) //
#undef CALL_RUNTIME_COUNTER
#define CALL_RUNTIME_COUNTER(name, nargs, ressize) #name,
FOR_EACH_INTRINSIC(CALL_RUNTIME_COUNTER) //
#undef CALL_RUNTIME_COUNTER
#define CALL_BUILTIN_COUNTER(name) #name,
BUILTIN_LIST_C(CALL_BUILTIN_COUNTER) //
#undef CALL_BUILTIN_COUNTER
#define CALL_BUILTIN_COUNTER(name) "API_" #name,
FOR_EACH_API_COUNTER(CALL_BUILTIN_COUNTER) //
#undef CALL_BUILTIN_COUNTER
#define CALL_BUILTIN_COUNTER(name) #name,
FOR_EACH_HANDLER_COUNTER(CALL_BUILTIN_COUNTER)
#undef CALL_BUILTIN_COUNTER
};
for (int i = 0; i < counters_count; i++) {
this->*(counters[i]) = RuntimeCallCounter(kNames[i]);
}
}
// static // static
const RuntimeCallStats::CounterId RuntimeCallStats::counters[] = { const RuntimeCallStats::CounterId RuntimeCallStats::counters[] = {
#define CALL_RUNTIME_COUNTER(name) &RuntimeCallStats::name, #define CALL_RUNTIME_COUNTER(name) &RuntimeCallStats::name,

View File

@ -486,21 +486,29 @@ double AggregatedMemoryHistogram<Histogram>::Aggregate(double current_ms,
class RuntimeCallCounter final { class RuntimeCallCounter final {
public: public:
explicit RuntimeCallCounter(const char* name) : name_(name) {} explicit RuntimeCallCounter(const char* name)
: name_(name), count_(0), time_(0) {}
V8_NOINLINE void Reset(); V8_NOINLINE void Reset();
V8_NOINLINE void Dump(v8::tracing::TracedValue* value); V8_NOINLINE void Dump(v8::tracing::TracedValue* value);
void Add(RuntimeCallCounter* other); void Add(RuntimeCallCounter* other);
const char* name() const { return name_; } const char* name() const { return name_; }
int64_t count() const { return count_; } int64_t count() const { return count_; }
base::TimeDelta time() const { return time_; } base::TimeDelta time() const {
return base::TimeDelta::FromMicroseconds(time_);
}
void Increment() { count_++; } void Increment() { count_++; }
void Add(base::TimeDelta delta) { time_ += delta; } void Add(base::TimeDelta delta) { time_ += delta.InMicroseconds(); }
private: private:
RuntimeCallCounter() {}
const char* name_; const char* name_;
int64_t count_ = 0; int64_t count_;
base::TimeDelta time_; // Stored as int64_t so that its initialization can be deferred.
int64_t time_;
friend class RuntimeCallStats;
}; };
// RuntimeCallTimer is used to keep track of the stack of currently active // RuntimeCallTimer is used to keep track of the stack of currently active
@ -827,25 +835,22 @@ class RuntimeCallTimer final {
class RuntimeCallStats final : public ZoneObject { class RuntimeCallStats final : public ZoneObject {
public: public:
typedef RuntimeCallCounter RuntimeCallStats::*CounterId; typedef RuntimeCallCounter RuntimeCallStats::*CounterId;
V8_EXPORT_PRIVATE RuntimeCallStats();
#define CALL_RUNTIME_COUNTER(name) \ #define CALL_RUNTIME_COUNTER(name) RuntimeCallCounter name;
RuntimeCallCounter name = RuntimeCallCounter(#name);
FOR_EACH_MANUAL_COUNTER(CALL_RUNTIME_COUNTER) FOR_EACH_MANUAL_COUNTER(CALL_RUNTIME_COUNTER)
#undef CALL_RUNTIME_COUNTER #undef CALL_RUNTIME_COUNTER
#define CALL_RUNTIME_COUNTER(name, nargs, ressize) \ #define CALL_RUNTIME_COUNTER(name, nargs, ressize) \
RuntimeCallCounter Runtime_##name = RuntimeCallCounter(#name); RuntimeCallCounter Runtime_##name;
FOR_EACH_INTRINSIC(CALL_RUNTIME_COUNTER) FOR_EACH_INTRINSIC(CALL_RUNTIME_COUNTER)
#undef CALL_RUNTIME_COUNTER #undef CALL_RUNTIME_COUNTER
#define CALL_BUILTIN_COUNTER(name) \ #define CALL_BUILTIN_COUNTER(name) RuntimeCallCounter Builtin_##name;
RuntimeCallCounter Builtin_##name = RuntimeCallCounter(#name);
BUILTIN_LIST_C(CALL_BUILTIN_COUNTER) BUILTIN_LIST_C(CALL_BUILTIN_COUNTER)
#undef CALL_BUILTIN_COUNTER #undef CALL_BUILTIN_COUNTER
#define CALL_BUILTIN_COUNTER(name) \ #define CALL_BUILTIN_COUNTER(name) RuntimeCallCounter API_##name;
RuntimeCallCounter API_##name = RuntimeCallCounter("API_" #name);
FOR_EACH_API_COUNTER(CALL_BUILTIN_COUNTER) FOR_EACH_API_COUNTER(CALL_BUILTIN_COUNTER)
#undef CALL_BUILTIN_COUNTER #undef CALL_BUILTIN_COUNTER
#define CALL_BUILTIN_COUNTER(name) \ #define CALL_BUILTIN_COUNTER(name) RuntimeCallCounter Handler_##name;
RuntimeCallCounter Handler_##name = RuntimeCallCounter(#name);
FOR_EACH_HANDLER_COUNTER(CALL_BUILTIN_COUNTER) FOR_EACH_HANDLER_COUNTER(CALL_BUILTIN_COUNTER)
#undef CALL_BUILTIN_COUNTER #undef CALL_BUILTIN_COUNTER
@ -875,11 +880,6 @@ class RuntimeCallStats final : public ZoneObject {
V8_EXPORT_PRIVATE void Print(std::ostream& os); V8_EXPORT_PRIVATE void Print(std::ostream& os);
V8_NOINLINE void Dump(v8::tracing::TracedValue* value); V8_NOINLINE void Dump(v8::tracing::TracedValue* value);
RuntimeCallStats() {
Reset();
in_use_ = false;
}
RuntimeCallTimer* current_timer() { return current_timer_.Value(); } RuntimeCallTimer* current_timer() { return current_timer_.Value(); }
bool InUse() { return in_use_; } bool InUse() { return in_use_; }