Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(330)

Side by Side Diff: runtime/vm/profiler_service.cc

Issue 1826753003: Use a cache for inlining information (functions + token positions) to speed up function profile (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 #include "vm/profiler_service.h" 5 #include "vm/profiler_service.h"
6 6
7 #include "vm/growable_array.h" 7 #include "vm/growable_array.h"
8 #include "vm/hash_map.h" 8 #include "vm/hash_map.h"
9 #include "vm/log.h" 9 #include "vm/log.h"
10 #include "vm/native_symbol.h" 10 #include "vm/native_symbol.h"
(...skipping 1037 matching lines...) Expand 10 before | Expand all | Expand 10 after
1048 ProfileFunctionTrieNodeCode code_object(index); 1048 ProfileFunctionTrieNodeCode code_object(index);
1049 code_object.Tick(); 1049 code_object.Tick();
1050 code_objects_.Add(code_object); 1050 code_objects_.Add(code_object);
1051 } 1051 }
1052 1052
1053 private: 1053 private:
1054 ZoneGrowableArray<ProfileFunctionTrieNodeCode> code_objects_; 1054 ZoneGrowableArray<ProfileFunctionTrieNodeCode> code_objects_;
1055 }; 1055 };
1056 1056
1057 1057
1058 class ProfileCodeInlinedFunctionsCache : public ValueObject {
1059 public:
1060 ProfileCodeInlinedFunctionsCache()
1061 : cache_cursor_(0) {
1062 for (intptr_t i = 0; i < kCacheSize; i++) {
1063 cache_[i].Reset();
1064 }
1065 cache_hit_ = 0;
1066 cache_miss_ = 0;
1067 }
1068
1069 ~ProfileCodeInlinedFunctionsCache() {
1070 if (FLAG_trace_profiler) {
1071 intptr_t total = cache_hit_ + cache_miss_;
1072 OS::Print("LOOKUPS: %" Pd " HITS: %" Pd " MISSES: %" Pd "\n",
1073 total,
1074 cache_hit_,
1075 cache_miss_);
1076 }
1077 }
1078
1079 void Get(uword pc,
1080 const Code& code,
1081 ProcessedSample* sample,
1082 intptr_t frame_index,
1083 // Outputs:
1084 GrowableArray<Function*>** inlined_functions,
1085 GrowableArray<TokenPosition>** inlined_token_positions,
1086 TokenPosition* token_position) {
1087 const intptr_t offset = OffsetForPC(pc, code, sample, frame_index);
1088 if (FindInCache(pc,
1089 offset,
1090 inlined_functions,
1091 inlined_token_positions,
1092 token_position)) {
1093 // Found in cache.
1094 return;
1095 }
1096 Add(pc, code, sample, frame_index,
1097 inlined_functions, inlined_token_positions, token_position);
1098 }
1099
1100 private:
1101 bool FindInCache(uword pc,
1102 intptr_t offset,
1103 GrowableArray<Function*>** inlined_functions,
1104 GrowableArray<TokenPosition>** inlined_token_positions,
1105 TokenPosition* token_position) {
1106 // Simple linear scan.
1107 for (intptr_t i = 0; i < kCacheSize; i++) {
1108 if ((cache_[i].pc == pc) && (cache_[i].offset == offset)) {
1109 // Hit.
1110 if (cache_[i].inlined_functions.length() == 0) {
1111 *inlined_functions = NULL;
1112 *inlined_token_positions = NULL;
1113 } else {
1114 *inlined_functions = &cache_[i].inlined_functions;
1115 *inlined_token_positions = &cache_[i].inlined_token_positions;
1116 }
1117 *token_position = cache_[i].token_position;
1118 cache_hit_++;
1119 return true;
1120 }
1121 }
1122 cache_miss_++;
1123 return false;
1124 }
1125
1126 // Add to cache and fill in outputs.
1127 void Add(uword pc,
1128 const Code& code,
1129 ProcessedSample* sample,
1130 intptr_t frame_index,
1131 // Outputs:
1132 GrowableArray<Function*>** inlined_functions,
1133 GrowableArray<TokenPosition>** inlined_token_positions,
1134 TokenPosition* token_position) {
1135 const intptr_t offset = OffsetForPC(pc, code, sample, frame_index);
1136 CacheEntry* cache_entry = &cache_[NextFreeIndex()];
1137 cache_entry->pc = pc;
1138 cache_entry->offset = offset;
1139 code.GetInlinedFunctionsAt(offset,
1140 &(cache_entry->inlined_functions),
1141 &(cache_entry->inlined_token_positions));
1142 cache_entry->token_position = code.GetTokenPositionAt(offset);
1143 *token_position = (cache_entry->token_position);
1144 *inlined_functions = NULL;
1145 *inlined_token_positions = NULL;
srdjan 2016/03/23 21:55:41 Move this two lines inside the if below, or move t
Cutch 2016/03/23 22:31:17 Done.
1146 if (cache_entry->inlined_functions.length() == 0) {
1147 return;
1148 }
1149 // The inlined token position table does not include the token position
1150 // of the final call. Insert it at the beginning because the table.
1151 // is reversed.
1152 cache_entry->inlined_token_positions.InsertAt(
1153 0,
1154 cache_entry->token_position);
1155
1156 // Write outputs.
1157 *inlined_functions = &(cache_entry->inlined_functions);
1158 *inlined_token_positions = &(cache_entry->inlined_token_positions);
1159 }
1160
1161 intptr_t NextFreeIndex() {
1162 cache_cursor_ = (cache_cursor_ + 1) % kCacheSize;
1163 return cache_cursor_;
1164 }
1165
1166 intptr_t OffsetForPC(uword pc,
1167 const Code& code,
1168 ProcessedSample* sample,
1169 intptr_t frame_index) {
1170 intptr_t offset = pc - code.EntryPoint();
1171 if (frame_index != 0) {
1172 // The PC of frames below the top frame is a call's return address,
1173 // which can belong to a different inlining interval than the call.
1174 offset--;
1175 } else if (sample->IsAllocationSample()) {
1176 // Allocation samples skip the top frame, so the top frame's pc is
1177 // also a call's return address.
1178 offset--;
1179 } else if (!sample->first_frame_executing()) {
1180 // If the first frame wasn't executing code (i.e. we started to collect
1181 // the stack trace at an exit frame), the top frame's pc is also a
1182 // call's return address.
1183 offset--;
1184 }
1185 return offset;
1186 }
1187
1188 struct CacheEntry {
1189 void Reset() {
1190 pc = 0;
1191 offset = 0;
1192 }
1193 uword pc;
1194 intptr_t offset;
1195 GrowableArray<Function*> inlined_functions;
1196 GrowableArray<TokenPosition> inlined_token_positions;
1197 TokenPosition token_position;
1198 };
1199
1200 static const intptr_t kCacheSize = 128;
srdjan 2016/03/23 21:55:40 Do you have any performance data for cache size 16
Cutch 2016/03/23 22:31:17 I've tried 32 and 64 and settled on 128 because it
1201 intptr_t cache_cursor_;
1202 CacheEntry cache_[kCacheSize];
1203 intptr_t cache_miss_;
1204 intptr_t cache_hit_;
1205 };
1206
1207
1058 class ProfileBuilder : public ValueObject { 1208 class ProfileBuilder : public ValueObject {
1059 public: 1209 public:
1060 enum ProfileInfoKind { 1210 enum ProfileInfoKind {
1061 kNone, 1211 kNone,
1062 kOptimized, 1212 kOptimized,
1063 kUnoptimized, 1213 kUnoptimized,
1064 kNative, 1214 kNative,
1065 kInlineStart, 1215 kInlineStart,
1066 kInlineFinish, 1216 kInlineFinish,
1067 kNumProfileInfoKind, 1217 kNumProfileInfoKind,
(...skipping 381 matching lines...) Expand 10 before | Expand all | Expand 10 after
1449 } 1599 }
1450 1600
1451 // Truncated tag. 1601 // Truncated tag.
1452 if (sample->truncated()) { 1602 if (sample->truncated()) {
1453 current = AppendTruncatedTag(current); 1603 current = AppendTruncatedTag(current);
1454 InclusiveTickTruncatedTag(); 1604 InclusiveTickTruncatedTag();
1455 } 1605 }
1456 } 1606 }
1457 } 1607 }
1458 1608
1459 intptr_t OffsetForPC(uword pc,
1460 const Code& code,
1461 ProcessedSample* sample,
1462 intptr_t frame_index) {
1463 intptr_t offset = pc - code.EntryPoint();
1464 if (frame_index != 0) {
1465 // The PC of frames below the top frame is a call's return address,
1466 // which can belong to a different inlining interval than the call.
1467 offset--;
1468 } else if (sample->IsAllocationSample()) {
1469 // Allocation samples skip the top frame, so the top frame's pc is
1470 // also a call's return address.
1471 offset--;
1472 } else if (!sample->first_frame_executing()) {
1473 // If the first frame wasn't executing code (i.e. we started to collect
1474 // the stack trace at an exit frame), the top frame's pc is also a
1475 // call's return address.
1476 offset--;
1477 }
1478 return offset;
1479 }
1480
1481 ProfileFunctionTrieNode* ProcessFrame( 1609 ProfileFunctionTrieNode* ProcessFrame(
1482 ProfileFunctionTrieNode* current, 1610 ProfileFunctionTrieNode* current,
1483 intptr_t sample_index, 1611 intptr_t sample_index,
1484 ProcessedSample* sample, 1612 ProcessedSample* sample,
1485 intptr_t frame_index) { 1613 intptr_t frame_index) {
1486 const uword pc = sample->At(frame_index); 1614 const uword pc = sample->At(frame_index);
1487 ProfileCode* profile_code = GetProfileCode(pc, 1615 ProfileCode* profile_code = GetProfileCode(pc,
1488 sample->timestamp()); 1616 sample->timestamp());
1489 ProfileFunction* function = profile_code->function(); 1617 ProfileFunction* function = profile_code->function();
1490 ASSERT(function != NULL); 1618 ASSERT(function != NULL);
1491 const intptr_t code_index = profile_code->code_table_index(); 1619 const intptr_t code_index = profile_code->code_table_index();
1492 ASSERT(profile_code != NULL); 1620 ASSERT(profile_code != NULL);
1493 const Code& code = Code::ZoneHandle(profile_code->code()); 1621 const Code& code = Code::ZoneHandle(profile_code->code());
1494 GrowableArray<Function*> inlined_functions; 1622 GrowableArray<Function*>* inlined_functions = NULL;
1495 GrowableArray<TokenPosition> inlined_token_positions; 1623 GrowableArray<TokenPosition>* inlined_token_positions = NULL;
1496 TokenPosition token_position = TokenPosition::kNoSource; 1624 TokenPosition token_position = TokenPosition::kNoSource;
1497 if (!code.IsNull()) { 1625 if (!code.IsNull()) {
1498 const intptr_t offset = OffsetForPC(pc, code, sample, frame_index); 1626 inlined_functions_cache_.Get(pc, code, sample, frame_index,
1499 code.GetInlinedFunctionsAt(offset, 1627 &inlined_functions,
1500 &inlined_functions, 1628 &inlined_token_positions,
1501 &inlined_token_positions); 1629 &token_position);
1502 token_position = code.GetTokenPositionAt(offset);
1503 if (inlined_functions.length() > 0) {
1504 // The inlined token position table does not include the token position
1505 // of the final call. Insert it at the beginning because the table.
1506 // is reversed.
1507 inlined_token_positions.InsertAt(0, token_position);
1508 }
1509 ASSERT(inlined_functions.length() <= inlined_token_positions.length());
1510 if (FLAG_trace_profiler_verbose) { 1630 if (FLAG_trace_profiler_verbose) {
1511 for (intptr_t i = 0; i < inlined_functions.length(); i++) { 1631 for (intptr_t i = 0; i < inlined_functions->length(); i++) {
1512 const String& name = 1632 const String& name =
1513 String::Handle(inlined_functions[i]->QualifiedScrubbedName()); 1633 String::Handle((*inlined_functions)[i]->QualifiedScrubbedName());
1514 THR_Print("InlinedFunction[%" Pd "] = {%s, %s}\n", 1634 THR_Print("InlinedFunction[%" Pd "] = {%s, %s}\n",
1515 i, 1635 i,
1516 name.ToCString(), 1636 name.ToCString(),
1517 inlined_token_positions[i].ToCString()); 1637 (*inlined_token_positions)[i].ToCString());
1518 } 1638 }
1519 } 1639 }
1520 } 1640 }
1521 if (code.IsNull() || (inlined_functions.length() == 0)) { 1641 if (code.IsNull() ||
1642 (inlined_functions == NULL) ||
1643 (inlined_functions->length() == 0)) {
1522 // No inlined functions. 1644 // No inlined functions.
1523 if (inclusive_tree_) { 1645 if (inclusive_tree_) {
1524 current = AppendKind(code, current); 1646 current = AppendKind(code, current);
1525 } 1647 }
1526 current = ProcessFunction(current, 1648 current = ProcessFunction(current,
1527 sample_index, 1649 sample_index,
1528 sample, 1650 sample,
1529 frame_index, 1651 frame_index,
1530 function, 1652 function,
1531 token_position, 1653 token_position,
1532 code_index); 1654 code_index);
1533 if (!inclusive_tree_) { 1655 if (!inclusive_tree_) {
1534 current = AppendKind(code, current); 1656 current = AppendKind(code, current);
1535 } 1657 }
1536 return current; 1658 return current;
1537 } 1659 }
1538 1660
1539 ASSERT(code.is_optimized()); 1661 ASSERT(code.is_optimized());
1540 1662
1541 if (inclusive_tree_) { 1663 if (inclusive_tree_) {
1542 for (intptr_t i = inlined_functions.length() - 1; i >= 0; i--) { 1664 for (intptr_t i = inlined_functions->length() - 1; i >= 0; i--) {
1543 Function* inlined_function = inlined_functions[i]; 1665 Function* inlined_function = (*inlined_functions)[i];
1544 ASSERT(inlined_function != NULL); 1666 ASSERT(inlined_function != NULL);
1545 ASSERT(!inlined_function->IsNull()); 1667 ASSERT(!inlined_function->IsNull());
1546 TokenPosition inlined_token_position = inlined_token_positions[i]; 1668 TokenPosition inlined_token_position = (*inlined_token_positions)[i];
1547 const bool inliner = i == (inlined_functions.length() - 1); 1669 const bool inliner = i == (inlined_functions->length() - 1);
1548 if (inliner) { 1670 if (inliner) {
1549 current = AppendKind(code, current); 1671 current = AppendKind(code, current);
1550 } 1672 }
1551 current = ProcessInlinedFunction(current, 1673 current = ProcessInlinedFunction(current,
1552 sample_index, 1674 sample_index,
1553 sample, 1675 sample,
1554 frame_index, 1676 frame_index,
1555 inlined_function, 1677 inlined_function,
1556 inlined_token_position, 1678 inlined_token_position,
1557 code_index); 1679 code_index);
1558 if (inliner) { 1680 if (inliner) {
1559 current = AppendKind(kInlineStart, current); 1681 current = AppendKind(kInlineStart, current);
1560 } 1682 }
1561 } 1683 }
1562 current = AppendKind(kInlineFinish, current); 1684 current = AppendKind(kInlineFinish, current);
1563 } else { 1685 } else {
1564 // Append the inlined children. 1686 // Append the inlined children.
1565 current = AppendKind(kInlineFinish, current); 1687 current = AppendKind(kInlineFinish, current);
1566 for (intptr_t i = 0; i < inlined_functions.length(); i++) { 1688 for (intptr_t i = 0; i < inlined_functions->length(); i++) {
1567 Function* inlined_function = inlined_functions[i]; 1689 Function* inlined_function = (*inlined_functions)[i];
1568 ASSERT(inlined_function != NULL); 1690 ASSERT(inlined_function != NULL);
1569 ASSERT(!inlined_function->IsNull()); 1691 ASSERT(!inlined_function->IsNull());
1570 TokenPosition inlined_token_position = inlined_token_positions[i]; 1692 TokenPosition inlined_token_position = (*inlined_token_positions)[i];
1571 const bool inliner = i == (inlined_functions.length() - 1); 1693 const bool inliner = i == (inlined_functions->length() - 1);
1572 if (inliner) { 1694 if (inliner) {
1573 current = AppendKind(kInlineStart, current); 1695 current = AppendKind(kInlineStart, current);
1574 } 1696 }
1575 current = ProcessInlinedFunction(current, 1697 current = ProcessInlinedFunction(current,
1576 sample_index, 1698 sample_index,
1577 sample, 1699 sample,
1578 frame_index + i, 1700 frame_index + i,
1579 inlined_function, 1701 inlined_function,
1580 inlined_token_position, 1702 inlined_token_position,
1581 code_index); 1703 code_index);
(...skipping 524 matching lines...) Expand 10 before | Expand all | Expand 10 after
2106 Isolate* vm_isolate_; 2228 Isolate* vm_isolate_;
2107 SampleFilter* filter_; 2229 SampleFilter* filter_;
2108 Profile::TagOrder tag_order_; 2230 Profile::TagOrder tag_order_;
2109 intptr_t extra_tags_; 2231 intptr_t extra_tags_;
2110 Profile* profile_; 2232 Profile* profile_;
2111 DeoptimizedCodeSet* deoptimized_code_; 2233 DeoptimizedCodeSet* deoptimized_code_;
2112 const Code& null_code_; 2234 const Code& null_code_;
2113 const Function& null_function_; 2235 const Function& null_function_;
2114 bool tick_functions_; 2236 bool tick_functions_;
2115 bool inclusive_tree_; 2237 bool inclusive_tree_;
2116 2238 ProfileCodeInlinedFunctionsCache inlined_functions_cache_;
2117 ProcessedSampleBuffer* samples_; 2239 ProcessedSampleBuffer* samples_;
2118 ProfileInfoKind info_kind_; 2240 ProfileInfoKind info_kind_;
2119 }; // ProfileBuilder. 2241 }; // ProfileBuilder.
2120 2242
2121 2243
2122 Profile::Profile(Isolate* isolate) 2244 Profile::Profile(Isolate* isolate)
2123 : isolate_(isolate), 2245 : isolate_(isolate),
2124 zone_(Thread::Current()->zone()), 2246 zone_(Thread::Current()->zone()),
2125 samples_(NULL), 2247 samples_(NULL),
2126 live_code_(NULL), 2248 live_code_(NULL),
(...skipping 479 matching lines...) Expand 10 before | Expand all | Expand 10 after
2606 // Disable thread interrupts while processing the buffer. 2728 // Disable thread interrupts while processing the buffer.
2607 DisableThreadInterruptsScope dtis(thread); 2729 DisableThreadInterruptsScope dtis(thread);
2608 2730
2609 ClearProfileVisitor clear_profile(isolate); 2731 ClearProfileVisitor clear_profile(isolate);
2610 sample_buffer->VisitSamples(&clear_profile); 2732 sample_buffer->VisitSamples(&clear_profile);
2611 } 2733 }
2612 2734
2613 #endif // !PRODUCT 2735 #endif // !PRODUCT
2614 2736
2615 } // namespace dart 2737 } // namespace dart
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698