Skip to content

Performance Testing #42

@vickash

Description

@vickash

I won't pretend I have any idea how the mruby VM works, but I got curious about how well it runs on the ESP32, and did some black-box testing.

Board: DOIT ESP32 Devkit V1
Clock: 240 MHz
Testing: Oscilloscope, double checked with time after loop - time before loop calculation.
Notes: mruby task pinned to Core 1, Core 1 watchdog timer disabled, so it doesn't crash.

Test 1: Class Methods

First up is a minimal project using the following code:

Results

# Setup for all loops
led = ESP32::GPIO_NUM_2
ESP32::GPIO::pinMode(led, ESP32::GPIO_MODE_OUTPUT)

# 53 kHz
loop {
  ESP32::GPIO::digital_write(2, 1)
  ESP32::GPIO::digital_write(2, 0)
}

# 42 kHz
loop {
  ESP32::GPIO::digital_write(led, ESP32::HIGH)
  ESP32::GPIO::digital_write(led, ESP32::LOW)
}

# 35 kHz
loop {
  ESP32::GPIO::digital_write(ESP32::GPIO_NUM_2, ESP32::HIGH)
  ESP32::GPIO::digital_write(ESP32::GPIO_NUM_2, ESP32::LOW)
}

Test 2: Instance Methods

Next up is the esp32-2mb folder from my project. This includes many more gems, but uses the exact C source from mruby-system and mruby-gpio, and I'm using the same mruby.

The one big difference is, instead of ESP32::GPIO::digital_write, I mapped the C function to an instance method, Board#digital_write. $board is an automatically created global instance.

Results

# Setup
include Denko
led = DigitalIO::Output.new(pin: 2)

# 67 kHz
# Literal values are fastest.
# Instance methods appear faster than Class/Module methods.
loop do
  $board.digital_write(2, 1)
  $board.digital_write(2, 0)
end

# 66 kHz
# Still fast with a local variable.
pin = 2
loop do
  $board.digital_write(pin, 1)
  $board.digital_write(pin, 0)
end

# 50 kHz
# led.pin is a getter for led@pin instance var.
loop do
  $board.digital_write(led.pin, 1)
  $board.digital_write(led.pin, 0)
end

# 40 kHz
# #digital_write also saves given value to led@state. Extra overhead.
loop do
  led.digital_write(1)
  led.digital_write(0)
end

# 32 kHz
# #on and #off just do the two lines in the above example.
loop do
  led.on
  led.off
end

# 32 kHz
# Aliases #on and #off. Aliasing looks like it's free.
loop do
  led.high
  led.low
end

# 18 kHz
# This LOOKS like it should be faster. It's not passing through led, not setting @state.
# But reading HIGH and LOW from Board are enough to make it slow.
# There are a lot of constants defined in Board. Maybe more makes it slower?
loop do
  $board.digital_write(2, Board::HIGH)
  $board.digital_write(2, Board::LOW)
end

# 16.5 kHz
# Even worse using reader method for pin number.
loop do
  $board.digital_write(led.pin, Board::HIGH)
  $board.digital_write(led.pin, Board::LOW)
end

# 16 kHz
# Worse again using reader methods for HIGH and LOW.
loop do
  $board.digital_write(led.pin, $board.high)
  $board.digital_write(led.pin, $board.low)
end

Observations

In decreasing order of speed for getting a value:

  • Literal value
  • Local variable with literal value
  • Instance method that just returns a literal value from instance var
  • Constant defined in Class
  • Instance method that gets constant from its Class

It also looks like instance methods are a bit faster than Class/Module methods, comparing first example in each test.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions