-
Notifications
You must be signed in to change notification settings - Fork 10.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom Executors Unexpected Performance Penalty #69053
Comments
Thanks for filing this. We are currently also benchmarking our NIO custom executors over here: apple/swift-nio#2536 Tl;dr: Right now our custom executor implementation allocates for every single job that is running. I have some patches to fix this up. However, there is an allocation in the runtime in |
From what I can tell in this case they'll never be any advantage to using a custom executor that runs on the EventLoop as you have already left the EventLoop when you created the unstructured Task to run your async code. |
Yeah the example above does have a bunch of caveats to it and I think we should ignore those for a second and assume that the underlying transport is fully structured as well. This is what I am showing in my PR and from my findings we can get very very close to the full NIO performance when using Concurrency as long as we get task executors. Just opened a PR that should tremendously increase the performance of our |
Yeah my final findings after a few hours of testing was basically what @ktoso said. @adam-fowler what I was trying to make happen was to skip the possibly-multiple context-switches that will happen in the 'write()' function... Basically buying one context switch so the 'write()' function can work without context switches after that. |
Description
An actor using a SwiftNIO
EventLoop
turns out to be slightly slower than an actor not using any custom executors.I came across this unexpected behavior when trying to optimize Vapor's OpenAPI integration to stream response bodies without constantly context-switching between a
Vapor.BodyStreamWriter
'sEventLoop
and a Swift-concurrency thread.The full thread of how i ended up going down this route is available here on the Swift Open Source slack server. However this issue not-only contains sufficient info regarding the problem, but also contains additional info about how i exactly tested this.
I also think that this might not necessarily be a bug, but just a suboptimal behavior which is planned to be optimized when e.g.
Task
s can accept custom executors as well.The tests have been done in RELEASE mode to ensure the legitimacy of the results.
I will refer to the implementation with actor using a NIO
EventLoop
custom executor as actor 1 / first implementation.And the implementation with non-custom-executor actor as actor 2 / second implementation.
Clone swift-openapi-generator/Examples/GreetingService.
Edit
Package.swift
to use this version of Vapor:For implementation 1, edit
Package.swift
to use this edition of swift-openapi-vapor (mahdibm/mmbm-test-actor
):For #2 implementation (
mahdibm/mmbm-test-actor2
):The actor in the #1 implementation looks like this:
The #2 implementation does not set a custom executor, but is otherwise identical:
I used a simple Vapor app to perform the tests:
This would take ~85s for the first implementation, and ~74s for the second.
The issue is clear here. The first implementation using a custom executor is ~15% slower than non-custom-executor implementation although
Vapor.BodyStreamWriter
performs its work on a NIOEventLoop
which when working with Swift-concurrency system, triggers a context switch.The tests were all done using Xcode 15 release, and on macOS 14.0.
The text was updated successfully, but these errors were encountered: