[libcamera-devel] [PATCH v4 10/12] utils: libtuning: generators: Add yaml output
Paul Elder
paul.elder at ideasonboard.com
Fri Nov 25 06:45:02 CET 2022
On Fri, Nov 25, 2022 at 03:15:05AM +0200, Laurent Pinchart wrote:
> Hi Paul,
>
> Thank you for the patch.
>
> On Thu, Nov 24, 2022 at 08:35:48PM +0900, Paul Elder wrote:
> > Add a generator to libtuning for writing tuning output to a yaml file.
> >
> > Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
> > Reviewed-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> >
> > ---
> > Changes in v4:
> > - explicitly set utf-8 encoding
> >
> > Changes in v3:
> > - add file description
> > - remove indirection from fake polymorphism
> >
> > New in 2
> > ---
> > utils/tuning/libtuning/generators/__init__.py | 1 +
> > .../libtuning/generators/yaml_output.py | 123 ++++++++++++++++++
> > 2 files changed, 124 insertions(+)
> > create mode 100644 utils/tuning/libtuning/generators/yaml_output.py
> >
> > diff --git a/utils/tuning/libtuning/generators/__init__.py b/utils/tuning/libtuning/generators/__init__.py
> > index 937aff30..f28b6149 100644
> > --- a/utils/tuning/libtuning/generators/__init__.py
> > +++ b/utils/tuning/libtuning/generators/__init__.py
> > @@ -3,3 +3,4 @@
> > # Copyright (C) 2022, Paul Elder <paul.elder at ideasonboard.com>
> >
> > from libtuning.generators.raspberrypi_output import RaspberryPiOutput
> > +from libtuning.generators.yaml_output import YamlOutput
> > diff --git a/utils/tuning/libtuning/generators/yaml_output.py b/utils/tuning/libtuning/generators/yaml_output.py
> > new file mode 100644
> > index 00000000..2449a093
> > --- /dev/null
> > +++ b/utils/tuning/libtuning/generators/yaml_output.py
> > @@ -0,0 +1,123 @@
> > +# SPDX-License-Identifier: GPL-2.0-or-later
> > +#
> > +# Copyright 2022 Paul Elder <paul.elder at ideasonboard.com>
> > +#
> > +# yaml_output.py - Generate tuning file in YAML format
> > +
> > +from .generator import Generator
> > +
> > +from numbers import Number
> > +from pathlib import Path
> > +
> > +import libtuning.utils as utils
> > +
> > +
> > +class YamlOutput(Generator):
> > + def __init__(self):
> > + super().__init__()
> > +
> > + def _stringify_number_list(self, listt: list):
> > + line_wrap = 80
> > +
> > + line = '[ ' + ', '.join([str(x) for x in listt]) + ' ]'
> > + if len(line) <= line_wrap:
> > + return [line]
> > +
> > + out_lines = ['[']
> > + line = ' '
> > + for x in listt:
> > + x_str = str(x)
> > + # If the first number is longer than line_wrap, it'll add an extra line
> > + if len(line) + len(x_str) > line_wrap:
> > + out_lines.append(line)
> > + line = f' {x_str},'
> > + continue
> > + line += f' {x_str},'
> > + out_lines.append(line)
> > + out_lines.append(']')
> > +
> > + return out_lines
> > +
> > + # @return Array of lines, and boolean of if all elements were numbers
> > + def _stringify_list(self, listt: list):
> > + out_lines = []
> > +
> > + all_numbers = set([isinstance(x, Number) for x in listt]).issubset({True})
> > +
> > + if all_numbers:
> > + return self._stringify_number_list(listt), True
> > +
> > + for value in listt:
> > + if isinstance(value, Number):
> > + out_lines.append(f'- {str(value)}')
> > + elif isinstance(value, str):
> > + out_lines.append(f'- "{value}"')
> > + elif isinstance(value, list):
> > + lines, all_numbers = self._stringify_list(value)
> > +
> > + if all_numbers:
> > + out_lines.append( f'- {lines[0]}')
> > + out_lines += [f' {line}' for line in lines[1:]]
> > + else:
> > + out_lines.append( f'-')
> > + out_lines += [f' {line}' for line in lines]
> > + elif isinstance(value, dict):
> > + lines = self._stringify_dict(value)
> > + out_lines.append( f'- {lines[0]}')
> > + out_lines += [f' {line}' for line in lines[1:]]
> > +
> > + return out_lines, False
> > +
> > + def _stringify_dict(self, dictt: dict):
> > + out_lines = []
> > +
> > + for key in dictt:
> > + value = dictt[key]
> > +
> > + if isinstance(value, Number):
> > + out_lines.append(f'{key}: {str(value)}')
> > + elif isinstance(value, str):
> > + out_lines.append(f'{key}: "{value}"')
> > + elif isinstance(value, list):
> > + lines, all_numbers = self._stringify_list(value)
> > +
> > + if all_numbers:
> > + out_lines.append( f'{key}: {lines[0]}')
> > + out_lines += [f'{" " * (len(key) + 2)}{line}' for line in lines[1:]]
> > + else:
> > + out_lines.append( f'{key}:')
> > + out_lines += [f' {line}' for line in lines]
> > + elif isinstance(value, dict):
> > + lines = self._stringify_dict(value)
> > + out_lines.append( f'{key}:')
> > + out_lines += [f' {line}' for line in lines]
> > +
> > + return out_lines
> > +
> > + def write(self, output_file: Path, output_dict: dict, output_order: list):
> > + out_lines = [
> > + '%YAML 1.1',
> > + '---',
> > + 'version: 1',
> > + # No need to condition this, as libtuning already guarantees that
> > + # we have at least one module. Even if the module has no output,
> > + # its prescence is sufficient.
> > + 'algorithms:'
> > + ]
> > +
> > + for module in output_order:
> > + out_lines.append(f' - {module.out_name}:')
> > +
> > + if len(output_dict[module]) == 0:
> > + continue
> > +
> > + if not isinstance(output_dict[module], dict):
> > + utils.eprint(f'Error: Output of {module.type} is not a dictionary')
> > + continue
> > +
> > + lines = self._stringify_dict(output_dict[module])
> > + out_lines += [f' {line}' for line in lines]
> > +
> > + with open(output_file, 'w', encoding='utf_8') as f:
>
> Interesting, I've always written it as 'UTF-8'. According to the table
> in https://docs.python.org/3.8/library/codecs.html#standard-encodings,
> 'utf_8' is the codec name, with spelling alternatives that differ only
> in case or use of a hyphen instead of an underscore being valid aliases.
> However,
>
> >>> codecs.lookup('utf_8').name
> 'utf-8'
Oops, yeah, I got it from the documentation. tfw the docs don't match
the implementation :(
I'll s/utf_8/utf-8/ when pushing.
Paul
> Consistency is great :-)
>
> > + for line in out_lines:
> > + f.write(f'{line}\n')
More information about the libcamera-devel
mailing list