1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5#pragma once
6
7const unsigned int MinNsPerNormalizedYield = 37; // measured typically 37-46 on post-Skylake
8const unsigned int NsPerOptimalMaxSpinIterationDuration = 272; // approx. 900 cycles, measured 281 on pre-Skylake, 263 on post-Skylake
9
10extern unsigned int g_yieldsPerNormalizedYield;
11extern unsigned int g_optimalMaxNormalizedYieldsPerSpinIteration;
12
13void InitializeYieldProcessorNormalizedCrst();
14void EnsureYieldProcessorNormalizedInitialized();
15
16class YieldProcessorNormalizationInfo
17{
18private:
19 unsigned int yieldsPerNormalizedYield;
20 unsigned int optimalMaxNormalizedYieldsPerSpinIteration;
21 unsigned int optimalMaxYieldsPerSpinIteration;
22
23public:
24 YieldProcessorNormalizationInfo()
25 : yieldsPerNormalizedYield(g_yieldsPerNormalizedYield),
26 optimalMaxNormalizedYieldsPerSpinIteration(g_optimalMaxNormalizedYieldsPerSpinIteration),
27 optimalMaxYieldsPerSpinIteration(yieldsPerNormalizedYield * optimalMaxNormalizedYieldsPerSpinIteration)
28 {
29 }
30
31 friend void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &);
32 friend void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &, unsigned int);
33 friend void YieldProcessorWithBackOffNormalized(const YieldProcessorNormalizationInfo &, unsigned int);
34};
35
36FORCEINLINE void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &normalizationInfo)
37{
38 LIMITED_METHOD_CONTRACT;
39
40 unsigned int n = normalizationInfo.yieldsPerNormalizedYield;
41 _ASSERTE(n != 0);
42 do
43 {
44 YieldProcessor();
45 } while (--n != 0);
46}
47
48FORCEINLINE void YieldProcessorNormalized(const YieldProcessorNormalizationInfo &normalizationInfo, unsigned int count)
49{
50 LIMITED_METHOD_CONTRACT;
51 _ASSERTE(count != 0);
52
53 if (sizeof(SIZE_T) <= sizeof(unsigned int))
54 {
55 // On platforms with a small SIZE_T, prevent overflow on the multiply below. normalizationInfo.yieldsPerNormalizedYield
56 // is limited to MinNsPerNormalizedYield by InitializeYieldProcessorNormalized().
57 const unsigned int MaxCount = (unsigned int)SIZE_T_MAX / MinNsPerNormalizedYield;
58 if (count > MaxCount)
59 {
60 count = MaxCount;
61 }
62 }
63
64 SIZE_T n = (SIZE_T)count * normalizationInfo.yieldsPerNormalizedYield;
65 _ASSERTE(n != 0);
66 do
67 {
68 YieldProcessor();
69 } while (--n != 0);
70}
71
72FORCEINLINE void YieldProcessorWithBackOffNormalized(
73 const YieldProcessorNormalizationInfo &normalizationInfo,
74 unsigned int spinIteration)
75{
76 LIMITED_METHOD_CONTRACT;
77
78 // normalizationInfo.optimalMaxNormalizedYieldsPerSpinIteration cannot exceed the value below based on calculations done in
79 // InitializeYieldProcessorNormalized()
80 const unsigned int MaxOptimalMaxNormalizedYieldsPerSpinIteration =
81 NsPerOptimalMaxSpinIterationDuration * 3 / (MinNsPerNormalizedYield * 2) + 1;
82 _ASSERTE(normalizationInfo.optimalMaxNormalizedYieldsPerSpinIteration <= MaxOptimalMaxNormalizedYieldsPerSpinIteration);
83
84 // This shift value should be adjusted based on the asserted condition below
85 const UINT8 MaxShift = 3;
86 static_assert_no_msg(((unsigned int)1 << (MaxShift + 1)) >= MaxOptimalMaxNormalizedYieldsPerSpinIteration);
87
88 unsigned int n;
89 if (spinIteration <= MaxShift &&
90 ((unsigned int)1 << spinIteration) < normalizationInfo.optimalMaxNormalizedYieldsPerSpinIteration)
91 {
92 n = ((unsigned int)1 << spinIteration) * normalizationInfo.yieldsPerNormalizedYield;
93 }
94 else
95 {
96 n = normalizationInfo.optimalMaxYieldsPerSpinIteration;
97 }
98 _ASSERTE(n != 0);
99 do
100 {
101 YieldProcessor();
102 } while (--n != 0);
103}
104