none
Performance for System.IO.Ports.SerialStream.ReadAsync() RRS feed

  • Question

  • I need to make an application which will request data quite intensely from devices connected a number of comm ports (say 10 ports), at a speed of 9600 baud, one startbit, no parity and 1 stopbit (The actual case is slightly different, but this makes calculations easy :)), which dictates 10 bits per byte and a byte speed of 960 per second.

    One reading is initiated by a request message of a few bytes (say 10), the other endpoint of the serial line responds with a message of a few bytes (say 10). Half-duplex communication.
    The response shall be somewhat processed and offered as part of a bigger byte array for a client machine to read by TCP.
    I do not care about the processing and the TCP handling so far because I am certain that this is simple and fast.

    This shall be repeated as fast as the serial line (and the other endpoint) is capable of, which should be approx. 960 bytes/s divided by the number of bytes for each request/reply which is 20 resulting in 48 full requests/replies each second.
    This speed is within the requirements for the system.

    So I made an application doing this on one serial-line (I only have 2 ports on dev. machine, the other is used for simulating the device which is requested) by adding an extension method for the stream fetched by SerialPort.BaseStream, this method takes two arguments (in addition to the stream itself), namely number of bytes to read and a timeout.
    Implement this with ReadAsync from the stream until all bytes are read or timeout occurs (by one reader task and one timeout task and Task.WhenAny()).

    This works, functionally-wise, just perfect, but to my surprise very CPU intensive.
    Before any optimization this takes up about 12-15% of my quad core i5 Intel with enough memory (which is not a super fast, but decent desktop).
    Then I expect that the machine will be very tired by running this on 10 serial lines.

    By awaiting Task.Delay as long as expected for the number of bytes to read plus some margin, say 18 milliseconds totally, the CPU load decreases to approx. 1.5% which should mean approx. 15% when all the serial lines are requested as this single line.
    This may seem acceptable, but the production machine will be a "weaker" machine, guessing on 1/3 of the power of the test machine and consequently a load of 45%. Add some load for processing the bytes and communication "upwards" with TCP (or UDP) and the calculated total load will be more than 50% which worries me.

    I am really surprised regarding this slow performance which I assume is mostly due to context switching, anyone may confirm this (or opposite)?
    Any obvious mistakes?
    Any obvious optimizations?
    (I have tried to tune the FIFO buffers of the comm port, the given CPU load figures are after best effort).

    I realize that the questions are too hard to answer without source code and I may expose it on OneDrive if anyone is interested in helping me out.

    Saturday, November 25, 2017 5:05 PM

All replies

  • More info:

    I tried to change the code by calling SerialPort.ReadExisting() after awaiting Task.Delay instead of the async reading. The CPU load was about the same as before.

    Then I used some old code I made which does similar ReadExisting, but not based on SerialPort, but rather based on my own (naive) comm port implementation with native (PInterop) CreateFile, ReadFile etc. and the load dropped to approx 0.075% !!

    I assume that this proves that SerialPort is far, far less than optimized (as stated by many others as well).
    As mentioned, my implementation of comm port is quite naive and I would like to avoid using it without much further work, anyone knows a good implementation out there?
    (much better than System.IO.Ports.SerialPort of course)


    • Edited by EuroEager Saturday, November 25, 2017 9:41 PM
    Saturday, November 25, 2017 9:40 PM
  • Even more info:

    Using SerialPortStream from https://github.com/jcurl/SerialPortStream.git and calling ReadExisting() after same awaiting as described above results in load which is hardly measurable.

    Using the same SerialPortStream in the same async manner as System.IO.Ports.SerialPort.BaseStream as described in the start post the load drops to far less than 1% which is highly acceptable.

    I have had problems with SerialPort earlier too (e.g. ref: https://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport or http://zachsaw.blogspot.no/2010/07/net-serialport-woes.html, b.t.w. the SerialPortStream solves the latter as well).

    How could/can Microsoft release this kind of garbage at all and doing nothing to fix the problems?
    They are wrong if they claim that serial ports are not used anymore.

    I will probably not use System.IO.Ports.SerialPort ever again!

    Sunday, November 26, 2017 10:09 AM
  • Hi EuroEager,

    Thank you for posting here.

    >>Any obvious mistakes?Any obvious optimizations?

    Do you mean you want to make it more efficient? To release resources would be a good choice.

    >>https://www.sparxeng.com/blog/software/must-use-net-system-io-ports-serialport 

    This link provide a approach to use serial port with win32 API. Have you tried that?

    >>http://zachsaw.blogspot.no/2010/07/net-serialport-woes.html, b.t.w. the SerialPortStream 

    The link you provided I could not open it.

    >>How could/can Microsoft release this kind of garbage at all and doing nothing to fix the problems?

    If you wan to release, you could refer to the MSDN article. Microsoft provided Dispose to release resources.

    https://docs.microsoft.com/en-us/dotnet/standard/garbage-collection/unmanaged

    https://msdn.microsoft.com/en-us/library/system.idisposable.aspx?f=255&MSPPError=-2147217396

    Best Regards,

    Wendy


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Wednesday, November 29, 2017 2:47 AM
    Moderator
  • Thanks

    Yes, I have tried using win32 api and it works much better (faster), but also more work to do...

    Thought it was obvious that the link "http://zachsaw.blogspot.no/2010/07/net-serialport-woes.html" did not include the text starting at the comma after .html (", b.t.w. the SerialPortStream..."), sorry for that, my mistake.

    I did not understand your comment to my statement: "How could/can Microsoft release this kind of garbage at all and doing nothing to fix the problems?"
    The problem has nothing to do with resource release/garbage collection etc. to at all, the problem is that SerialPort simply does not work even close to optimal before it is closed/disposed/collected etc.

    Microsoft has included SerialPort since .net 2.0 and it has never been working good. One of the reasons to use .net is to avoid low-level programming and easier memory management etc., mostly this is working really good in .net and we expect that components provided by Microsoft as part of the framework works. P/Invoke should normally be used for functionality which is not part of .net framework and not as an alternative to badly written provided components (they should be fixed).

    Wednesday, November 29, 2017 7:55 AM