Skip to content

Commit 3e65c9d

Browse files
mralephCommit Queue
authored andcommitted
[vm] Buffer writes in TimelineEventFileRecorderBase
Previous implementation allocated individual events and then executed writes directly - both of these add unnecessary overhead. Instead allocate and process events in blocks just like other recorders and buffer writes in a temporary buffer before passing data to Dart::file_write_callback(). Currently if you write something like this: int fib(int n) { return Timeline.timeSync('fib', () { // typical recursive fib implementation here. }); } for (var i = 0; i < 1000; i++) { fib(10); } and then run it with perfettofile recorder you end up waiting several seconds before recorder is drained with program spending most time doing small writes via File::WriteFully. This is no longer the case after this change. TEST=ci Change-Id: I6a33ccdb1f293a94669ad0dd7d883837b86c8b6f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/426861 Commit-Queue: Slava Egorov <[email protected]> Reviewed-by: Ryan Macnak <[email protected]>
1 parent d35c01e commit 3e65c9d

File tree

2 files changed

+177
-67
lines changed

2 files changed

+177
-67
lines changed

runtime/vm/timeline.cc

Lines changed: 130 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,11 +1938,6 @@ void TimelineEventBufferedRecorder::PrintTraceEvent(
19381938
}
19391939
#endif // !defined(PRODUCT)
19401940

1941-
TimelineEventBlock* TimelineEventFixedBufferRecorder::GetHeadBlockLocked() {
1942-
ASSERT(lock_.IsOwnedByCurrentThread());
1943-
return &blocks_[0];
1944-
}
1945-
19461941
void TimelineEventFixedBufferRecorder::ClearLocked() {
19471942
ASSERT(lock_.IsOwnedByCurrentThread());
19481943
for (intptr_t i = 0; i < num_blocks_; i++) {
@@ -2151,14 +2146,7 @@ static void TimelineEventFileRecorderBaseStart(uword parameter) {
21512146
}
21522147

21532148
TimelineEventFileRecorderBase::TimelineEventFileRecorderBase(const char* path)
2154-
: TimelineEventPlatformRecorder(),
2155-
monitor_(),
2156-
head_(nullptr),
2157-
tail_(nullptr),
2158-
file_(nullptr),
2159-
shutting_down_(false),
2160-
drained_(false),
2161-
thread_id_(OSThread::kInvalidThreadJoinId) {
2149+
: TimelineEventRecorder() {
21622150
Dart_FileOpenCallback file_open = Dart::file_open_callback();
21632151
Dart_FileWriteCallback file_write = Dart::file_write_callback();
21642152
Dart_FileCloseCallback file_close = Dart::file_close_callback();
@@ -2189,8 +2177,20 @@ TimelineEventFileRecorderBase::~TimelineEventFileRecorderBase() {
21892177
OSThread::Join(thread_id_);
21902178
thread_id_ = OSThread::kInvalidThreadJoinId;
21912179

2192-
ASSERT(head_ == nullptr);
2193-
ASSERT(tail_ == nullptr);
2180+
// At this point all blocks created by this recorder should be drained
2181+
// and placed into |empty_blocks_|. Delete all of them.
2182+
ASSERT(drained_);
2183+
ASSERT(completed_blocks_head_ == nullptr);
2184+
ASSERT(completed_blocks_tail_ == nullptr);
2185+
while (empty_blocks_ != nullptr) {
2186+
auto block = empty_blocks_;
2187+
empty_blocks_ = empty_blocks_->next();
2188+
delete block;
2189+
block_count_--;
2190+
}
2191+
ASSERT(block_count_ == 0);
2192+
2193+
FlushBuffer();
21942194

21952195
Dart_FileCloseCallback file_close = Dart::file_close_callback();
21962196
(*file_close)(file_);
@@ -2202,36 +2202,83 @@ void TimelineEventFileRecorderBase::Drain() {
22022202
thread_id_ = OSThread::GetCurrentThreadJoinId(OSThread::Current());
22032203
ml.Notify();
22042204
for (;;) {
2205-
if (head_ == nullptr) {
2205+
if (completed_blocks_head_ == nullptr) {
2206+
ASSERT(completed_blocks_tail_ == nullptr);
22062207
if (shutting_down_) {
22072208
break;
22082209
}
22092210
ml.Wait();
22102211
continue; // Recheck empty.
22112212
}
2212-
TimelineEvent* event = head_;
2213-
TimelineEvent* next = event->next();
2214-
head_ = next;
2215-
if (next == nullptr) {
2216-
tail_ = nullptr;
2217-
}
2218-
ml.Exit();
2213+
// Take the whole list of pending blocks and drain all of them.
2214+
auto blocks = completed_blocks_head_;
2215+
completed_blocks_tail_ = completed_blocks_head_ = nullptr;
22192216
{
2220-
DrainImpl(*event);
2221-
delete event;
2217+
MonitorLeaveScope leave_ml(&ml);
2218+
DrainBlockChain(blocks);
22222219
}
2223-
ml.Enter();
22242220
}
22252221
drained_ = true;
22262222
ml.Notify();
22272223
}
22282224

2229-
void TimelineEventFileRecorderBase::Write(const char* buffer,
2230-
intptr_t len) const {
2225+
void TimelineEventFileRecorderBase::DrainBlockChain(TimelineEventBlock* block) {
2226+
while (block != nullptr) {
2227+
auto next_block = block->next();
2228+
block->set_next(nullptr);
2229+
2230+
for (intptr_t i = 0, length = block->length(); i < length; i++) {
2231+
DrainImpl(*block->At(i));
2232+
}
2233+
block->Reset();
2234+
2235+
// Place block for reuse.
2236+
{
2237+
MonitorLocker ml(&monitor_);
2238+
block->set_next(empty_blocks_);
2239+
empty_blocks_ = block;
2240+
}
2241+
block = next_block;
2242+
}
2243+
}
2244+
2245+
void TimelineEventFileRecorderBase::FlushBuffer() {
2246+
if (buffer_pos_ != 0) {
2247+
WriteToFile(buffer_.get(), buffer_pos_);
2248+
buffer_pos_ = 0;
2249+
}
2250+
}
2251+
2252+
void TimelineEventFileRecorderBase::Write(const char* bytes, intptr_t length) {
2253+
if (length >= kBufferSize / 2) {
2254+
FlushBuffer();
2255+
WriteToFile(bytes, length);
2256+
return;
2257+
}
2258+
2259+
do {
2260+
intptr_t space_left = kBufferSize - buffer_pos_;
2261+
intptr_t bytes_to_write = Utils::Minimum(length, space_left);
2262+
memcpy(buffer_.get() + buffer_pos_, bytes, bytes_to_write); // NOLINT
2263+
buffer_pos_ += bytes_to_write;
2264+
length -= bytes_to_write;
2265+
bytes += bytes_to_write;
2266+
if (buffer_pos_ == kBufferSize) {
2267+
FlushBuffer();
2268+
}
2269+
} while (length > 0);
2270+
}
2271+
2272+
void TimelineEventFileRecorderBase::WriteToFile(const char* buffer,
2273+
intptr_t len) const {
22312274
Dart_FileWriteCallback file_write = Dart::file_write_callback();
22322275
(*file_write)(buffer, len, file_);
22332276
}
22342277

2278+
TimelineEvent* TimelineEventFileRecorderBase::StartEvent() {
2279+
return ThreadBlockStartEvent();
2280+
}
2281+
22352282
void TimelineEventFileRecorderBase::CompleteEvent(TimelineEvent* event) {
22362283
if (event == nullptr) {
22372284
return;
@@ -2240,17 +2287,26 @@ void TimelineEventFileRecorderBase::CompleteEvent(TimelineEvent* event) {
22402287
delete event;
22412288
return;
22422289
}
2290+
ThreadBlockCompleteEvent(event);
2291+
}
22432292

2244-
MonitorLocker ml(&monitor_);
2245-
ASSERT(!shutting_down_);
2246-
event->set_next(nullptr);
2247-
if (tail_ == nullptr) {
2248-
head_ = tail_ = event;
2249-
} else {
2250-
tail_->set_next(event);
2251-
tail_ = event;
2293+
void TimelineEventFileRecorderBase::FinishBlock(TimelineEventBlock* block) {
2294+
TimelineEventRecorder::FinishBlock(block);
2295+
2296+
if (block != nullptr) {
2297+
// Append completed block to the end of the list of completed blocks.
2298+
// We want to keep events from the same thread ordered in sequentially
2299+
// in the output.
2300+
MonitorLocker ml(&monitor_);
2301+
block->set_next(nullptr);
2302+
if (completed_blocks_tail_ != nullptr) {
2303+
completed_blocks_tail_->set_next(block);
2304+
completed_blocks_tail_ = block;
2305+
} else {
2306+
completed_blocks_head_ = completed_blocks_tail_ = block;
2307+
}
2308+
ml.Notify();
22522309
}
2253-
ml.Notify();
22542310
}
22552311

22562312
void TimelineEventFileRecorderBase::StartUp(const char* name) {
@@ -2274,6 +2330,37 @@ void TimelineEventFileRecorderBase::ShutDown() {
22742330
}
22752331
}
22762332

2333+
TimelineEventBlock* TimelineEventFileRecorderBase::GetNewBlockLocked() {
2334+
ASSERT(lock_.IsOwnedByCurrentThread());
2335+
// Start by reusing a block.
2336+
TimelineEventBlock* block = nullptr;
2337+
if (empty_blocks_ != nullptr) {
2338+
// TODO(vegorov) maybe we don't want to take a lock just to grab an empty
2339+
// block?
2340+
MonitorLocker ml(&monitor_);
2341+
if (empty_blocks_ != nullptr) {
2342+
block = empty_blocks_;
2343+
empty_blocks_ = empty_blocks_->next();
2344+
if (FLAG_trace_timeline) {
2345+
OS::PrintErr("Reused empty block %p\n", block);
2346+
}
2347+
}
2348+
}
2349+
if (block == nullptr) {
2350+
block = new TimelineEventBlock(block_count_++);
2351+
if (FLAG_trace_timeline) {
2352+
OS::PrintErr("Created new block %p\n", block);
2353+
}
2354+
}
2355+
block->Open();
2356+
2357+
return block;
2358+
}
2359+
2360+
void TimelineEventFileRecorderBase::ClearLocked() {
2361+
ASSERT(lock_.IsOwnedByCurrentThread());
2362+
}
2363+
22772364
TimelineEventFileRecorder::TimelineEventFileRecorder(const char* path)
22782365
: TimelineEventFileRecorderBase(path), first_(true) {
22792366
// Chrome trace format has two forms:
@@ -2295,7 +2382,7 @@ void TimelineEventFileRecorder::AddTrackMetadataBasedOnThread(
22952382
const intptr_t process_id,
22962383
const intptr_t trace_id,
22972384
const char* thread_name) {
2298-
TimelineEvent* event = new TimelineEvent();
2385+
TimelineEvent* event = StartEvent();
22992386
event->Metadata("thread_name");
23002387
event->SetNumArguments(1);
23012388
event->CopyArgument(0, "name", thread_name);
@@ -2327,8 +2414,7 @@ class TimelineEventPerfettoFileRecorder : public TimelineEventFileRecorderBase {
23272414

23282415
private:
23292416
void WritePacket(
2330-
protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet)
2331-
const;
2417+
protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet);
23322418
void DrainImpl(const TimelineEvent& event) final;
23332419

23342420
TracePacketWriter writer_;
@@ -2361,9 +2447,10 @@ TimelineEventPerfettoFileRecorder::TimelineEventPerfettoFileRecorder(
23612447
}
23622448

23632449
TimelineEventPerfettoFileRecorder::~TimelineEventPerfettoFileRecorder() {
2450+
ShutDown();
2451+
23642452
protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>& packet =
23652453
this->packet();
2366-
ShutDown();
23672454
// We do not need to lock the following section, because at this point
23682455
// |RecorderSynchronizationLock| must have been put in a state that prevents
23692456
// the metadata maps from being modified.
@@ -2388,15 +2475,14 @@ TimelineEventPerfettoFileRecorder::~TimelineEventPerfettoFileRecorder() {
23882475
}
23892476

23902477
void TimelineEventPerfettoFileRecorder::WritePacket(
2391-
protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet)
2392-
const {
2478+
protozero::HeapBuffered<perfetto::protos::pbzero::TracePacket>* packet) {
23932479
const std::tuple<std::unique_ptr<const uint8_t[]>, intptr_t>& response =
23942480
perfetto_utils::GetProtoPreamble(packet);
23952481
Write(reinterpret_cast<const char*>(std::get<0>(response).get()),
23962482
std::get<1>(response));
23972483
for (const protozero::ScatteredHeapBuffer::Slice& slice :
23982484
packet->GetSlices()) {
2399-
Write(reinterpret_cast<char*>(slice.start()),
2485+
Write(reinterpret_cast<const char*>(slice.start()),
24002486
slice.size() - slice.unused_bytes());
24012487
}
24022488
}
@@ -2435,11 +2521,6 @@ void TimelineEventEndlessRecorder::ForEachNonEmptyBlock(
24352521
}
24362522
#endif // !defined(PRODUCT)
24372523

2438-
TimelineEventBlock* TimelineEventEndlessRecorder::GetHeadBlockLocked() {
2439-
ASSERT(lock_.IsOwnedByCurrentThread());
2440-
return head_;
2441-
}
2442-
24432524
TimelineEvent* TimelineEventEndlessRecorder::StartEvent() {
24442525
return ThreadBlockStartEvent();
24452526
}

0 commit comments

Comments
 (0)