Skip to content

100% CPU utilization on RPi4 #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Syrex-o opened this issue Jul 16, 2024 · 13 comments
Open

100% CPU utilization on RPi4 #93

Syrex-o opened this issue Jul 16, 2024 · 13 comments

Comments

@Syrex-o
Copy link

Syrex-o commented Jul 16, 2024

Is anyone else also facing the issue, that the CPU utilization reaches 100% on the receiving side?

I use a Pi4 2GB and have a constant 100% cpu utilization on one core.
image

@jerryneedell
Copy link
Contributor

Can you post the code you are running?

@Syrex-o
Copy link
Author

Syrex-o commented Jul 17, 2024

Sure,

I reduced it to the minimum and tested it. Still 100% on a single core:

#!/usr/bin/python3

import time, struct, sys, board, busio
import digitalio
import adafruit_rfm9x

RADIO_FREQ_MHZ = 434.0
CS = digitalio.DigitalInOut(board.CE1)
RESET = digitalio.DigitalInOut(board.D25)
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
radio = adafruit_rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ)

radio.tx_power = 23
radio.spreading_factor = 7
radio.enable_crc = True
radio.signal_bandwidth = 250000

if __name__ == '__main__':
    try:
        while True:
            packet = radio.receive(timeout=10, with_ack=False, with_header=False)
            if packet is not None:
                print("Received: ", packet)
    except KeyboardInterrupt:
        sys.stdout.flush()
    finally:
        sys.stdout.flush()

Python version is 3.9.2
Iḿ using the SX1278 modules (RA-02)

@jerryneedell
Copy link
Contributor

Thanks, I tried your code on a Pi5 (snce that is what I had handy) and the load gets distributed a bit better, but I'm sure it would be the same as your results on a Pi4. I'm not really surprised since there is nothing in the code that will give the CPU a "break". I just wanted to confirm the behaviour.
FYI, I am working on a version of the library that uses asyncio and I will give that a try to see if it make any difference. The new library is still a "Work In Progress", but you are welcome to try it. This library combines the RFM9x and RFM69 libraries and enables using FSK/OOK on the RFM9X. The repository is at : https://github.com/jerryneedell/CircuitPython_RFM.git . the example https://github.com/jerryneedell/CircuitPython_RFM/blob/main/examples/rfm9x_rh_asyncio_listen.py is probably the most relevant. I'll try adapting your example to see how it works but I won't be able to try it until later today. I'll also see if I can set up a Pi4 to were are comparing the same thing.

@Syrex-o
Copy link
Author

Syrex-o commented Jul 17, 2024

Thanks for testing :)
I´m a little bit confused by this behavior. I work with NRF24 modules and receiving sides with continuous listening and never had such a load.

I already saw your lib and appreciate your efforts :) I will definitely try your new lib and give feedback here.

@jerryneedell litte Update:
I even have slight improvements when using asyncio and this library.
I tried the pyLoRa library now as well. It acts equal.

Essentially my biggest concern is that a RPi Zero in 24/7 receive mode might have a short lifespan due to constant 100% usage.

@jerryneedell
Copy link
Contributor

I tried this with the new library and the CPU usage appears to be great;y reduced (on my Pi 5) - Note: I did change the frequency to 915 since I am in the USA.

#!/usr/bin/python3

import time, struct, sys, board, busio
import asyncio
import digitalio
from circuitpython_rfm import rfm9x

RADIO_FREQ_MHZ = 915.0
CS = digitalio.DigitalInOut(board.CE1)
RESET = digitalio.DigitalInOut(board.D25)
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
radio = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ)

radio.tx_power = 23
radio.spreading_factor = 7
radio.enable_crc = True
radio.signal_bandwidth = 250000

if __name__ == '__main__':
    try:
        while True:
            packet = radio.receive(timeout=10, with_header=False)
            if packet is not None:
                print("Received: ", packet)
    except KeyboardInterrupt:
        sys.stdout.flush()
    finally:
        sys.stdout.flush()

Now to see if I can find my Pi4...

@jerryneedell
Copy link
Contributor

jerryneedell commented Jul 17, 2024

Looking at the adafruit_rfm9x.py code, it may be possible to reduce the CPU usage by inserting a short sleep - something like "time.sleep(.001)" in the "receive" function -- https://github.com/adafruit/Adafruit_CircuitPython_RFM9x/blob/main/adafruit_rfm9x.py#L844

I don't think this will impact the receiver in list mode, it should just slow down the polling for the packet.
As it is now, the polling is continuous -- hence the high CPU usage.
I have not tried this -- your mileage may vary ;-)

Edited to add: I am curious if the 100%CPU usage is actually causing you problems or if it is just a curiosity.

@Syrex-o
Copy link
Author

Syrex-o commented Jul 17, 2024

Just tested with the new lib on a Pi4 and it works like a charm :)
image

Do you have a rough time estimation for the lib to go live on PyPI?
As soon as another Pi Zero arrives, I will post test results from it :)

Edit: I don´t really have a problem with the CPU usage. It is just my assumption.

@jerryneedell
Copy link
Contributor

Also FYI -- On a RPi Zero2W using the new library with the example above drops the CPU usage from 100% to about 75% on one core.

@jerryneedell
Copy link
Contributor

Do you have a rough time estimation for the lib to go live on PyPI?

Not really, but since you are the first person to actually express an interest in using it, I will try to get back to my testing. I just need to convince myself it "does no harm". With the combined libraries and new modes, it just takes awhile to create examples and test it on both the Raspberry Pis and other MCUs.
This was a great example of the usefulness of the asyncio addition . I really had not considered the impact on CPU usage. I does also appear to have some positive impact on the failure rate of transmissions and that has taken up a lot of testing time.

I'll post here when I make progress on releasing the new library.

Thanks for the testing.

@Syrex-o
Copy link
Author

Syrex-o commented Jul 18, 2024

What exactly do you mean by "harm"?

If you need any testing support, I'm open to assist :)

@jerryneedell
Copy link
Contributor

I just want to make sure it does note break anything that works in the current libraries.
If you use it, please let me know if you find any issues and feel free to offer comments on the code.

@crichmon762
Copy link

I ran into the same high load issue, which took a while to figure out because I was chasing missed/mangled messages. What I ended up doing was adding a 20ms delay in the receive loop where it waits for self.rx_done. The load went from mid-90% to <5%. A 50ms delay was too long and I was still missing/mangling received messages. I imagine the minimum time could be calculated based on the on-air time of the smallest packet at the fastest data rate. I get 4-5 messages as a block every 5min, and since this change, zero bad messages (actual contents checked with sha1sum on both ends).

@jerryneedell
Copy link
Contributor

jerryneedell commented Aug 11, 2024

You may want to try the new library https://github.com/adafruit/Adafruit_CircuitPython_RFM which utilizes asyncio. It has not been released yet, so it is not in the bundle or PyPi, but you can download it from the repository if you want to give it a try. Your code changes should be minimal to use it with your existing code, take a look at the examples. Any comments or suggestions would be welcome.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants