3 +----------------------------------------------------------------------+
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 //////////////////////////////////////////////////////////////////////////////
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
;
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
]);
81 if (count
> max_count
) {
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;
126 for (auto const& it
: profile
.data
->entryTypes
) {
127 if (EntryTypes(it
.first
.second
).isMonotypeState()) {
128 monotype
+= 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;
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
;
173 auto const p_cutoff
= RO::EvalBespokeArraySinkSpecializationThreshold
/ 100;
174 auto const p_sampled
= 1.0 * sampled
/ (sampled
+ unsampled
);
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();
201 return dt
== KindOfUninit
202 ? ArrayLayout(TopMonotypeVecLayout::Index())
203 : ArrayLayout(MonotypeVecLayout::Index(dt
));
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 //////////////////////////////////////////////////////////////////////////////