-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Quadratic runtime when sending many requests #87
Comments
Huh, weird. I'll investigate. As a quick note, I don't think my recent changes will have affected that, as they imposed a linear cost on each header block encoded, and header block encoding is always going to be linear-time anyway. So we need to investigate further and find where the quadratic operation count is. |
Hrm, this is a nasty one. Regardless of whether or not the HTTP/2 stack has any quadratic runtime in it, we're not getting a chance to exercise it. This is because we're hitting a quadratic runtime problem in NIO's scheduler first. We'll need to resolve that before we go any further. The issue is fundamentally to do with the way our While we wait for the above Swift bug to be fixed I'm also going to rewrite the I am not confident this is the end of the quadratic behaviour this test will see. In particular, I'm sure there is a quadratic loop in the HTTP/2 code that I've already found that this test should hit. With that one I'm less confident whether we should fix it or let it sit there, but that's a discussion for another day: first, let's fix the quadratic behaviour we know about. |
See apple/swift-nio#960 for the fix for |
Wow, impressive detective work! Looking forward to seeing what the performance of this test will be once all the quadratic runtime issues are fixed. |
Incidentally, there's another major performance gain you can have here that works today. Right now the test spins in a loop on In the pre- Put another way, the cross-thread communication still utterly dominates the quadratic runtime of the heap logic if the iteration count gets large enough. |
@MrMage Is this problem still outstanding for you? |
@Lukasa I haven't tried recently; my understanding was that there were other causes of quadratic behavior that have yet to be fixed. Is that still the case or should I re-run our tests? |
So there's one possible source of quadratic behaviour in your code, which is that calls to However, I don't believe that quadratic behaviour is likely to be a performance problem in real programs. Your test wildly exceeds So I'm interested in seeing whether you're still having trouble. If your test runs in an acceptable amount of time then I'm going to consider that quadratic behaviour low-urgency to address. |
@Lukasa thank you for the elaboration! I can confirm that sending requests is now very fast, even from "off" the event loop (I guess acquiring 2000 locks isn't a major bottleneck yet). However, waiting for all requests to be received takes three times longer for each doubling of the request count:
Looking at the Instruments traces, it does appear that flushing is the culprit here, and is a bottleneck, mostly on the client: I assume this is the behavior you are describing; given that it only occurs in a fairly "extreme" test right now, there's probably no point in trying to fix this. |
Follow-up: I can confirm that injecting a
inside the "send" loop completely eliminates the quadratic-runtime issue. However, that runtime appears to not be very relevant for 2000 requests, anyway; it only becomes apparent when sending 4000 or more requests. |
Ok, for now I think we can accept that if you're exceeding SETTINGS_MAX_CONCURRENT_STREAMS by 40x then you may begin to see performance problems and want to rearchitect your application. We can revisit this decision down the road if it becomes worthwhile. Let's call this done for now. Thanks for the report, it led to a number of useful fixes! |
Sorry, why does this work? |
The test runtime explodes because each |
* Avoid quadratic runtime in `testUnaryLotsOfRequests`. See apple/swift-nio-http2#87 (comment). * Decrease a few test timeouts (so tests pass in Release builds as well) and improve test error reporting. * Delay spinning up an event loop group for each test until the test is actually being set up. This avoid prematurely spinning up dozens of event loop threads when the test cases get allocated.
Ahh of course, thanks! |
* Avoid quadratic runtime in `testUnaryLotsOfRequests`. See apple/swift-nio-http2#87 (comment). * Decrease a few test timeouts (so tests pass in Release builds as well) and improve test error reporting. * Delay spinning up an event loop group for each test until the test is actually being set up. This avoid prematurely spinning up dozens of event loop threads when the test cases get allocated.
* Avoid quadratic runtime in `testUnaryLotsOfRequests`. See apple/swift-nio-http2#87 (comment). * Decrease a few test timeouts (so tests pass in Release builds as well) and improve test error reporting. * Delay spinning up an event loop group for each test until the test is actually being set up. This avoid prematurely spinning up dozens of event loop threads when the test cases get allocated.
Using NIOHTTP2 version 1.0.1.
Caveat: We might be doing something wrong in SwiftGRPC and/or this might be fixed by @Lukasa's recent changes, but I figured you guys might want to take a look.
When I run https://github.com/grpc/grpc-swift/blob/da1a59c919555367abfd6e2ffb285a175ffa11c0/Tests/SwiftGRPCNIOTests/NIOFunctionalTests.swift#L79 in Release mode with
numberOfRequests = 2_000
:When run with
numberOfRequests = 4_000
:Partial Instruments screenshots:
The text was updated successfully, but these errors were encountered: