-
Notifications
You must be signed in to change notification settings - Fork 21
Add CPU and kernel checks to performance tests #213
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -12,6 +12,7 @@ defmodule Circuits.GPIO.Diagnostics do | |||||
| to work on some devices. | ||||||
| """ | ||||||
| alias Circuits.GPIO | ||||||
| alias Circuits.GPIO.Diagnostics.CPU | ||||||
|
|
||||||
| @doc """ | ||||||
| Reminder for how to use report/2 | ||||||
|
|
@@ -55,14 +56,23 @@ defmodule Circuits.GPIO.Diagnostics do | |||||
| Input ids: #{inspect(in_identifiers)} | ||||||
| Backend: #{inspect(Circuits.GPIO.backend_info()[:name])} | ||||||
|
|
||||||
| == Functionality == | ||||||
|
|
||||||
| """, | ||||||
| Enum.map(results, &pass_text/1), | ||||||
| """ | ||||||
|
|
||||||
| write/2: #{round(speed_results.write_cps)} calls/s | ||||||
| read/1: #{round(speed_results.read_cps)} calls/s | ||||||
| write_one/3: #{round(speed_results.write_one_cps)} calls/s | ||||||
| read_one/2: #{round(speed_results.read_one_cps)} calls/s | ||||||
| == Performance == | ||||||
|
|
||||||
| Kernel: #{speed_results.uname} | ||||||
| CPU count: #{speed_results.cpu_count} | ||||||
| CPU speed: #{:erlang.float_to_binary(speed_results.speed_mhz, decimals: 1)} MHz | ||||||
| Warnings?: #{speed_results.warnings?} | ||||||
|
|
||||||
| write/2: #{cps_to_us(speed_results.write_cps)} µs/call | ||||||
| read/1: #{cps_to_us(speed_results.read_cps)} µs/call | ||||||
| write_one/3: #{cps_to_us(speed_results.write_one_cps)} µs/call | ||||||
| read_one/2: #{cps_to_us(speed_results.read_one_cps)} µs/call | ||||||
|
|
||||||
| """, | ||||||
| if(check_connections?, | ||||||
|
|
@@ -81,6 +91,9 @@ defmodule Circuits.GPIO.Diagnostics do | |||||
| passed | ||||||
| end | ||||||
|
|
||||||
| # Truncate sub-nanosecond for readability | ||||||
| defp cps_to_us(cps), do: :erlang.float_to_binary(1_000_000 / cps, decimals: 3) | ||||||
|
|
||||||
| defp pass_text({name, :ok}), do: [name, ": ", :green, "PASSED", :reset, "\n"] | ||||||
|
|
||||||
| defp pass_text({name, {:error, reason}}), | ||||||
|
|
@@ -108,17 +121,29 @@ defmodule Circuits.GPIO.Diagnostics do | |||||
| @doc """ | ||||||
| Run GPIO API performance tests | ||||||
|
|
||||||
| Disclaimer: There should be a better way than relying on the Circuits.GPIO | ||||||
| write performance on nearly every device. Write performance shouldn't be | ||||||
| terrible, though. | ||||||
| If you get warnings about the CPU speed, run | ||||||
| `Circuits.GPIO.Diagnostics.CPU.force_slowest/0` or | ||||||
| `Circuits.GPIO.Diagnostics.CPU.set_speed/1` to make sure that the CPU doesn't | ||||||
|
||||||
| `Circuits.GPIO.Diagnostics.CPU.set_speed/1` to make sure that the CPU doesn't | |
| `Circuits.GPIO.Diagnostics.CPU.set_frequency/1` to make sure that the CPU doesn't |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,154 @@ | ||||||||||||||||||||||||||||||||||
| # SPDX-FileCopyrightText: 2024 Frank Hunleth | ||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||
| # SPDX-License-Identifier: Apache-2.0 | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| defmodule Circuits.GPIO.Diagnostics.CPU do | ||||||||||||||||||||||||||||||||||
| @moduledoc """ | ||||||||||||||||||||||||||||||||||
| CPU | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
| CPU | |
| Utilities for inspecting and controlling CPU frequency scaling for diagnostics | |
| and benchmarking purposes. | |
| This module provides helpers to: | |
| * List available CPUs using the Linux `/sys/bus/cpu/devices` interface. | |
| * Force all CPUs to their slowest or fastest frequency using CPU scaling | |
| governors (e.g., `"powersave"` and `"performance"`). | |
| * Set all CPUs to a specific target frequency (in MHz) when the `userspace` | |
| scaling governor is available. | |
| These functions are intended for use on Linux systems where CPU frequency | |
| scaling is exposed via sysfs. They are useful for making benchmark runs and | |
| GPIO diagnostics more repeatable by putting the CPU in a known performance | |
| state. |
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic here is incorrect. Enum.all? will only return true if ALL CPUs have warnings. Since check_cpu_scheduler/1 returns true when a warning is detected, this should use Enum.any? instead to detect if ANY CPU has a warning condition.
| scheduler_warnings? = Enum.all?(cpus, &check_cpu_scheduler/1) | |
| scheduler_warnings? = Enum.any?(cpus, &check_cpu_scheduler/1) |
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic for detecting mismatched CPU speeds is flawed. It only compares the mean to the first CPU speed, which can miss cases where speeds vary but the mean happens to match the first element. For example, if CPU speeds are [1500, 1000, 2000], the mean is 1500 which matches the first element, so no warning would be issued even though speeds don't match. Consider checking if all speeds are equal by verifying that the maximum and minimum speeds differ by less than the tolerance, or by checking if all speeds equal the first speed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The comment "Truncate sub-nanosecond for readability" is misleading. The function converts calls per second to microseconds per call and formats with 3 decimal places, which means it shows values to the nearest 0.001 microseconds (1 nanosecond). A more accurate comment would be "Convert calls per second to microseconds per call with 3 decimal places for readability".