Reputation: 853
When I try to read from a serial port (uart) in Linux on an RaspberryPi I always get a CPU load of 5-10% when in a loop. As SerialPorts should be blocking, this shouldn't use that much cpu load, or am I wrong?
I tried two codes:
var port = new SerialPort("/dev/ttyUSB0", 57600);
port.Open();
while (true)
{
if (port.BytesToRead > 0)
{
while (port.BytesToRead > 0)
Console.Write($"{port.ReadByte().ToString("X2")} ");
Console.WriteLine("");
}
Thread.Sleep(100);
}
static int blockLimit = 100;
static void Main(string[] args)
{
var port = new SerialPort("/dev/ttyUSB0", 57600);
port.Open();
byte[] buffer = new byte[blockLimit];
Action kickoffRead = null;
kickoffRead = delegate
{
port.BaseStream.BeginRead(buffer, 0, buffer.Length, delegate (IAsyncResult ar)
{
try
{
int actualLength = port.BaseStream.EndRead(ar);
byte[] received = new byte[actualLength];
Buffer.BlockCopy(buffer, 0, received, 0, actualLength);
raiseAppSerialDataEvent(received);
}
catch (IOException exc)
{
handleAppSerialError(exc);
}
kickoffRead();
}, null);
};
kickoffRead();
while (true)
Thread.Sleep(1000);
}
private static void handleAppSerialError(IOException exc)
{
throw new NotImplementedException();
}
private static void raiseAppSerialDataEvent(byte[] received)
{
Console.WriteLine(BitConverter.ToString(received));
}
Both with the same result: Two processes which uses together 5% to 10% cpu load
Using .NET Core 3.0 Preview 2
and System.IO.Ports 4.6.0-preview-19073.11
on a RaspberryPi 3b+
running with HypriotOS 1.10.0
Upvotes: 5
Views: 4122
Reputation: 11
I've had the same problem and did some profiling using Jetbrains's dotTrace. I got the following info:
7.71% Poll • 25,862/25,862 ms • Interop+Serial.Poll(SafeHandle, PollEvents, Int32, out PollEvents)
7.71% PollEvents • System.IO.Ports.SerialStream.PollEvents(Int32, Boolean, Boolean, out Nullable)
7.71% IOLoop • System.IO.Ports.SerialStream.IOLoop
7.71% InnerInvoke • System.Threading.Tasks.Task.InnerInvoke
Thread #8
7 procent was about the same as I saw when running top -d 1
.
Looking at the called methods I assume that polling is used, which is not very efficient. Especially if the polling interval is very short. So i took a look at the dotnet source code on GitHub and noticed that the interval is hard-coded to 1 ms (line 845).
Interop.PollEvents events = PollEvents(
1,
pollReadEvents: hasPendingReads,
pollWriteEvents: hasPendingWrites,
out Interop.ErrorInfo? error);
I'll see if I can create an issue at the GitHub repo.
Upvotes: 1
Reputation: 1144
As for now (NET Core 3.1) SerialPort implementation is very CPU intensive. I've ported Mono SerialPort to Net Standard library according to dima117 response here .NET Core - Use System.IO.Ports.SerialPort in visual studio code
I've published it to github:
https://github.com/michaldobrodenka/System.IO.Ports.Mono
With this SerialPort implementation, CPU usage dropped from 25% to 5% on my allwinner h3 hw
Upvotes: 3