Eliminate abstract "EmptyOrMonotype" layouts
[hiphop-php.git] / hphp / runtime / base / bespoke / layout-selection.cpp
blobc63ec71fa95baee2a5a6b6e840ca342188110971
1 /*
3 +----------------------------------------------------------------------+
4 | HipHop for PHP |
5 +----------------------------------------------------------------------+
6 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 3.01 of the PHP license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.php.net/license/3_01.txt |
12 | If you did not receive a copy of the PHP license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@php.net so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/base/bespoke/layout-selection.h"
20 #include "hphp/runtime/base/bespoke/layout.h"
21 #include "hphp/runtime/base/bespoke/monotype-dict.h"
22 #include "hphp/runtime/base/bespoke/monotype-vec.h"
23 #include "hphp/runtime/base/runtime-option.h"
25 namespace HPHP { namespace bespoke {
27 TRACE_SET_MOD(bespoke);
29 //////////////////////////////////////////////////////////////////////////////
31 namespace {
33 //////////////////////////////////////////////////////////////////////////////
35 using jit::ArrayLayout;
37 // Returns KeyTypes::Any if there's no good key type to specialize on.
38 KeyTypes selectKeyType(const SinkProfile& profile, double p_cutoff) {
39 assertx(profile.data);
41 auto const load = [](auto& x) { return x.load(std::memory_order_relaxed); };
43 auto const empty = load(profile.data->keyCounts[int(KeyTypes::Empty)]);
44 auto const ints = load(profile.data->keyCounts[int(KeyTypes::Ints)]);
45 auto const strs = load(profile.data->keyCounts[int(KeyTypes::Strings)]);
46 auto const sstrs = load(profile.data->keyCounts[int(KeyTypes::StaticStrings)]);
47 auto const any = load(profile.data->keyCounts[int(KeyTypes::Any)]);
49 auto const total = empty + ints + strs + sstrs + any;
50 if (!total) return KeyTypes::Any;
52 auto const p_ints = 1.0 * (empty + ints) / total;
53 auto const p_strs = 1.0 * (empty + strs + sstrs) / total;
54 auto const p_sstrs = 1.0 * (empty + sstrs) / total;
56 if (p_sstrs >= p_cutoff) return KeyTypes::StaticStrings;
57 if (p_strs >= p_cutoff) return KeyTypes::Strings;
58 if (p_ints >= p_cutoff) return KeyTypes::Ints;
59 return KeyTypes::Any;
62 // Returns kKindOfUninit if we should specialize on "monotype of unknown type".
63 // Returns kInvalidDataType if we should not specialize this sink on a monotype.
64 DataType selectValType(const SinkProfile& profile, double p_cutoff) {
65 assertx(profile.data);
67 auto const load = [](auto& x) { return x.load(std::memory_order_relaxed); };
69 auto const empty = load(profile.data->valCounts[SinkProfile::kNoValTypes]);
70 auto const any = load(profile.data->valCounts[SinkProfile::kAnyValType]);
72 uint64_t total = empty + any;
73 uint64_t max_count = 0;
74 auto max_index = safe_cast<int>(SinkProfile::kNumValTypes);
76 static_assert(SinkProfile::kNoValTypes == SinkProfile::kNumValTypes - 2);
77 static_assert(SinkProfile::kAnyValType == SinkProfile::kNumValTypes - 1);
78 for (auto i = 0; i < SinkProfile::kNoValTypes; i++) {
79 auto const count = load(profile.data->valCounts[i]);
80 total += count;
81 if (count > max_count) {
82 max_count = count;
83 max_index = i;
87 if (!total) return kInvalidDataType;
89 auto const p_empty = 1.0 * empty / total;
90 auto const p_max = 1.0 * (empty + max_count) / total;
91 auto const p_mono = 1.0 * (total - any) / total;
93 // TODO(kshaunak): We may want to specialize on empty in the future.
94 if (p_empty >= p_cutoff) return KindOfUninit;
95 if (p_max >= p_cutoff) return safe_cast<DataType>(max_index + kMinDataType);
96 if (p_mono >= p_cutoff) return KindOfUninit;
97 return kInvalidDataType;
100 ArrayLayout selectSourceLayout(LoggingProfile& profile) {
101 assertx(profile.data);
103 auto const mode = RO::EvalBespokeArraySpecializationMode;
104 if (mode == 1 || mode == 2) return ArrayLayout::Vanilla();
106 auto const load = [](auto& x) { return x.load(std::memory_order_relaxed); };
108 auto const logging = load(profile.data->loggingArraysEmitted);
110 if (!logging) return ArrayLayout::Vanilla();
112 uint64_t escalated = 0;
113 for (auto const& it : profile.data->events) {
114 auto const op = getArrayOp(it.first);
115 if (op == ArrayOp::EscalateToVanilla) escalated += it.second;
118 auto const p_cutoff = RO::EvalBespokeArraySourceSpecializationThreshold / 100;
119 auto const p_escalated = 1.0 * std::min(escalated, logging) / logging;
121 if (p_escalated > 1 - p_cutoff) return ArrayLayout::Vanilla();
123 uint64_t monotype = 0;
124 uint64_t total = 0;
126 for (auto const& it : profile.data->entryTypes) {
127 if (EntryTypes(it.first.second).isMonotypeState()) {
128 monotype += it.second;
130 total += it.second;
133 auto const p_monotype = 1.0 * monotype / total;
134 if (p_monotype >= p_cutoff) {
135 auto const sad = profile.data->staticSampledArray;
136 if (sad == nullptr) return ArrayLayout::Vanilla();
137 auto const mad = maybeMonoify(sad);
138 if (mad == nullptr) return ArrayLayout::Vanilla();
139 profile.setStaticBespokeArray(mad);
140 return ArrayLayout(mad->layoutIndex());
143 return ArrayLayout::Vanilla();
146 ArrayLayout selectSinkLayout(const SinkProfile& profile) {
147 assertx(profile.data);
149 auto const mode = RO::EvalBespokeArraySpecializationMode;
150 if (mode == 1) return ArrayLayout::Vanilla();
151 if (mode == 2) return ArrayLayout::Top();
153 auto const load = [](auto& x) { return x.load(std::memory_order_relaxed); };
155 auto const sampled = load(profile.data->sampledCount);
156 auto const unsampled = load(profile.data->unsampledCount);
158 if (!sampled) return unsampled ? ArrayLayout::Vanilla() : ArrayLayout::Top();
160 uint64_t vanilla = 0;
161 uint64_t monotype = 0;
162 uint64_t total = 0;
164 for (auto const& it : profile.data->sources) {
165 if (it.first->layout.vanilla()) {
166 vanilla += it.second;
167 } else if (it.first->layout.monotype()) {
168 monotype += it.second;
170 total += it.second;
173 auto const p_cutoff = RO::EvalBespokeArraySinkSpecializationThreshold / 100;
174 auto const p_sampled = 1.0 * sampled / (sampled + unsampled);
176 if (!total) {
177 if ((1 - p_sampled) >= p_cutoff) return ArrayLayout::Vanilla();
178 return ArrayLayout::Top();
181 auto const p_vanilla = p_sampled * vanilla / total + (1 - p_sampled);
182 auto const p_monotype = p_sampled * monotype / total;
184 if (p_vanilla >= p_cutoff) return ArrayLayout::Vanilla();
186 if (p_monotype >= p_cutoff) {
187 using AK = ArrayData::ArrayKind;
188 auto const vec = load(profile.data->arrCounts[AK::kVecKind / 2]) +
189 load(profile.data->arrCounts[AK::kPackedKind / 2]);
190 auto const dict = load(profile.data->arrCounts[AK::kDictKind / 2]) +
191 load(profile.data->arrCounts[AK::kMixedKind / 2]);
192 auto const keyset = load(profile.data->arrCounts[AK::kKeysetKind / 2]);
194 assertx(vec || dict || keyset);
195 if (bool(vec) + bool(dict) + bool(keyset) != 1) return ArrayLayout::Top();
197 auto const dt = selectValType(profile, p_cutoff);
198 if (dt == kInvalidDataType) return ArrayLayout::Top();
200 if (vec) {
201 return dt == KindOfUninit
202 ? ArrayLayout(TopMonotypeVecLayout::Index())
203 : ArrayLayout(MonotypeVecLayout::Index(dt));
206 if (dict) {
207 auto const kt = selectKeyType(profile, p_cutoff);
208 if (kt == KeyTypes::Any) return ArrayLayout::Bespoke();
209 return dt == KindOfUninit
210 ? ArrayLayout(TopMonotypeDictLayout::Index(kt))
211 : ArrayLayout(MonotypeDictLayout::Index(kt, dt));
215 return ArrayLayout::Top();
218 //////////////////////////////////////////////////////////////////////////////
222 //////////////////////////////////////////////////////////////////////////////
224 ArrayLayout layoutForSource(SrcKey sk) {
225 auto const profile = getLoggingProfile(sk);
226 return profile ? profile->layout : ArrayLayout::Vanilla();
229 ArrayLayout layoutForSink(const jit::TransIDSet& ids, SrcKey sk) {
230 // TODO(kshaunak): Delete this block when we can ser/de bespoke profiles.
231 auto const mode = RO::EvalBespokeArraySpecializationMode;
232 if (mode == 1) return ArrayLayout::Vanilla();
233 if (mode == 2) return ArrayLayout::Top();
235 if (ids.empty()) return ArrayLayout::Top();
236 auto result = ArrayLayout::Bottom();
237 for (auto const id : ids) {
238 auto const profile = getSinkProfile(id, sk);
239 if (profile) result = result | profile->layout;
241 return result == ArrayLayout::Bottom() ? ArrayLayout::Top() : result;
244 void selectBespokeLayouts() {
245 setLoggingEnabled(false);
246 eachSource([](auto& x) { x.layout = selectSourceLayout(x); });
247 eachSink([](auto& x) { x.layout = selectSinkLayout(x); });
248 Layout::FinalizeHierarchy();
249 startExportProfiles();
252 //////////////////////////////////////////////////////////////////////////////