@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Make mundane performance improvements to Diffusion browse views

Summary:
Ref T2450. This reorganizes code to improve performance.

Mostly, there are a lot of things which are unique per commit (author name, links, short name, etc), but we were rendering them for every line.

This often meant we'd render the same author's name thousands of times. This is slower than rendering it only once.

In 99% of interfaces this doesn't matter, but blame is weird and it's significant on big files.

Test Plan:
Locally, `__phutil_library_map__.php` now has costs of roughly:

- 550ms for main content (from 650ms before the patch).
- 1,500ms for blame content (frrom 1,800ms before the patch).

So this isn't huge, is a decent ~20%-ish performance gain for shuffling some stuff around.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T2450

Differential Revision: https://secure.phabricator.com/D14963

+304 -259
+304 -259
src/applications/diffusion/controller/DiffusionBrowseController.php
··· 592 592 593 593 $viewer = $this->getViewer(); 594 594 595 - $blame_handles = array(); 596 595 if ($needs_blame) { 597 596 $blame = $this->loadBlame($path, $drequest->getCommit()); 598 - if ($blame) { 599 - $author_phids = mpull($blame, 'getAuthorPHID'); 600 - $blame_handles = $viewer->loadHandles($author_phids); 601 - } 597 + list($blame_list, $blame_commits) = $blame; 602 598 } else { 603 - $blame = array(); 599 + $blame_list = array(); 600 + $blame_commits = array(); 604 601 } 605 602 606 603 $file_corpus = $file_content->getCorpus(); ··· 608 605 $can_highlight = (strlen($file_corpus) <= $highlight_limit); 609 606 610 607 if (!$show_color) { 611 - $lines = phutil_split_lines($file_corpus); 612 - 613 - $style = 614 - 'border: none; width: 100%; height: 80em; font-family: monospace'; 615 - if (!$show_blame) { 616 - $corpus = phutil_tag( 617 - 'textarea', 618 - array( 619 - 'style' => $style, 620 - ), 621 - $file_corpus); 622 - } else { 623 - $rows = array(); 624 - foreach ($lines as $line_number => $line) { 625 - $commit = idx($blame, $line_number); 626 - if ($commit) { 627 - $author = $commit->renderAuthorShortName($blame_handles); 628 - $commit_name = $commit->getShortName(); 629 - } else { 630 - $author = null; 631 - $commit_name = null; 632 - } 633 - 634 - $rows[] = sprintf( 635 - '%-10s %-20s %s', 636 - $commit_name, 637 - $author, 638 - $line); 639 - } 640 - 641 - $corpus = phutil_tag( 642 - 'textarea', 643 - array( 644 - 'style' => $style, 645 - ), 646 - implode('', $rows)); 647 - } 608 + $corpus = $this->renderPlaintextCorpus( 609 + $file_corpus, 610 + $blame_list, 611 + $blame_commits, 612 + $show_blame); 648 613 } else { 649 614 if ($can_highlight) { 650 615 require_celerity_resource('syntax-highlighting-css'); ··· 659 624 660 625 $rows = $this->buildDisplayRows( 661 626 $lines, 662 - $blame, 627 + $blame_list, 628 + $blame_commits, 663 629 $show_blame, 664 630 $show_color); 665 631 ··· 883 849 884 850 private function buildDisplayRows( 885 851 array $lines, 886 - array $blame, 852 + array $blame_list, 853 + array $blame_commits, 887 854 $show_color, 888 855 $show_blame) { 889 856 857 + $request = $this->getRequest(); 858 + $viewer = $this->getViewer(); 890 859 $drequest = $this->getDiffusionRequest(); 891 860 $repository = $drequest->getRepository(); 892 861 893 - $handles = array(); 894 - if ($blame) { 895 - $epoch_list = mpull($blame, 'getEpoch', 'getID'); 896 - $epoch_list = array_filter($epoch_list); 862 + $revision_map = array(); 863 + $revisions = array(); 864 + if ($blame_commits) { 865 + $commit_map = mpull($blame_commits, 'getCommitIdentifier', 'getPHID'); 866 + 867 + $revision_ids = id(new DifferentialRevision()) 868 + ->loadIDsByCommitPHIDs(array_keys($commit_map)); 869 + if ($revision_ids) { 870 + $revisions = id(new DifferentialRevisionQuery()) 871 + ->setViewer($viewer) 872 + ->withIDs($revision_ids) 873 + ->execute(); 874 + $revisions = mpull($revisions, null, 'getID'); 875 + } 876 + 877 + foreach ($revision_ids as $commit_phid => $revision_id) { 878 + $revision_map[$commit_map[$commit_phid]] = $revision_id; 879 + } 880 + } 881 + 882 + $phids = array(); 883 + foreach ($blame_commits as $commit) { 884 + $author_phid = $commit->getAuthorPHID(); 885 + if ($author_phid === null) { 886 + continue; 887 + } 888 + $phids[$author_phid] = $author_phid; 889 + } 890 + 891 + foreach ($revisions as $revision) { 892 + $author_phid = $revision->getAuthorPHID(); 893 + if ($author_phid === null) { 894 + continue; 895 + } 896 + $phids[$author_phid] = $author_phid; 897 + } 898 + 899 + $handles = $viewer->loadHandles($phids); 900 + 901 + $colors = array(); 902 + if ($blame_commits) { 903 + $epochs = array(); 904 + 905 + foreach ($blame_commits as $identifier => $commit) { 906 + $epochs[$identifier] = $commit->getEpoch(); 907 + } 908 + 909 + $epoch_list = array_filter($epochs); 897 910 $epoch_list = array_unique($epoch_list); 898 911 $epoch_list = array_values($epoch_list); 899 912 900 913 $epoch_min = min($epoch_list); 901 914 $epoch_max = max($epoch_list); 902 915 $epoch_range = ($epoch_max - $epoch_min) + 1; 916 + 917 + foreach ($blame_commits as $identifier => $commit) { 918 + $epoch = $epochs[$identifier]; 919 + if (!$epoch) { 920 + $color = '#ffffdd'; // Warning color, missing data. 921 + } else { 922 + $color_ratio = ($epoch - $epoch_min) / $epoch_range; 923 + $color_value = 0xE6 * (1.0 - $color_ratio); 924 + $color = sprintf( 925 + '#%02x%02x%02x', 926 + $color_value, 927 + 0xF6, 928 + $color_value); 929 + } 930 + 931 + $colors[$identifier] = $color; 932 + } 903 933 } 904 934 935 + $display = array(); 936 + $last_identifier = null; 937 + $last_color = null; 938 + foreach ($lines as $line_index => $line) { 939 + $color = '#f6f6f6'; 940 + $duplicate = false; 941 + if (isset($blame_list[$line_index])) { 942 + $identifier = $blame_list[$line_index]; 943 + if (isset($colors[$identifier])) { 944 + $color = $colors[$identifier]; 945 + } 946 + 947 + if ($identifier === $last_identifier) { 948 + $duplicate = true; 949 + } else { 950 + $last_identifier = $identifier; 951 + } 952 + } 953 + 954 + $display[$line_index] = array( 955 + 'data' => $line, 956 + 'target' => false, 957 + 'highlighted' => false, 958 + 'color' => $color, 959 + 'duplicate' => $duplicate, 960 + ); 961 + } 905 962 906 963 $line_arr = array(); 907 964 $line_str = $drequest->getLine(); ··· 921 978 } 922 979 } 923 980 924 - $display = array(); 925 - 926 - $line_number = 1; 927 - $last_commit = null; 928 - $color = null; 929 - foreach ($lines as $line_index => $line) { 930 - $display_line = array( 931 - 'epoch' => null, 932 - 'commit' => null, 933 - 'author' => null, 934 - 'target' => null, 935 - 'highlighted' => null, 936 - 'line' => $line_number, 937 - 'data' => $line, 938 - ); 939 - 940 - if ($show_blame) { 941 - // If the line's rev is same as the line above, show empty content 942 - // with same color; otherwise generate blame info. The newer a change 943 - // is, the more saturated the color. 944 - 945 - $commit = idx($blame, $line_index, $last_commit); 946 - 947 - if ($commit && $last_commit && 948 - ($last_commit->getID() == $commit->getID())) { 949 - $display_line['color'] = $color; 950 - } else { 951 - if ($commit) { 952 - $epoch = $commit->getEpoch(); 953 - } else { 954 - $epoch = null; 955 - } 956 - 957 - if (!$epoch) { 958 - if (!$blame) { 959 - $color = '#f6f6f6'; 960 - } else { 961 - $color = '#ffd'; // Render as warning. 962 - } 963 - } else { 964 - $color_ratio = ($epoch - $epoch_min) / $epoch_range; 965 - $color_value = 0xE6 * (1.0 - $color_ratio); 966 - $color = sprintf( 967 - '#%02x%02x%02x', 968 - $color_value, 969 - 0xF6, 970 - $color_value); 971 - } 972 - 973 - $display_line['epoch'] = $epoch; 974 - $display_line['color'] = $color; 975 - 976 - if ($commit) { 977 - $display_line['commit'] = $commit; 978 - } else { 979 - $display_line['commit'] = null; 980 - } 981 - 982 - $last_commit = $commit; 983 - } 981 + // Mark the first highlighted line as the target line. 982 + if ($line_arr) { 983 + $target_line = $line_arr[0]['min']; 984 + if (isset($display[$target_line - 1])) { 985 + $display[$target_line - 1]['target'] = true; 984 986 } 985 - 986 - if ($line_arr) { 987 - if ($line_number == $line_arr[0]['min']) { 988 - $display_line['target'] = true; 989 - } 990 - foreach ($line_arr as $range) { 991 - if ($line_number >= $range['min'] && 992 - $line_number <= $range['max']) { 993 - $display_line['highlighted'] = true; 994 - } 995 - } 996 - } 997 - 998 - $display[] = $display_line; 999 - ++$line_number; 1000 987 } 1001 988 1002 - $request = $this->getRequest(); 1003 - $viewer = $request->getUser(); 1004 - 1005 - $commits = mpull($blame, null, 'getCommitIdentifier'); 1006 - 1007 - $revision_ids = id(new DifferentialRevision()) 1008 - ->loadIDsByCommitPHIDs(mpull($commits, 'getPHID')); 1009 - $revisions = array(); 1010 - if ($revision_ids) { 1011 - $revisions = id(new DifferentialRevisionQuery()) 1012 - ->setViewer($viewer) 1013 - ->withIDs($revision_ids) 1014 - ->execute(); 1015 - } 1016 - 1017 - $phids = array(); 1018 - foreach ($commits as $blame_commit) { 1019 - if ($blame_commit->getAuthorPHID()) { 1020 - $phids[] = $blame_commit->getAuthorPHID(); 1021 - } 1022 - } 1023 - foreach ($revisions as $revision) { 1024 - if ($revision->getAuthorPHID()) { 1025 - $phids[] = $revision->getAuthorPHID(); 989 + // Mark all other highlighted lines as highlighted. 990 + foreach ($line_arr as $range) { 991 + for ($ii = $range['min']; $ii <= $range['max']; $ii++) { 992 + if (isset($display[$ii - 1])) { 993 + $display[$ii - 1]['highlighted'] = true; 994 + } 1026 995 } 1027 996 } 1028 - $handles = $this->loadViewerHandles($phids); 1029 997 1030 998 $engine = null; 1031 999 $inlines = array(); ··· 1059 1027 1060 1028 // NOTE: We're doing this manually because rendering is otherwise 1061 1029 // dominated by URI generation for very large files. 1062 - $line_base = (string)$repository->generateURI( 1030 + $line_base = (string)$drequest->generateURI( 1063 1031 array( 1064 1032 'action' => 'browse', 1065 1033 'stable' => true, ··· 1070 1038 Javelin::initBehavior('phabricator-tooltips'); 1071 1039 Javelin::initBehavior('phabricator-line-linker'); 1072 1040 1073 - foreach ($display as $line) { 1074 - $line_href = $line_base.'$'.$line['line']; 1041 + // Render these once, since they tend to get repeated many times in large 1042 + // blame outputs. 1043 + $commit_links = $this->renderCommitLinks($blame_commits, $handles); 1044 + $revision_links = $this->renderRevisionLinks($revisions, $handles); 1075 1045 1076 - $blame = array(); 1077 - $style = null; 1078 - if (array_key_exists('color', $line)) { 1079 - if ($line['color']) { 1080 - $style = 'background: '.$line['color'].';'; 1081 - } 1046 + $skip_text = pht('Skip Past This Commit'); 1047 + foreach ($display as $line_index => $line) { 1048 + $row = array(); 1082 1049 1083 - $before_link = null; 1084 - $commit_link = null; 1085 - $revision_link = null; 1086 - if (idx($line, 'commit')) { 1087 - $commit = $line['commit']; 1050 + $line_number = $line_index + 1; 1051 + $line_href = $line_base.'$'.$line_number; 1088 1052 1089 - if ($commit) { 1090 - $tooltip = $this->renderCommitTooltip( 1091 - $commit, 1092 - $handles, 1093 - $commit->renderAuthorLink($handles)); 1094 - } else { 1095 - $tooltip = null; 1096 - } 1053 + if (isset($blame_list[$line_index])) { 1054 + $identifier = $blame_list[$line_index]; 1055 + } else { 1056 + $identifier = null; 1057 + } 1097 1058 1098 - $commit_link = javelin_tag( 1099 - 'a', 1100 - array( 1101 - 'href' => $drequest->generateURI( 1102 - array( 1103 - 'action' => 'commit', 1104 - 'commit' => $commit->getCommitIdentifier(), 1105 - )), 1106 - 'sigil' => 'has-tooltip', 1107 - 'meta' => array( 1108 - 'tip' => $tooltip, 1109 - 'align' => 'E', 1110 - 'size' => 600, 1111 - ), 1112 - ), 1113 - $commit->getShortName()); 1059 + $revision_link = null; 1060 + $commit_link = null; 1061 + $before_link = null; 1062 + $style = null; 1063 + if ($identifier && !$line['duplicate']) { 1064 + $style = 'background: '.$line['color'].';'; 1114 1065 1115 - $revision_id = null; 1116 - if ($commit) { 1117 - $revision_id = idx($revision_ids, $commit->getPHID()); 1118 - } 1066 + if (isset($commit_links[$identifier])) { 1067 + $commit_link = $commit_links[$identifier]; 1068 + } 1119 1069 1120 - if ($revision_id) { 1121 - $revision = idx($revisions, $revision_id); 1122 - if ($revision) { 1123 - $tooltip = $this->renderRevisionTooltip($revision, $handles); 1124 - $revision_link = javelin_tag( 1125 - 'a', 1126 - array( 1127 - 'href' => '/D'.$revision->getID(), 1128 - 'sigil' => 'has-tooltip', 1129 - 'meta' => array( 1130 - 'tip' => $tooltip, 1131 - 'align' => 'E', 1132 - 'size' => 600, 1133 - ), 1134 - ), 1135 - 'D'.$revision->getID()); 1136 - } 1137 - } 1138 - 1139 - if ($commit) { 1140 - $identifier = $commit->getCommitIdentifier(); 1141 - $skip_href = $line_href.'?before='.$identifier.'&view=blame'; 1142 - 1143 - $before_link = javelin_tag( 1144 - 'a', 1145 - array( 1146 - 'href' => $skip_href, 1147 - 'sigil' => 'has-tooltip', 1148 - 'meta' => array( 1149 - 'tip' => pht('Skip Past This Commit'), 1150 - 'align' => 'E', 1151 - 'size' => 300, 1152 - ), 1153 - ), 1154 - "\xC2\xAB"); 1070 + if (isset($revision_map[$identifier])) { 1071 + $revision_id = $revision_map[$identifier]; 1072 + if (isset($revision_links[$revision_id])) { 1073 + $revision_link = $revision_links[$revision_id]; 1155 1074 } 1156 1075 } 1157 1076 1158 - $blame[] = phutil_tag( 1159 - 'th', 1077 + $skip_href = $line_href.'?before='.$identifier.'&view=blame'; 1078 + $before_link = javelin_tag( 1079 + 'a', 1160 1080 array( 1161 - 'class' => 'diffusion-blame-link', 1081 + 'href' => $skip_href, 1082 + 'sigil' => 'has-tooltip', 1083 + 'meta' => array( 1084 + 'tip' => $skip_text, 1085 + 'align' => 'E', 1086 + 'size' => 300, 1087 + ), 1162 1088 ), 1163 - $before_link); 1089 + "\xC2\xAB"); 1090 + } 1164 1091 1165 - $object_links = array(); 1166 - $object_links[] = $commit_link; 1167 - if ($revision_link) { 1168 - $object_links[] = phutil_tag('span', array(), '/'); 1169 - $object_links[] = $revision_link; 1170 - } 1092 + $row[] = phutil_tag( 1093 + 'th', 1094 + array( 1095 + 'class' => 'diffusion-blame-link', 1096 + ), 1097 + $before_link); 1171 1098 1172 - $blame[] = phutil_tag( 1173 - 'th', 1174 - array( 1175 - 'class' => 'diffusion-rev-link', 1176 - ), 1177 - $object_links); 1099 + $object_links = array(); 1100 + $object_links[] = $commit_link; 1101 + if ($revision_link) { 1102 + $object_links[] = phutil_tag('span', array(), '/'); 1103 + $object_links[] = $revision_link; 1178 1104 } 1179 1105 1106 + $row[] = phutil_tag( 1107 + 'th', 1108 + array( 1109 + 'class' => 'diffusion-rev-link', 1110 + ), 1111 + $object_links); 1112 + 1180 1113 $line_link = phutil_tag( 1181 1114 'a', 1182 1115 array( 1183 1116 'href' => $line_href, 1184 1117 'style' => $style, 1185 1118 ), 1186 - $line['line']); 1119 + $line_number); 1187 1120 1188 - $blame[] = javelin_tag( 1121 + $row[] = javelin_tag( 1189 1122 'th', 1190 1123 array( 1191 1124 'class' => 'diffusion-line-link', ··· 1210 1143 $anchor_text = null; 1211 1144 } 1212 1145 1213 - $blame[] = phutil_tag( 1146 + $row[] = phutil_tag( 1214 1147 'td', 1215 1148 array( 1216 1149 ), ··· 1234 1167 $cov_class = 'N'; 1235 1168 } 1236 1169 1237 - $blame[] = phutil_tag( 1170 + $row[] = phutil_tag( 1238 1171 'td', 1239 1172 array( 1240 1173 'class' => 'cov cov-'.$cov_class, ··· 1249 1182 'phabricator-source-highlight' : 1250 1183 null), 1251 1184 ), 1252 - $blame); 1185 + $row); 1253 1186 1254 1187 $cur_inlines = $this->renderInlines( 1255 - idx($inlines, $line['line'], array()), 1188 + idx($inlines, $line_number, array()), 1256 1189 $show_blame, 1257 1190 $this->coverage, 1258 1191 $engine); ··· 1520 1453 1521 1454 private function renderCommitTooltip( 1522 1455 PhabricatorRepositoryCommit $commit, 1523 - array $handles, 1524 1456 $author) { 1525 1457 1526 1458 $viewer = $this->getRequest()->getUser(); ··· 1528 1460 $date = phabricator_date($commit->getEpoch(), $viewer); 1529 1461 $summary = trim($commit->getSummary()); 1530 1462 1531 - if ($commit->getAuthorPHID()) { 1532 - $author = $handles[$commit->getAuthorPHID()]->getName(); 1533 - } 1534 - 1535 1463 return "{$summary}\n{$date} \xC2\xB7 {$author}"; 1536 1464 } 1537 1465 ··· 1809 1737 $commits = array(); 1810 1738 } 1811 1739 1812 - foreach ($identifiers as $key => $identifier) { 1813 - $commit = idx($commits, $identifier); 1814 - if ($commit) { 1815 - $identifiers[$key] = $commit; 1740 + return array($identifiers, $commits); 1741 + } 1742 + 1743 + private function renderCommitLinks(array $commits, $handles) { 1744 + $links = array(); 1745 + foreach ($commits as $identifier => $commit) { 1746 + $tooltip = $this->renderCommitTooltip( 1747 + $commit, 1748 + $commit->renderAuthorShortName($handles)); 1749 + 1750 + $commit_link = javelin_tag( 1751 + 'a', 1752 + array( 1753 + 'href' => $commit->getURI(), 1754 + 'sigil' => 'has-tooltip', 1755 + 'meta' => array( 1756 + 'tip' => $tooltip, 1757 + 'align' => 'E', 1758 + 'size' => 600, 1759 + ), 1760 + ), 1761 + $commit->getShortName()); 1762 + 1763 + $links[$identifier] = $commit_link; 1764 + } 1765 + 1766 + return $links; 1767 + } 1768 + 1769 + private function renderRevisionLinks(array $revisions, $handles) { 1770 + $links = array(); 1771 + 1772 + foreach ($revisions as $revision) { 1773 + $revision_id = $revision->getID(); 1774 + 1775 + $tooltip = $this->renderRevisionTooltip($revision, $handles); 1776 + 1777 + $revision_link = javelin_tag( 1778 + 'a', 1779 + array( 1780 + 'href' => $revision->getURI(), 1781 + 'sigil' => 'has-tooltip', 1782 + 'meta' => array( 1783 + 'tip' => $tooltip, 1784 + 'align' => 'E', 1785 + 'size' => 600, 1786 + ), 1787 + ), 1788 + $revision->getMonogram()); 1789 + 1790 + $links[$revision_id] = $revision_link; 1791 + } 1792 + 1793 + return $links; 1794 + } 1795 + 1796 + private function renderPlaintextCorpus( 1797 + $file_corpus, 1798 + array $blame_list, 1799 + array $blame_commits, 1800 + $show_blame) { 1801 + 1802 + $viewer = $this->getViewer(); 1803 + 1804 + if (!$show_blame) { 1805 + $corpus = $file_corpus; 1806 + } else { 1807 + $author_phids = array(); 1808 + foreach ($blame_commits as $commit) { 1809 + $author_phid = $commit->getAuthorPHID(); 1810 + if ($author_phid === null) { 1811 + continue; 1812 + } 1813 + $author_phids[$author_phid] = $author_phid; 1814 + } 1815 + 1816 + if ($author_phids) { 1817 + $handles = $viewer->loadHandles($author_phids); 1816 1818 } else { 1817 - $identifiers[$key] = null; 1819 + $handles = array(); 1820 + } 1821 + 1822 + $authors = array(); 1823 + $names = array(); 1824 + foreach ($blame_commits as $identifier => $commit) { 1825 + $author = $commit->renderAuthorShortName($handles); 1826 + $name = $commit->getShortName(); 1827 + 1828 + $authors[$identifier] = $author; 1829 + $names[$identifier] = $name; 1818 1830 } 1831 + 1832 + $lines = phutil_split_lines($file_corpus); 1833 + 1834 + $rows = array(); 1835 + foreach ($lines as $line_number => $line) { 1836 + $commit_name = null; 1837 + $author = null; 1838 + 1839 + if (isset($blame_list[$line_number])) { 1840 + $identifier = $blame_list[$line_number]; 1841 + 1842 + if (isset($names[$identifier])) { 1843 + $commit_name = $names[$identifier]; 1844 + } 1845 + 1846 + if (isset($authors[$identifier])) { 1847 + $author = $authors[$identifier]; 1848 + } 1849 + } 1850 + 1851 + $rows[] = sprintf( 1852 + '%-10s %-20s %s', 1853 + $commit_name, 1854 + $author, 1855 + $line); 1856 + } 1857 + $corpus = implode('', $rows); 1819 1858 } 1820 1859 1821 - return $identifiers; 1860 + return phutil_tag( 1861 + 'textarea', 1862 + array( 1863 + 'style' => 'border: none; width: 100%; height: 80em; '. 1864 + 'font-family: monospace', 1865 + ), 1866 + $corpus); 1822 1867 } 1823 1868 1824 1869 }