In a class I have a loop in which data, polled from a serial port, are written to a circular buffer (Byte Array), in the same loop I want to send sentences (from ReadPtr to the next LF-Byte) with an event handler to the main program. What is the best way (parameter type of the eventhandler) to do this as fast as possible and without creating much work for the carbage collector.
The originally used approach was to copy the data to a string:
string response = ;
for (i = 0; i < bytesToSend; i++)
response = response + (char)RingBuffer[ReadPtr + i];
OnDataReceived(this, response); // Call of eventhandler
The disadvantages of this approach is, as I think, that copying to the string is time consuming and that lots of string instances are created.
I tried another approach, namely to pass the RingBuffer, the startindex and the ByteCount as parameters of the eventargs with the eventhandler. This should be fast, as the RingBuffer is transferred as a reference type and no new objects are created (for GC). The disadvantage is that if the data are not read at once by the event listener, they can be overwritten with new data in the RingBuffer. A means to prevent this could be an autoResetEvent in the loop which is set when the eventhandler has read the data and is terminating. (This should slow down the application ?).
Another approach would be to create a new byte Array for each data sentence, copy the data in this Byte Array and use this new Byte Array as event argument. The disadvantage would be more work for GC.
Does anybody have an idea for better solutions or ideas how the disadvantage of the different approaches could be avoided?
@ ianlee74 -
Hi ianlee, thank you.
I want to use this in a driver for the bluetooth module. In the driver class the data from the serial port are polled. Then they are written to a circular buffer to do the splitting after the LF-characters. Then the sentences are transferred via an eventhandler to the “main program”. This was the mode the creator of the module driver used and I think this is good, as it makes it easy for the user to use the module.
Edit: I have thought about your suggestion. May be it is really faster to poll the data from code in the main program directly from the serial port or from the circular buffer.
You don’t have to worry about GC.
You simply can’t avoid GC run as long as you are using Strings to communicate with the Bluetooth module.
First, write a functional code, test it and make it robust. After that, you can instrument your code and optimize where it’s really important.
Some comment on your code.
I don’t think you need a circular buffer. Serial communication always use a FIFO pattern.
Create a string by casting any byte to a char only works with ANSI. NETMF Strings are UTF8 and some characters are more than 1 byte length (up to 4). You should use Encoding.UTF8 class to convert a message from a byte array.
Edit: NETMF strings are Unicode encoded. UTF8 is the only encoder available in NETMF and the more reliable way to convert a String to byte array.
Hi, NicolasG. Thank you for your reply.
Glad to hear that you think that the GC will not have important influence on the performance of the program.
Nevertheless I tried to avoid using strings instead of (static) Byte Arrays.
You said: optimize, where it is really important. Yes, thats the really important thing, I dont have much experience to see at a glance, where the most time consuming parts are to be searched.
You make me weep bitterly, if you say that I need no circular buffer. It took so long to have it free of bugs (as I hope). By the way: a circular buffer is a special kind of FIFO buffer.
Yes, using Encoding.UTF8 class is the better way. I tried it out. Casting any byte to a character lasts 6 times as long as e.g.
myString = new String(Encoding.UTF8.GetChars(myByteArray,0,count));
The method itself is, as I think, no problem in the direction from Byte Array to string, only in the other direction there would be problem.
I have the feeling, that using events to transmit the data to the main program could be the bottleneck.
Preliminary test revealed, that I could reach only a baudrate of around 10,000 to see the data in the main program.
I will try if polling is faster.
Thanks for your help and interest.
I just want to say that if you’re not an experienced coder, you can’t think about ALL aspects at the same time. Take care of your design first: simpler is better.
The whole Gadgeteer project is not designed for performance but to be easy to use by beginners.
The internal SerialPort use a simple FIFO buffer.
Circular buffers are used when there is a continuous stream of data comming from a peripheral that can not be interrupted by a buffer overflow (such as analog converter). A reader can then use only a part of the incomming data.
I guess that long messages are more efficient that short ones.