Commit c42b2c44 authored by ulan's avatar ulan Committed by Commit bot

Refactor the ring buffer in GCTracer.

Now instead of saving all event details in the ring buffer,
we save only the bytes and duration.

This reduces the GCTracer size from 20K to 3K and simplifies code.

BUG=chromium:597310
LOG=NO

Review URL: https://codereview.chromium.org/1830723004

Cr-Commit-Position: refs/heads/master@{#35104}
parent 945a2b7a
This diff is collapsed.
......@@ -12,80 +12,49 @@
namespace v8 {
namespace internal {
// A simple ring buffer class with maximum size known at compile time.
// The class only implements the functionality required in GCTracer.
template <typename T, size_t MAX_SIZE>
template <typename T>
class RingBuffer {
public:
class const_iterator {
public:
const_iterator() : index_(0), elements_(NULL) {}
const_iterator(size_t index, const T* elements)
: index_(index), elements_(elements) {}
bool operator==(const const_iterator& rhs) const {
return elements_ == rhs.elements_ && index_ == rhs.index_;
}
bool operator!=(const const_iterator& rhs) const {
return elements_ != rhs.elements_ || index_ != rhs.index_;
}
operator const T*() const { return elements_ + index_; }
const T* operator->() const { return elements_ + index_; }
const T& operator*() const { return elements_[index_]; }
const_iterator& operator++() {
index_ = (index_ + 1) % (MAX_SIZE + 1);
return *this;
RingBuffer() { Reset(); }
static const int kSize = 10;
void Push(const T& value) {
if (count_ == kSize) {
elements_[start_++] = value;
if (start_ == kSize) start_ = 0;
} else {
DCHECK_EQ(start_, 0);
elements_[count_++] = value;
}
const_iterator& operator--() {
index_ = (index_ + MAX_SIZE) % (MAX_SIZE + 1);
return *this;
}
private:
size_t index_;
const T* elements_;
};
RingBuffer() : begin_(0), end_(0) {}
int Count() const { return count_; }
bool empty() const { return begin_ == end_; }
size_t size() const {
return (end_ - begin_ + MAX_SIZE + 1) % (MAX_SIZE + 1);
template <typename Callback>
T Sum(Callback callback, const T& initial) const {
int j = start_ + count_ - 1;
if (j >= kSize) j -= kSize;
T result = initial;
for (int i = 0; i < count_; i++) {
result = callback(result, elements_[j]);
if (--j == -1) j += kSize;
}
const_iterator begin() const { return const_iterator(begin_, elements_); }
const_iterator end() const { return const_iterator(end_, elements_); }
const_iterator back() const { return --end(); }
void push_back(const T& element) {
elements_[end_] = element;
end_ = (end_ + 1) % (MAX_SIZE + 1);
if (end_ == begin_) begin_ = (begin_ + 1) % (MAX_SIZE + 1);
}
void push_front(const T& element) {
begin_ = (begin_ + MAX_SIZE) % (MAX_SIZE + 1);
if (begin_ == end_) end_ = (end_ + MAX_SIZE) % (MAX_SIZE + 1);
elements_[begin_] = element;
return result;
}
void reset() {
begin_ = 0;
end_ = 0;
}
void Reset() { start_ = count_ = 0; }
private:
T elements_[MAX_SIZE + 1];
size_t begin_;
size_t end_;
T elements_[kSize];
int start_;
int count_;
DISALLOW_COPY_AND_ASSIGN(RingBuffer);
};
typedef std::pair<uint64_t, double> BytesAndDuration;
inline BytesAndDuration MakeBytesAndDuration(uint64_t bytes, double duration) {
return std::make_pair(bytes, duration);
}
enum ScavengeSpeedMode { kForAllObjects, kForSurvivedObjects };
......@@ -158,58 +127,6 @@ class GCTracer {
};
class AllocationEvent {
public:
// Default constructor leaves the event uninitialized.
AllocationEvent() {}
AllocationEvent(double duration, size_t allocation_in_bytes);
// Time spent in the mutator during the end of the last sample to the
// beginning of the next sample.
double duration_;
// Memory allocated in the new space during the end of the last sample
// to the beginning of the next sample
size_t allocation_in_bytes_;
};
class CompactionEvent {
public:
CompactionEvent() : duration(0), live_bytes_compacted(0) {}
CompactionEvent(double duration, intptr_t live_bytes_compacted)
: duration(duration), live_bytes_compacted(live_bytes_compacted) {}
double duration;
intptr_t live_bytes_compacted;
};
class ContextDisposalEvent {
public:
// Default constructor leaves the event uninitialized.
ContextDisposalEvent() {}
explicit ContextDisposalEvent(double time);
// Time when context disposal event happened.
double time_;
};
class SurvivalEvent {
public:
// Default constructor leaves the event uninitialized.
SurvivalEvent() {}
explicit SurvivalEvent(double survival_ratio);
double promotion_ratio_;
};
class Event {
public:
enum Type {
......@@ -314,19 +231,6 @@ class GCTracer {
double scopes[Scope::NUMBER_OF_SCOPES];
};
static const size_t kRingBufferMaxSize = 10;
typedef RingBuffer<Event, kRingBufferMaxSize> EventBuffer;
typedef RingBuffer<AllocationEvent, kRingBufferMaxSize> AllocationEventBuffer;
typedef RingBuffer<ContextDisposalEvent, kRingBufferMaxSize>
ContextDisposalEventBuffer;
typedef RingBuffer<CompactionEvent, kRingBufferMaxSize> CompactionEventBuffer;
typedef RingBuffer<SurvivalEvent, kRingBufferMaxSize> SurvivalEventBuffer;
static const int kThroughputTimeFrameMs = 5000;
explicit GCTracer(Heap* heap);
......@@ -445,6 +349,13 @@ class GCTracer {
// Discard all recorded survival events.
void ResetSurvivalEvents();
// Returns the average speed of the events in the buffer.
// If the buffer is empty, the result is 0.
// Otherwise, the result is between 1 byte/ms and 1 GB/ms.
static int AverageSpeed(const RingBuffer<BytesAndDuration>& buffer);
static int AverageSpeed(const RingBuffer<BytesAndDuration>& buffer,
const BytesAndDuration& initial, double time_ms);
private:
// Print one detailed trace line in name=value format.
// TODO(ernstm): Move to Heap.
......@@ -458,12 +369,6 @@ class GCTracer {
// it can be included in later crash dumps.
void Output(const char* format, ...) const;
// Compute the mean duration of the events in the given ring buffer.
double MeanDuration(const EventBuffer& events) const;
// Compute the max duration of the events in the given ring buffer.
double MaxDuration(const EventBuffer& events) const;
void ClearMarkCompactStatistics() {
cumulative_incremental_marking_steps_ = 0;
cumulative_incremental_marking_bytes_ = 0;
......@@ -500,28 +405,6 @@ class GCTracer {
// Previous INCREMENTAL_MARK_COMPACTOR event.
Event previous_incremental_mark_compactor_event_;
// RingBuffers for SCAVENGER events.
EventBuffer scavenger_events_;
// RingBuffers for MARK_COMPACTOR events.
EventBuffer mark_compactor_events_;
// RingBuffers for INCREMENTAL_MARK_COMPACTOR events.
EventBuffer incremental_mark_compactor_events_;
// RingBuffer for allocation events.
AllocationEventBuffer new_space_allocation_events_;
AllocationEventBuffer old_generation_allocation_events_;
// RingBuffer for context disposal events.
ContextDisposalEventBuffer context_disposal_events_;
// RingBuffer for compaction events.
CompactionEventBuffer compaction_events_;
// RingBuffer for survival events.
SurvivalEventBuffer survival_events_;
// Cumulative number of incremental marking steps since creation of tracer.
int cumulative_incremental_marking_steps_;
......@@ -581,6 +464,17 @@ class GCTracer {
// Separate timer used for --runtime_call_stats
RuntimeCallTimer timer_;
RingBuffer<BytesAndDuration> recorded_incremental_marking_steps_;
RingBuffer<BytesAndDuration> recorded_scavenges_total_;
RingBuffer<BytesAndDuration> recorded_scavenges_survived_;
RingBuffer<BytesAndDuration> recorded_compactions_;
RingBuffer<BytesAndDuration> recorded_mark_compacts_;
RingBuffer<BytesAndDuration> recorded_incremental_mark_compacts_;
RingBuffer<BytesAndDuration> recorded_new_generation_allocations_;
RingBuffer<BytesAndDuration> recorded_old_generation_allocations_;
RingBuffer<double> recorded_context_disposal_times_;
RingBuffer<double> recorded_survival_ratios_;
DISALLOW_COPY_AND_ASSIGN(GCTracer);
};
} // namespace internal
......
......@@ -143,7 +143,6 @@
'test-fixed-dtoa.cc',
'test-flags.cc',
'test-func-name-inference.cc',
'test-gc-tracer.cc',
'test-global-handles.cc',
'test-global-object.cc',
'test-hashing.cc',
......
// Copyright 2014 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <stdlib.h>
#include <utility>
#include "src/heap/gc-tracer.h"
#include "test/cctest/cctest.h"
using namespace v8::internal;
TEST(RingBufferPartialFill) {
const int max_size = 6;
typedef RingBuffer<int, max_size>::const_iterator Iter;
RingBuffer<int, max_size> ring_buffer;
CHECK(ring_buffer.empty());
CHECK_EQ(static_cast<int>(ring_buffer.size()), 0);
CHECK(ring_buffer.begin() == ring_buffer.end());
// Fill ring_buffer partially: [0, 1, 2]
for (int i = 0; i < max_size / 2; i++) ring_buffer.push_back(i);
CHECK(!ring_buffer.empty());
CHECK(static_cast<int>(ring_buffer.size()) == max_size / 2);
CHECK(ring_buffer.begin() != ring_buffer.end());
// Test forward itartion
int i = 0;
for (Iter iter = ring_buffer.begin(); iter != ring_buffer.end(); ++iter) {
CHECK(*iter == i);
++i;
}
CHECK_EQ(i, 3); // one past last element.
// Test backward iteration
i = 2;
Iter iter = ring_buffer.back();
while (true) {
CHECK(*iter == i);
if (iter == ring_buffer.begin()) break;
--iter;
--i;
}
CHECK_EQ(i, 0);
}
TEST(RingBufferWrapAround) {
const int max_size = 6;
typedef RingBuffer<int, max_size>::const_iterator Iter;
RingBuffer<int, max_size> ring_buffer;
// Fill ring_buffer (wrap around): [9, 10, 11, 12, 13, 14]
for (int i = 0; i < 2 * max_size + 3; i++) ring_buffer.push_back(i);
CHECK(!ring_buffer.empty());
CHECK(static_cast<int>(ring_buffer.size()) == max_size);
CHECK(ring_buffer.begin() != ring_buffer.end());
// Test forward iteration
int i = 9;
for (Iter iter = ring_buffer.begin(); iter != ring_buffer.end(); ++iter) {
CHECK(*iter == i);
++i;
}
CHECK_EQ(i, 15); // one past last element.
// Test backward iteration
i = 14;
Iter iter = ring_buffer.back();
while (true) {
CHECK(*iter == i);
if (iter == ring_buffer.begin()) break;
--iter;
--i;
}
CHECK_EQ(i, 9);
}
TEST(RingBufferPushFront) {
const int max_size = 6;
typedef RingBuffer<int, max_size>::const_iterator Iter;
RingBuffer<int, max_size> ring_buffer;
// Fill ring_buffer (wrap around): [14, 13, 12, 11, 10, 9]
for (int i = 0; i < 2 * max_size + 3; i++) ring_buffer.push_front(i);
CHECK(!ring_buffer.empty());
CHECK(static_cast<int>(ring_buffer.size()) == max_size);
CHECK(ring_buffer.begin() != ring_buffer.end());
// Test forward iteration
int i = 14;
for (Iter iter = ring_buffer.begin(); iter != ring_buffer.end(); ++iter) {
CHECK(*iter == i);
--i;
}
CHECK_EQ(i, 8); // one past last element.
}
// Copyright 2014 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cmath>
#include <limits>
#include "src/heap/gc-tracer.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
TEST(GCTracer, AverageSpeed) {
RingBuffer<BytesAndDuration> buffer;
EXPECT_EQ(100 / 2,
GCTracer::AverageSpeed(buffer, MakeBytesAndDuration(100, 2), 0));
buffer.Push(MakeBytesAndDuration(100, 8));
EXPECT_EQ(100 / 2,
GCTracer::AverageSpeed(buffer, MakeBytesAndDuration(100, 2), 2));
EXPECT_EQ(200 / 10,
GCTracer::AverageSpeed(buffer, MakeBytesAndDuration(100, 2), 3));
const int max_speed = 1024 * MB;
buffer.Reset();
buffer.Push(MakeBytesAndDuration(max_speed, 0.5));
EXPECT_EQ(max_speed,
GCTracer::AverageSpeed(buffer, MakeBytesAndDuration(0, 0), 1));
const int min_speed = 1;
buffer.Reset();
buffer.Push(MakeBytesAndDuration(1, 10000));
EXPECT_EQ(min_speed,
GCTracer::AverageSpeed(buffer, MakeBytesAndDuration(0, 0), 1));
buffer.Reset();
int sum = 0;
for (int i = 0; i < buffer.kSize; i++) {
sum += i + 1;
buffer.Push(MakeBytesAndDuration(i + 1, 1));
}
EXPECT_EQ(
static_cast<int>(sum * 1.0 / buffer.kSize + 0.5),
GCTracer::AverageSpeed(buffer, MakeBytesAndDuration(0, 0), buffer.kSize));
buffer.Push(MakeBytesAndDuration(100, 1));
EXPECT_EQ(
static_cast<int>((sum * 1.0 - 1 + 100) / buffer.kSize + 0.5),
GCTracer::AverageSpeed(buffer, MakeBytesAndDuration(0, 0), buffer.kSize));
}
} // namespace internal
} // namespace v8
......@@ -107,6 +107,7 @@
'libplatform/worker-thread-unittest.cc',
'heap/bitmap-unittest.cc',
'heap/gc-idle-time-handler-unittest.cc',
'heap/gc-tracer-unittest.cc',
'heap/memory-reducer-unittest.cc',
'heap/heap-unittest.cc',
'heap/scavenge-job-unittest.cc',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment