Skip to content
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

Speed optimizations to gpio.py #21

Closed
wants to merge 1 commit into from

Conversation

Gadgetoid
Copy link
Collaborator

These are discussed and shared by @sheffieldnick in #11

I have commited these changes for evaluation since they differ slightly to those in PR #12

These are discussed and shared by @sheffieldnick in vitiral#11

I have commited these changes for evaluation since they differ slightly to those in PR vitiral#12

Co-authored-by: sheffieldnick
@Gadgetoid
Copy link
Collaborator Author

I ran some tests on alternate methods of file IO and general pokery, boiling down to the following findings.

This is an absolute best case approximation of the current method of reading a pin 10 million times:

with open("/sys/class/gpio/gpio10/value", "w+") as f:
    for _ in range(10 * 1000 * 1000):
        f.seek(0)
        val = int(f.read())

This takes roughly 4min 30sec on my Pi 4.

Compare to using unbuffered bytes IO and some oldschool bytes-to-int conversion magc:

with open("/sys/class/gpio/gpio10/value", "rb", buffering=0) as f:
    for _ in range(10 * 1000 * 1000):
        f.seek(0)
        val = f.read()[0] - 48

This takes 1min 41sec on my Pi 4. Roughly 2.6x faster.

This is pretty reasonable. Avoiding a costly conversion from a "string" to an int should be faster - you'd think - but it's actually the switch from text to bytes IO that makes all the difference. Let's eliminate the conversion from whatever-to-int from the equation and just read the value. I'll do 1m iterations here because waiting is dull.

Here's regular ol' text mode:

    for _ in range(1 * 1000 * 1000):
        f.seek(0)
        val = f.read()

Which takes 25.15s.

And here's in bytes IO:

with open("/sys/class/gpio/gpio10/value", "rb", buffering=0) as f:
    for _ in range(1 * 1000 * 1000):
        f.seek(0)
        val = f.read()

Which took 9.78s.

Again the bytes method is roughly 2.6x faster and anything Python does to the values is largely inconsequential.

I applied these findings both to the read and set methods of this library (ignoring for now that the method used for writing breaks Python 2.x support) and confirmed on my oscilloscope that the result is - again - roughly 2.6x faster than this optimized version of the library.

With my own rewrite I achieve a 130KHz pin toggle frequency:

image

Running the same code against this change set yields roughly 53KHz:

image

My measured results are roughly 2.4x faster at - currently - the cost of Python 2.x support.

@Gadgetoid
Copy link
Collaborator Author

Replaced by #22

@Gadgetoid Gadgetoid closed this Aug 12, 2021
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

Successfully merging this pull request may close these issues.

None yet

1 participant