Skip to content

Commit 12038b4

Browse files
authored
Optimization: HostWarmupMiddleware (JIT profile fix and async throughput) (#8588)
This is from some worker profiling and noticed the JIT trace here was running each invocation which looked unintended - confirmed with @safihamid. This now runs for `JitTraceRuntime` just once per instance of the middleware (effectively: per host, but if we _did_ change the middleware ever it could re-run for future flexibility). This also means the profile it creates on disk would be the _last_ instead of the _first_. Whether these differ at all would depend on how busy the stamp is. While in here, also removed a few allocations and the async overhead of the middleware for _all_ requests (whether in warmup state or not). It's not much, but about a 1% throughput gain from lower latency per request. #### Benchmarking ```powershell .\bombard.exe -k -l -c 100 -d 60s "http://localhost:5000/api/HttpTriggerAnon?name=bob" ``` #### Before (~61,050 rps) ```lua Statistics Avg Stdev Max Reqs/sec 61336.81 13051.80 131179.96 Latency 1.63ms 427.90us 317.42ms Latency Distribution 50% 1.51ms 75% 2.00ms 90% 2.00ms 95% 2.06ms 99% 5.00ms HTTP codes: 1xx - 0, 2xx - 3677932, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 14.67MB/s Statistics Avg Stdev Max Reqs/sec 61108.92 12781.70 76012.83 Latency 1.63ms 395.52us 66.31ms Latency Distribution 50% 1.51ms 75% 2.00ms 90% 2.00ms 95% 2.07ms 99% 5.04ms HTTP codes: 1xx - 0, 2xx - 3665847, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 14.62MB/s Statistics Avg Stdev Max Reqs/sec 60706.31 12939.59 77057.50 Latency 1.64ms 433.08us 74.49ms Latency Distribution 50% 1.51ms 75% 2.00ms 90% 2.00ms 95% 2.09ms 99% 5.00ms HTTP codes: 1xx - 0, 2xx - 3641578, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 14.53MB/s ``` #### After (~62,310 rps) ```lua Statistics Avg Stdev Max Reqs/sec 61925.27 13209.73 79099.13 Latency 1.61ms 451.98us 99.54ms Latency Distribution 50% 1.51ms 75% 2.00ms 90% 2.01ms 95% 2.21ms 99% 5.00ms HTTP codes: 1xx - 0, 2xx - 3715842, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 14.82MB/s Statistics Avg Stdev Max Reqs/sec 62510.94 12910.73 81487.14 Latency 1.60ms 405.25us 99.18ms Latency Distribution 50% 1.51ms 75% 2.00ms 90% 2.01ms 95% 2.21ms 99% 4.53ms HTTP codes: 1xx - 0, 2xx - 3749532, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 14.96MB/s Statistics Avg Stdev Max Reqs/sec 62496.97 12958.93 78327.32 Latency 1.60ms 394.55us 96.56ms Latency Distribution 50% 1.50ms 75% 2.00ms 90% 2.00ms 95% 2.05ms 99% 5.00ms HTTP codes: 1xx - 0, 2xx - 3748852, 3xx - 0, 4xx - 0, 5xx - 0 others - 0 Throughput: 14.96MB/s ``` ### Pull request checklist * [x] My changes **do not** require documentation changes * [ ] Otherwise: Documentation issue linked to PR * [x] My changes **should not** be added to the release notes for the next release * [ ] Otherwise: I've added my notes to `release_notes.md` * [ ] My changes **do not** need to be backported to a previous version * [ ] Otherwise: Backport tracked by issue/PR #issue_or_pr * [x] My changes **do not** require diagnostic events changes * Otherwise: I have added/updated all related diagnostic events and their documentation (Documentation issue linked to PR) * [ ] I have added all required tests (Unit tests, E2E tests)
1 parent 4b38a37 commit 12038b4

File tree

1 file changed

+26
-8
lines changed

1 file changed

+26
-8
lines changed

src/WebJobs.Script.WebHost/Middleware/HostWarmupMiddleware.cs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ public class HostWarmupMiddleware
2222
private readonly IScriptHostManager _hostManager;
2323
private readonly ILogger _logger;
2424
private string _assemblyLocalPath;
25+
private volatile bool _jitTraceHasRun;
26+
27+
private static readonly PathString _warmupRoutePath = new PathString($"/api/{WarmUpConstants.FunctionName}");
28+
private static readonly PathString _warmupRouteAlternatePath = new PathString($"/api/{WarmUpConstants.AlternateRoute}");
2529

2630
public HostWarmupMiddleware(RequestDelegate next, IScriptWebHostEnvironment webHostEnvironment, IEnvironment environment, IScriptHostManager hostManager, ILogger<HostWarmupMiddleware> logger)
2731
{
@@ -33,20 +37,35 @@ public HostWarmupMiddleware(RequestDelegate next, IScriptWebHostEnvironment webH
3337
_assemblyLocalPath = Path.GetDirectoryName(new Uri(typeof(HostWarmupMiddleware).Assembly.Location).LocalPath);
3438
}
3539

36-
public async Task Invoke(HttpContext httpContext)
40+
public Task Invoke(HttpContext httpContext)
3741
{
3842
if (IsWarmUpRequest(httpContext.Request, _webHostEnvironment.InStandbyMode, _environment))
43+
{
44+
return WarmupInvoke(httpContext);
45+
}
46+
47+
return _next.Invoke(httpContext);
48+
}
49+
50+
/// <summary>
51+
/// This is so we only pay the async overhead while in the warmup path, but not for primary runtime.
52+
/// </summary>
53+
public async Task WarmupInvoke(HttpContext httpContext)
54+
{
55+
// We only want to run our JIT traces on the first warmup call.
56+
if (!_jitTraceHasRun)
3957
{
4058
PreJitPrepare(WarmUpConstants.JitTraceFileName);
4159
if (_environment.IsLinuxConsumption())
4260
{
4361
PreJitPrepare(WarmUpConstants.LinuxJitTraceFileName);
4462
}
63+
_jitTraceHasRun = true;
64+
}
4565

46-
ReadRuntimeAssemblyFiles();
66+
ReadRuntimeAssemblyFiles();
4767

48-
await WarmUp(httpContext.Request);
49-
}
68+
await WarmUp(httpContext.Request);
5069

5170
await _next.Invoke(httpContext);
5271
}
@@ -127,10 +146,9 @@ public async Task WarmUp(HttpRequest request)
127146

128147
public static bool IsWarmUpRequest(HttpRequest request, bool inStandbyMode, IEnvironment environment)
129148
{
130-
return inStandbyMode &&
131-
((environment.IsAppService() && request.IsAppServiceInternalRequest(environment)) || environment.IsLinuxConsumption()) &&
132-
(request.Path.StartsWithSegments(new PathString($"/api/{WarmUpConstants.FunctionName}")) ||
133-
request.Path.StartsWithSegments(new PathString($"/api/{WarmUpConstants.AlternateRoute}")));
149+
return inStandbyMode
150+
&& ((environment.IsAppService() && request.IsAppServiceInternalRequest(environment)) || environment.IsLinuxConsumption())
151+
&& (request.Path.StartsWithSegments(_warmupRoutePath) || request.Path.StartsWithSegments(_warmupRouteAlternatePath));
134152
}
135153
}
136154
}

0 commit comments

Comments
 (0)