arduino / arduino-iot-cloud-py

Arduino IoT Cloud Python Client.
Mozilla Public License 2.0
29 stars 4 forks source link

misc: Optimize logging statements memory usage. #73

Closed iabdalkader closed 1 year ago

Bodobolero commented 1 year ago

Thanks for reducing the dynamic memory usage of logging.

@iabdalkader

I think it is not necessary to guard for static strings like https://github.com/arduino/arduino-iot-cloud-py/pull/73/files#diff-bacac9d865001a2293347f26dd66df12cdc58f0ad71654069e27afc44ce06c47L184

because static strings are in the read only memory segment anyway.

It only is necessary for f-strings which are concatenated dynamically based on parts like

https://github.com/arduino/arduino-iot-cloud-py/pull/73/files#diff-bacac9d865001a2293347f26dd66df12cdc58f0ad71654069e27afc44ce06c47R106

iabdalkader commented 1 year ago

because static strings are in the read only memory segment anyway.

I'm not entirely sure but I think that's only true if the module is frozen when running on MicroPython, but it's not normally frozen, it's loaded in memory and parsed, compiled etc.. but either way most of the strings are fstrings, at least the ones that get printed at every step, so the overhead is minimal.

Note in some cases a check is going to be done anyway, for example multiple debug statements, here an entire loop is skipped:

if log_enabled(logging.DEBUG):
    logging.debug("Pushing records to Arduino IoT cloud:")
    for record in self.senmlpack._data:
        logging.debug(f"  ==> record: {record.name} value: {str(record.value)[:48]}...")
Bodobolero commented 1 year ago

I'm not entirely sure but I think that's only true if the module is frozen when running on MicroPython, but it's not normally frozen

You are right - I was thinking of C/C++ Arduino code and string handling. I still need to remember myself that I am now using python.

iabdalkader commented 1 year ago

You are right - I was thinking of C/C++ Arduino code and string handling. I still need to remember myself that I am now using python.

For non-fstrings it's probably passed by a reference (pointer) to the string, it's maybe worth asking about, but since those are just a few lines that get executed once per connection, I will remove the check for them.

iabdalkader commented 1 year ago

@Bodobolero They do seem to consume memory:

import gc
import logging

logging.basicConfig(
    datefmt="%H:%M:%S",
    format="%(asctime)s.%(msecs)03d %(message)s",
    level=logging.INFO,
)

gc.collect()
logging.info("RTC time set from NTP.")
logging.info("RTC time set from NTP.")
logging.info("RTC time set from NTP.")
logging.info("RTC time set from NTP.")
mem = gc.mem_free()
gc.collect()
print(gc.mem_free() - mem)

Prints 1840 and it grows if you add more statements.

iabdalkader commented 1 year ago

No actually they don't if log level is say ERROR which I should have used, they don't consume any extra memory. So I will remove the check before static strings.

iabdalkader commented 1 year ago

Updated an merged.