3
3
4
4
using System ;
5
5
using System . IO ;
6
+ using System . Linq ;
6
7
using System . Threading ;
7
8
using System . Threading . Tasks ;
8
9
using Microsoft . AspNetCore . Http ;
@@ -20,6 +21,7 @@ public class HostWarmupMiddleware
20
21
private readonly IEnvironment _environment ;
21
22
private readonly IScriptHostManager _hostManager ;
22
23
private readonly ILogger _logger ;
24
+ private string _assemblyLocalPath ;
23
25
24
26
public HostWarmupMiddleware ( RequestDelegate next , IScriptWebHostEnvironment webHostEnvironment , IEnvironment environment , IScriptHostManager hostManager , ILogger < HostWarmupMiddleware > logger )
25
27
{
@@ -28,6 +30,7 @@ public HostWarmupMiddleware(RequestDelegate next, IScriptWebHostEnvironment webH
28
30
_environment = environment ;
29
31
_hostManager = hostManager ;
30
32
_logger = logger ;
33
+ _assemblyLocalPath = Path . GetDirectoryName ( new Uri ( typeof ( HostWarmupMiddleware ) . Assembly . Location ) . LocalPath ) ;
31
34
}
32
35
33
36
public async Task Invoke ( HttpContext httpContext )
@@ -40,17 +43,62 @@ public async Task Invoke(HttpContext httpContext)
40
43
PreJitPrepare ( WarmUpConstants . LinuxJitTraceFileName ) ;
41
44
}
42
45
46
+ ReadRuntimeAssemblyFiles ( ) ;
47
+
43
48
await WarmUp ( httpContext . Request ) ;
44
49
}
45
50
46
51
await _next . Invoke ( httpContext ) ;
47
52
}
48
53
54
+ internal void ReadRuntimeAssemblyFiles ( )
55
+ {
56
+ try
57
+ {
58
+ string [ ] allFiles = Directory . GetFiles ( _assemblyLocalPath , "*.dll" , SearchOption . TopDirectoryOnly ) ;
59
+ // Read File content in 4K chunks
60
+ int maxBuffer = 4 * 1024 ;
61
+ byte [ ] chunk = new byte [ maxBuffer ] ;
62
+ Random random = new Random ( ) ;
63
+ foreach ( string file in allFiles )
64
+ {
65
+ // Read file content to avoid disk reads during specialization. This is only to page-in bytes.
66
+ ReadFileInChunks ( file , chunk , maxBuffer , random ) ;
67
+ }
68
+ _logger . LogDebug ( new EventId ( 100 , nameof ( ReadRuntimeAssemblyFiles ) ) , "Number of files read: '{allFilesCount}'. AssemblyLocalPath: '{assemblyLocalPath}' " , allFiles . Count ( ) , _assemblyLocalPath ) ;
69
+ }
70
+ catch ( Exception ex )
71
+ {
72
+ _logger . LogError ( new EventId ( 100 , nameof ( ReadRuntimeAssemblyFiles ) ) , ex , "Reading ReadRuntimeAssemblyFiles failed. AssemblyLocalPath: '{assemblyLocalPath}'" , _assemblyLocalPath ) ;
73
+ }
74
+ }
75
+
76
+ private void ReadFileInChunks ( string file , byte [ ] chunk , int maxBuffer , Random random )
77
+ {
78
+ try
79
+ {
80
+ using ( FileStream fileStream = new FileStream ( file , FileMode . Open , FileAccess . Read ) )
81
+ {
82
+ int bytesRead ;
83
+ while ( ( bytesRead = fileStream . Read ( chunk , 0 , maxBuffer ) ) != 0 )
84
+ {
85
+ // Read one random byte for every 4K bytes - 4K is default OS page size. This will help avoid disk read during specialization
86
+ // see for details on OS page buffering in Windows - https://docs.microsoft.com/en-us/windows/win32/fileio/file-buffering
87
+ var randomByte = Convert . ToInt32 ( chunk [ random . Next ( 0 , bytesRead - 1 ) ] ) ;
88
+ }
89
+ }
90
+ }
91
+ catch ( Exception ex )
92
+ {
93
+ _logger . LogError ( new EventId ( 100 , nameof ( ReadFileInChunks ) ) , ex , "Reading file '{file}' failed. AssemblyLocalPath: '{assemblyLocalPath}'" , file , _assemblyLocalPath ) ;
94
+ }
95
+ }
96
+
49
97
private void PreJitPrepare ( string jitTraceFileName )
50
98
{
51
99
// This is to PreJIT all methods captured in coldstart.jittrace file to improve cold start time
52
100
var path = Path . Combine (
53
- Path . GetDirectoryName ( new Uri ( typeof ( HostWarmupMiddleware ) . Assembly . Location ) . LocalPath ) ,
101
+ _assemblyLocalPath ,
54
102
WarmUpConstants . PreJitFolderName , jitTraceFileName ) ;
55
103
56
104
var file = new FileInfo ( path ) ;
0 commit comments