WebInputStream::connect() very slow performance

I’ve noticed some very slow performance in one of my plugins when connecting to remote resources, and traced it to WebInputStream::connect()

To reproduce the issue, I’ve written two minimal test apps, one in Objective-C calling into Cocoa directly, and one using JUCE. I used Apple’s Network Link Conditioner to emulate a fairly poor connection (5 Mb/s with 50ms latency), then ran 100 iterations of each “connect” operation and measured the average. The results are below.

The JUCE test:

Average “connect” time over 100 iterations: ~6 seconds

The Cocoa test:

Average “connect” time over 100 iterations: ~1 second

This correlates to comparisons with curl from the command line, which gives similar results to the Cocoa test.

I have my deployment target set to 10.12, so JUCE is using NSURLSession the same API as the Cocoa example.

What is the reason for this massive difference in performance?

Note: I get similar results regardless of whether I run the tests concurrently or sequentially.

that thing works fast here, are you sure its not related to handshake of SSL or something like that. that is slow…

@sambecket I’m using the same https URL for both the JUCE test and the Cocoa test, yet the JUCE code averages 6 times slower than calling directly into Cocoa. When you say “fast” can you give an example of a connection time using this method for a given URL over a given available bandwidth?

Internally, we just call the Cocoa functions, so can’t really see why it’d be vastly different. Maybe run it in the profiler and see where it’s blocking?

I’ve taken a deeper look at this now, and it turns out the difference in connection response time is explained by the fact I’m using NSURLSession dataTaskWithRequest:completionHandler: in my Cocoa test and JUCE uses NSURLSession sessionWithConfiguration:delegate:.

I can reproduce the differences in performance if I take JUCE out of the equation and just call these methods directly.

I’m curious as to why using the delegate-based method is so much slower than using the block-based completion handler, I’ve asked on Stack Overflow about this and I’ll post back here if anything potentially useful comes up.

OK, it turns out that the delegate vs completionHandler difference was a red herring.

The correct explanation is that my Cocoa test is using [NSURLSession sharedSession] and thus re-using the same NSURLSession for each connection request, whereas JUCE creates a new NSURLSession object for every connect() call.

This is significant not only for multiple successive connections to the same URL but also for multiple connections to the same server because an NSURLSession may re-use the TCP-connection to the server or cache other resources between requests.

@jules Is there any likelihood of the JUCE dev team making WebInputStream persist session handles between calls to connect() in some future release, or is this something I’ll need to add myself…?

Thanks for the heads-up on this one, I guess it might be possible - obviously we have to take into account the fact that people may be doing lots of different operations from different threads, so may not be able to share a session, but we’ll investigate.

4 Likes

Sorry to revive a very old thread, but I’ve hit an issue of stream.connect() being very slow in my app as well.

Using something like postman I’m getting sub 100ms response times, but inside JUCE stream.connect(); takes > 300 - 700ms – is there any way to debug this further?