Re: Need advice about [NSInputStream read:maxLength:] performance.
Re: Need advice about [NSInputStream read:maxLength:] performance.
- Subject: Re: Need advice about [NSInputStream read:maxLength:] performance.
- From: Robert Monaghan <email@hidden>
- Date: Fri, 11 Sep 2015 07:48:12 +0200
Hi all..
I have run into this as well. One of my products uses 10GBaseT ethernet, and is often hobbled when moving data.
I don’t have a good solution to offer for performance, but this is what I have come up with.
- Sockets and NSObjects perform an awful lot of memcpy’s. (Unnecessary copying memory can be one huge waste of time..)
- There is no way to shortcut any stream/socket reads by providing a dedicated buffer. (i.e.: like a “PF_Ring" style ring buffer)
The way that I have improved this was to use GCDAsyncSocket, and then to provide my own subclass of NSMutableData.
This required me to hack the GCDAsyncSocket code to switch between a traditional NSMutableData object, and my own.
The solution worked pretty nicely. I was able to moderately reduce the number of memcpy’s, which in turn, picked up performance.
In my mind, the best solution is to create a pin-able piece of memory that can’t be paged out, and then have the networking stack “read/stream” functions write directly into it. I have considered writing a Network Kernel Extension, which would essential provide the functionality of PF_RING.. (This exists in Linux.) But it would involve a new set of skills.. :)
Curious to hear what others have done to pick up performance.
bob..
> On Sep 11, 2015, at 06:15, Jens Alfke <email@hidden> wrote:
>
> GCDAsyncSocket has an interesting approach, where you can tell it what you want to read, like a certain number of bytes, or everything up to a specific byte sequence (like CRLF), and it'll call you back when it's read it.
>
> Another approach would be to use synchronous I/O calls, of course, but Cocoa APIs don't make that easy to do.
>
> —Jens
>
>> On Sep 10, 2015, at 6:12 PM, Adam Kaplan <email@hidden> wrote:
>>
>> It’s amazing how many times we solve this same problem over-and-over. It’s so difficult because every case is slightly nuanced :(
>>
>> I agree with everything Jens said, as usual. A ring buffer is fastest, but if your max message size is unknown and you can’t do stream-style parsing, they’ll always be some nasty edge cases…
>>
>> Just feeding into an NSMutableData is a good approach too and probably the most common. There are some downsides, such as multi-threaded parsing complexity and manually having to manage the addition and removal of data blocks.
>>
>> A third tool in the arsenal is GCDs dispatch_data_t, which is very well suited for combining, traversing and removing arbitrarily-sized data blocks in a multi-threaded streaming environment. The downsides there are GCD's boilerplate overhead and a performance profile that is not always easy to reason about.
>>
>> — adam
>>
>>
>>> On Wednesday, September 9, 2015 at 6:30 PM, Jens Alfke wrote:
>>>
>>>
>>>> On Sep 9, 2015, at 2:07 PM, Motti Shneor <email@hidden (mailto:email@hidden)> wrote:
>>>> I need reading messages off the stream, each prefixed by 4 bytes containing its size. I was wondering if it was advisable to first read just the size (4 bytes) then read the rest of the message, or maybe it was better to have a big buffer into which to “drain” the stream, then parse my messages off the buffer?
>>>
>>> The first approach won’t work, because the amount of data received is unpredictable. For instance you might receive only three bytes, which isn’t enough to parse the data size. Or you might be able to read the data size, but then try to read a 100000-byte message and get only 1500 bytes of it.
>>>
>>> The usual approach when parsing data asynchronously from a network stream is to keep a variable-size buffer of unparsed data. When data arrives, you read all of it and append it to the buffer; then you try to parse the data in the buffer. If there’s not enough to parse yet, you give up till there’s more; or if you parse some data, you remove it from the start of the buffer.
>>>
>>> So in your case you’d first check whether the buffer has at least four bytes; if so, you look at the length, and check whether that many more bytes are available. If they are, you read the whole message and remove it from the buffer.
>>>
>>> An NSMutableData object works well as a buffer, if you’re not too concerned about high performance. The fastest implementation is probably some sort of custom ring buffer.
>>>
>>> —Jens
>>> _______________________________________________
>>> Do not post admin requests to the list. They will be ignored.
>>> Macnetworkprog mailing list (email@hidden (mailto:email@hidden))
>>> Help/Unsubscribe/Update your Subscription:
>>>
>>> This email sent to email@hidden (mailto:email@hidden)
>>
>>
>
> _______________________________________________
> Do not post admin requests to the list. They will be ignored.
> Macnetworkprog mailing list (email@hidden)
> Help/Unsubscribe/Update your Subscription:
>
> This email sent to email@hidden
_______________________________________________
Do not post admin requests to the list. They will be ignored.
Macnetworkprog mailing list (email@hidden)
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden