1+ using System ;
2+ using System . Runtime . CompilerServices ;
3+
4+ using BenchmarkDotNet . UnitTesting ;
5+
6+ using CodeJam . Arithmetic ;
7+
8+ using NUnit . Framework ;
9+
10+ using static CodeJam . AssemblyWideConfig ;
11+
12+ namespace CodeJam
13+ {
14+ /// <summary>
15+ /// Proof test: Aggressive inlining can be used to boost the code.
16+ /// </summary>
17+ [ TestFixture ( Category = PerfTestsConstants . PerfTestCategory + ": Self-testing" ) ]
18+ [ Explicit ( PerfTestsConstants . ExplicitExcludeReason ) ]
19+ public class AggressiveInliningPerfTests
20+ {
21+ // Use case:
22+ // 1. We have a complex logec split into multiple methods
23+ // 2. The logic is used on hotpath
24+ // 3. [MethodImpl(AggressiveInlining)] can be used to force the inlining and to speedup the code.
25+ // NB: use with care. In some cases inlining can result in significant slowdown.
26+ private const int Count = 10 * 1000 * 1000 ;
27+
28+ [ Test ]
29+ public void RunCaseAggInlineNoEffect ( ) =>
30+ CompetitionBenchmarkRunner . Run < CaseAggInlineNoEffect > ( RunConfig ) ;
31+
32+ public class CaseAggInlineNoEffect
33+ {
34+ #region PerfTest helpers
35+ private static int CallManualInline ( int i ) => i + 5 ;
36+
37+ // Will inline - auto
38+ private static int CallAuto1 ( int i ) => CallAuto2 ( i ) + 1 ;
39+ private static int CallAuto2 ( int i ) => CallAuto3 ( i ) + 1 ;
40+ private static int CallAuto3 ( int i ) => CallAuto4 ( i ) + 1 ;
41+ private static int CallAuto4 ( int i ) => CallAuto5 ( i ) + 1 ;
42+ private static int CallAuto5 ( int i ) => i + 1 ;
43+
44+ // Will NOT inline - forced
45+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
46+ private static int CallNoInline1 ( int i ) => CallNoInline2 ( i ) + 1 ;
47+
48+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
49+ private static int CallNoInline2 ( int i ) => CallNoInline3 ( i ) + 1 ;
50+
51+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
52+ private static int CallNoInline3 ( int i ) => CallNoInline4 ( i ) + 1 ;
53+
54+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
55+ private static int CallNoInline4 ( int i ) => CallNoInline5 ( i ) + 1 ;
56+
57+ [ MethodImpl ( MethodImplOptions . NoInlining ) ]
58+ private static int CallNoInline5 ( int i ) => i + 1 ;
59+
60+ // Will inline - forced
61+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
62+ private static int CallInline1 ( int i ) => CallInline2 ( i ) + 1 ;
63+
64+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
65+ private static int CallInline2 ( int i ) => CallInline3 ( i ) + 1 ;
66+
67+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
68+ private static int CallInline3 ( int i ) => CallInline4 ( i ) + 1 ;
69+
70+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
71+ private static int CallInline4 ( int i ) => CallInline5 ( i ) + 1 ;
72+
73+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
74+ private static int CallInline5 ( int i ) => i + 1 ;
75+ #endregion
76+
77+ [ CompetitionBaseline ]
78+ public int Test00Baseline ( )
79+ {
80+ var sum = 0 ;
81+ for ( var i = 0 ; i < Count ; i ++ )
82+ {
83+ sum += CallManualInline ( i ) ;
84+ }
85+ return sum ;
86+ }
87+
88+ [ CompetitionBenchmark ( 0.97 , 1.04 ) ]
89+ public int Test01Auto ( )
90+ {
91+ var sum = 0 ;
92+ for ( var i = 0 ; i < Count ; i ++ )
93+ {
94+ sum += CallAuto1 ( i ) ;
95+ }
96+ return sum ;
97+ }
98+
99+ [ CompetitionBenchmark ( 10.33 , 11.01 ) ]
100+ public int Test02NoInline ( )
101+ {
102+ var sum = 0 ;
103+ for ( var i = 0 ; i < Count ; i ++ )
104+ {
105+ sum += CallNoInline1 ( i ) ;
106+ }
107+ return sum ;
108+ }
109+
110+
111+ [ CompetitionBenchmark ( 0.96 , 1.04 ) ]
112+ public int Test03AggressiveInline ( )
113+ {
114+ var sum = 0 ;
115+ for ( var i = 0 ; i < Count ; i ++ )
116+ {
117+ sum += CallInline1 ( i ) ;
118+ }
119+ return sum ;
120+ }
121+ }
122+
123+ [ Test ]
124+ public void RunCaseAggInlineEffective ( ) =>
125+ CompetitionBenchmarkRunner . Run < CaseAggInlineEffective > ( RunConfig ) ;
126+
127+ public class CaseAggInlineEffective
128+ {
129+ #region PerfTest helpers
130+ // Will NOT inline - auto
131+ private struct StructAuto < T >
132+ {
133+ private static readonly Func < T , T , int > _compareFunc = Operators < T > . Compare ;
134+
135+ public StructAuto ( T value , bool validate )
136+ {
137+ if ( validate )
138+ {
139+ if ( _compareFunc ( value , default ( T ) ) != 0 )
140+ {
141+ throw new Exception ( "1" ) ;
142+ }
143+ }
144+ else
145+ {
146+ if ( value == null )
147+ {
148+ throw new Exception ( "2" ) ;
149+ }
150+ }
151+ Value = value ;
152+ }
153+
154+ public T Value { get ; }
155+ }
156+
157+ // Will NOT inline - forced
158+ private struct StructNoInline < T >
159+ {
160+ private static readonly Func < T , T , int > _compareFunc = Operators < T > . Compare ;
161+
162+ public StructNoInline ( T value , bool validate )
163+ {
164+ if ( validate )
165+ {
166+ if ( _compareFunc ( value , default ( T ) ) != 0 )
167+ {
168+ throw new Exception ( "1" ) ;
169+ }
170+ }
171+ else
172+ {
173+ if ( value == null )
174+ {
175+ throw new Exception ( "2" ) ;
176+ }
177+ }
178+ Value = value ;
179+ }
180+
181+ public T Value { get ; }
182+ }
183+
184+ // Will inline - forced
185+ private struct StructInline < T >
186+ {
187+ private static readonly Func < T , T , int > _compareFunc = Operators < T > . Compare ;
188+
189+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
190+ public StructInline ( T value , bool validate )
191+ {
192+ if ( validate )
193+ {
194+ if ( _compareFunc ( value , default ( T ) ) != 0 )
195+ {
196+ throw new Exception ( "1" ) ;
197+ }
198+ }
199+ else
200+ {
201+ if ( value == null )
202+ {
203+ throw new Exception ( "2" ) ;
204+ }
205+ }
206+ Value = value ;
207+ }
208+
209+ public T Value { get ; }
210+ }
211+
212+ private static int CallManualInline ( int i ) => i + 1 ;
213+
214+ private static int CallAuto ( int i ) =>
215+ new StructAuto < int > ( i , false ) . Value + 1 ;
216+
217+ private static int CallNoInline ( int i ) =>
218+ new StructNoInline < int > ( i , false ) . Value + 1 ;
219+
220+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
221+ private static int CallInline ( int i ) =>
222+ new StructInline < int > ( i , false ) . Value + 1 ;
223+ #endregion
224+
225+ [ CompetitionBaseline ]
226+ public int Test00Baseline ( )
227+ {
228+ var sum = 0 ;
229+ for ( var i = 0 ; i < Count ; i ++ )
230+ {
231+ sum += CallManualInline ( i ) ;
232+ }
233+ return sum ;
234+ }
235+
236+ [ CompetitionBenchmark ( 5.82 , 6.20 ) ]
237+ public int Test01Auto ( )
238+ {
239+ var sum = 0 ;
240+ for ( var i = 0 ; i < Count ; i ++ )
241+ {
242+ sum += CallAuto ( i ) ;
243+ }
244+ return sum ;
245+ }
246+
247+ [ CompetitionBenchmark ( 5.74 , 6.12 ) ]
248+ public int Test02NoInline ( )
249+ {
250+ var sum = 0 ;
251+ for ( var i = 0 ; i < Count ; i ++ )
252+ {
253+ sum += CallNoInline ( i ) ;
254+ }
255+ return sum ;
256+ }
257+
258+ [ CompetitionBenchmark ( 0.98 , 1.05 ) ]
259+ public int Test03AggressiveInline ( )
260+ {
261+ var sum = 0 ;
262+ for ( var i = 0 ; i < Count ; i ++ )
263+ {
264+ sum += CallInline ( i ) ;
265+ }
266+ return sum ;
267+ }
268+ }
269+ }
270+ }
0 commit comments