[{"id":25753,"web_url":"https://patchwork.libcamera.org/comment/25753/","msgid":"<Y2uFBVVHYpcYn0sx@pendragon.ideasonboard.com>","date":"2022-11-09T10:46:29","subject":"Re: [libcamera-devel] [PATCH v2 08/11] utils: libtuning:\n\tgenerators: Add yaml output","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nThank you for the patch.\n\nOn Sat, Oct 22, 2022 at 03:23:07PM +0900, Paul Elder via libcamera-devel wrote:\n> Add a generator to libtuning for writing tuning output to a yaml file.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> \n> ---\n> New in 2\n> ---\n>  utils/tuning/libtuning/generators/__init__.py |   1 +\n>  .../libtuning/generators/yaml_output.py       | 121 ++++++++++++++++++\n>  2 files changed, 122 insertions(+)\n>  create mode 100644 utils/tuning/libtuning/generators/yaml_output.py\n> \n> diff --git a/utils/tuning/libtuning/generators/__init__.py b/utils/tuning/libtuning/generators/__init__.py\n> index 937aff30..f28b6149 100644\n> --- a/utils/tuning/libtuning/generators/__init__.py\n> +++ b/utils/tuning/libtuning/generators/__init__.py\n> @@ -3,3 +3,4 @@\n>  # Copyright (C) 2022, Paul Elder <paul.elder@ideasonboard.com>\n>  \n>  from libtuning.generators.raspberrypi_output import RaspberryPiOutput\n> +from libtuning.generators.yaml_output import YamlOutput\n> diff --git a/utils/tuning/libtuning/generators/yaml_output.py b/utils/tuning/libtuning/generators/yaml_output.py\n> new file mode 100644\n> index 00000000..b5e600f1\n> --- /dev/null\n> +++ b/utils/tuning/libtuning/generators/yaml_output.py\n> @@ -0,0 +1,121 @@\n> +# SPDX-License-Identifier: GPL-2.0-or-later\n> +#\n> +# Copyright 2022 Paul Elder <paul.elder@ideasonboard.com>\n> +\n> +from .generator import Generator\n> +\n> +from numbers import Number\n> +from pathlib import Path\n> +\n> +import libtuning.utils as utils\n> +\n> +\n> +class YamlOutput(Generator):\n> +    def __init__(self):\n> +        super().__init__()\n> +\n> +    def _stringify_number_list(self, listt: list):\n> +        line_wrap = 80\n> +\n> +        line = '[ ' + ', '.join([str(x) for x in listt]) + ' ]'\n> +        if len(line) <= line_wrap:\n> +            return [line]\n> +\n> +        out_lines = ['[']\n> +        line = ' '\n> +        for x in listt:\n> +            x_str = str(x)\n> +            # If the first number is longer than line_wrap, it'll add an extra line\n> +            if len(line) + len(x_str) > line_wrap:\n> +                out_lines.append(line)\n> +                line = f'  {x_str},'\n> +                continue\n> +            line += f' {x_str},'\n> +        out_lines.append(line)\n> +        out_lines.append(']')\n> +\n> +        return out_lines\n> +\n> +    # @return Array of lines, and boolean of if all elements were numbers\n> +    def _stringify_list(self, listt: list):\n> +        out_lines = []\n> +\n> +        all_numbers = set([isinstance(x, Number) for x in listt]).issubset({True})\n> +\n> +        if all_numbers:\n> +            return self._stringify_number_list(listt), True\n> +\n> +        for value in listt:\n> +            if isinstance(value, Number):\n> +                out_lines.append(f'- {str(value)}')\n> +            elif isinstance(value, str):\n> +                out_lines.append(f'- \"{value}\"')\n> +            elif isinstance(value, list):\n> +                lines, all_numbers = self._stringify_list(value)\n> +\n> +                if all_numbers:\n> +                    out_lines.append( f'- {lines[0]}')\n> +                    out_lines +=     [f'  {line}' for line in lines[1:]]\n> +                else:\n> +                    out_lines.append( f'-')\n> +                    out_lines += [f'  {line}' for line in lines]\n> +            elif isinstance(value, dict):\n> +                lines = self._stringify_dict(value)\n> +                out_lines.append( f'- {lines[0]}')\n> +                out_lines +=     [f'  {line}' for line in lines[1:]]\n> +\n> +        return out_lines, False\n> +\n> +    def _stringify_dict(self, dictt: dict):\n> +        out_lines = []\n> +\n> +        for key in dictt:\n> +            value = dictt[key]\n> +\n> +            if isinstance(value, Number):\n> +                out_lines.append(f'{key}: {str(value)}')\n> +            elif isinstance(value, str):\n> +                out_lines.append(f'{key}: \"{value}\"')\n> +            elif isinstance(value, list):\n> +                lines, all_numbers = self._stringify_list(value)\n> +\n> +                if all_numbers:\n> +                    out_lines.append( f'{key}: {lines[0]}')\n> +                    out_lines +=     [f'{\" \" * (len(key) + 2)}{line}' for line in lines[1:]]\n> +                else:\n> +                    out_lines.append( f'{key}:')\n> +                    out_lines +=     [f'  {line}' for line in lines]\n> +            elif isinstance(value, dict):\n> +                lines = self._stringify_dict(value)\n> +                out_lines.append( f'{key}:')\n> +                out_lines +=     [f'  {line}' for line in lines]\n> +\n> +        return out_lines\n> +\n> +    def _write(self, output_file: Path, output_dict: dict, output_order: list):\n> +        out_lines = [\n> +            '%YAML 1.1',\n> +            '---',\n> +            'version: 1',\n> +            # No need to condition this, as libtuning already guarantees that\n> +            # we have at least one module. Even if the module has no output,\n> +            # its prescence is sufficient.\n> +            'algorithms:'\n> +        ]\n> +\n> +        for module in output_order:\n> +            out_lines.append(f'  - {module.out_name}:')\n> +\n> +            if len(output_dict[module]) == 0:\n> +                continue\n> +\n> +            if not isinstance(output_dict[module], dict):\n> +                utils.eprint(f'Error: Output of {module.type} is not a dictionary')\n> +                continue\n> +\n> +            lines = self._stringify_dict(output_dict[module])\n> +            out_lines += [f'      {line}' for line in lines]\n> +\n> +        with open(output_file, 'w') as f:\n> +            for line in out_lines:\n> +                f.write(f'{line}\\n')\n\nCould we use the libyaml generator instead of reinventing the wheel ?","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id B601ABE08B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  9 Nov 2022 10:46:51 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 3930F63084;\n\tWed,  9 Nov 2022 11:46:51 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 780E161F3F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  9 Nov 2022 11:46:50 +0100 (CET)","from pendragon.ideasonboard.com\n\t(117.145-247-81.adsl-dyn.isp.belgacom.be [81.247.145.117])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 7256C896;\n\tWed,  9 Nov 2022 11:46:49 +0100 (CET)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1667990811;\n\tbh=M1QieQsAHlh1tTD9Y0Curz0IYzLkkLe273T8Q5dLrg8=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=u7XcvkvAVu7D99708U9ucBVhuMVnnv+N2ixcoLmI3N7Aw1ZqCAq9xHR80dFMP0XBc\n\trl7hQRnBHGO0p3k2TbA9SRn5oBpby0mJyr4tr5TVUeYr256okWaPcaC7bq84aJGeYA\n\tPMHwvK2ypbqK/b6a6xPC0pa1On5W8oHi7HePMAZ6/S6ysL3YyhKSkLbsUJPgsFdW1h\n\tNwjqEqMihjjIX9WrSN6PSR5hBWw7bzOek1hHYE8yQ+qEUIz00sbg7u5WL7SV7ewKFA\n\tlZsqQx6RXky05Z+r4Ds8sltYRS5FF3xrUpKVqGCuJn7htrGrMkLEsR0IVSyijlSe4b\n\tX9LgI08v4Nnxw==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1667990810;\n\tbh=M1QieQsAHlh1tTD9Y0Curz0IYzLkkLe273T8Q5dLrg8=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=EV+96fDUWaqT/lLayfjQsT7I+HLa4U4l1Hb7VxkNGyWicVxwE4hkfnP3DnVtE+UaJ\n\tMEoQhgBT0clc47i6HMtkiBglq3Hn8CX4H7c8TFpgqhGC5MXmyGvn64nf88rNc4XQr2\n\twKfNmGd6KoH+g3TiCNbdJrJJzQuCPv/bDL6Mz01Y="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"EV+96fDU\"; dkim-atps=neutral","Date":"Wed, 9 Nov 2022 12:46:29 +0200","To":"Paul Elder <paul.elder@ideasonboard.com>","Message-ID":"<Y2uFBVVHYpcYn0sx@pendragon.ideasonboard.com>","References":"<20221022062310.2545463-1-paul.elder@ideasonboard.com>\n\t<20221022062310.2545463-9-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20221022062310.2545463-9-paul.elder@ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v2 08/11] utils: libtuning:\n\tgenerators: Add yaml output","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":25761,"web_url":"https://patchwork.libcamera.org/comment/25761/","msgid":"<Y2yWe4/FmP2G57EN@pyrite.rasen.tech>","date":"2022-11-10T06:13:15","subject":"Re: [libcamera-devel] [PATCH v2 08/11] utils: libtuning:\n\tgenerators: Add yaml output","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"On Wed, Nov 09, 2022 at 12:46:29PM +0200, Laurent Pinchart wrote:\n> Hi Paul,\n> \n> Thank you for the patch.\n> \n> On Sat, Oct 22, 2022 at 03:23:07PM +0900, Paul Elder via libcamera-devel wrote:\n> > Add a generator to libtuning for writing tuning output to a yaml file.\n> > \n> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> > \n> > ---\n> > New in 2\n> > ---\n> >  utils/tuning/libtuning/generators/__init__.py |   1 +\n> >  .../libtuning/generators/yaml_output.py       | 121 ++++++++++++++++++\n> >  2 files changed, 122 insertions(+)\n> >  create mode 100644 utils/tuning/libtuning/generators/yaml_output.py\n> > \n> > diff --git a/utils/tuning/libtuning/generators/__init__.py b/utils/tuning/libtuning/generators/__init__.py\n> > index 937aff30..f28b6149 100644\n> > --- a/utils/tuning/libtuning/generators/__init__.py\n> > +++ b/utils/tuning/libtuning/generators/__init__.py\n> > @@ -3,3 +3,4 @@\n> >  # Copyright (C) 2022, Paul Elder <paul.elder@ideasonboard.com>\n> >  \n> >  from libtuning.generators.raspberrypi_output import RaspberryPiOutput\n> > +from libtuning.generators.yaml_output import YamlOutput\n> > diff --git a/utils/tuning/libtuning/generators/yaml_output.py b/utils/tuning/libtuning/generators/yaml_output.py\n> > new file mode 100644\n> > index 00000000..b5e600f1\n> > --- /dev/null\n> > +++ b/utils/tuning/libtuning/generators/yaml_output.py\n> > @@ -0,0 +1,121 @@\n> > +# SPDX-License-Identifier: GPL-2.0-or-later\n> > +#\n> > +# Copyright 2022 Paul Elder <paul.elder@ideasonboard.com>\n> > +\n> > +from .generator import Generator\n> > +\n> > +from numbers import Number\n> > +from pathlib import Path\n> > +\n> > +import libtuning.utils as utils\n> > +\n> > +\n> > +class YamlOutput(Generator):\n> > +    def __init__(self):\n> > +        super().__init__()\n> > +\n> > +    def _stringify_number_list(self, listt: list):\n> > +        line_wrap = 80\n> > +\n> > +        line = '[ ' + ', '.join([str(x) for x in listt]) + ' ]'\n> > +        if len(line) <= line_wrap:\n> > +            return [line]\n> > +\n> > +        out_lines = ['[']\n> > +        line = ' '\n> > +        for x in listt:\n> > +            x_str = str(x)\n> > +            # If the first number is longer than line_wrap, it'll add an extra line\n> > +            if len(line) + len(x_str) > line_wrap:\n> > +                out_lines.append(line)\n> > +                line = f'  {x_str},'\n> > +                continue\n> > +            line += f' {x_str},'\n> > +        out_lines.append(line)\n> > +        out_lines.append(']')\n> > +\n> > +        return out_lines\n> > +\n> > +    # @return Array of lines, and boolean of if all elements were numbers\n> > +    def _stringify_list(self, listt: list):\n> > +        out_lines = []\n> > +\n> > +        all_numbers = set([isinstance(x, Number) for x in listt]).issubset({True})\n> > +\n> > +        if all_numbers:\n> > +            return self._stringify_number_list(listt), True\n> > +\n> > +        for value in listt:\n> > +            if isinstance(value, Number):\n> > +                out_lines.append(f'- {str(value)}')\n> > +            elif isinstance(value, str):\n> > +                out_lines.append(f'- \"{value}\"')\n> > +            elif isinstance(value, list):\n> > +                lines, all_numbers = self._stringify_list(value)\n> > +\n> > +                if all_numbers:\n> > +                    out_lines.append( f'- {lines[0]}')\n> > +                    out_lines +=     [f'  {line}' for line in lines[1:]]\n> > +                else:\n> > +                    out_lines.append( f'-')\n> > +                    out_lines += [f'  {line}' for line in lines]\n> > +            elif isinstance(value, dict):\n> > +                lines = self._stringify_dict(value)\n> > +                out_lines.append( f'- {lines[0]}')\n> > +                out_lines +=     [f'  {line}' for line in lines[1:]]\n> > +\n> > +        return out_lines, False\n> > +\n> > +    def _stringify_dict(self, dictt: dict):\n> > +        out_lines = []\n> > +\n> > +        for key in dictt:\n> > +            value = dictt[key]\n> > +\n> > +            if isinstance(value, Number):\n> > +                out_lines.append(f'{key}: {str(value)}')\n> > +            elif isinstance(value, str):\n> > +                out_lines.append(f'{key}: \"{value}\"')\n> > +            elif isinstance(value, list):\n> > +                lines, all_numbers = self._stringify_list(value)\n> > +\n> > +                if all_numbers:\n> > +                    out_lines.append( f'{key}: {lines[0]}')\n> > +                    out_lines +=     [f'{\" \" * (len(key) + 2)}{line}' for line in lines[1:]]\n> > +                else:\n> > +                    out_lines.append( f'{key}:')\n> > +                    out_lines +=     [f'  {line}' for line in lines]\n> > +            elif isinstance(value, dict):\n> > +                lines = self._stringify_dict(value)\n> > +                out_lines.append( f'{key}:')\n> > +                out_lines +=     [f'  {line}' for line in lines]\n> > +\n> > +        return out_lines\n> > +\n> > +    def _write(self, output_file: Path, output_dict: dict, output_order: list):\n> > +        out_lines = [\n> > +            '%YAML 1.1',\n> > +            '---',\n> > +            'version: 1',\n> > +            # No need to condition this, as libtuning already guarantees that\n> > +            # we have at least one module. Even if the module has no output,\n> > +            # its prescence is sufficient.\n> > +            'algorithms:'\n> > +        ]\n> > +\n> > +        for module in output_order:\n> > +            out_lines.append(f'  - {module.out_name}:')\n> > +\n> > +            if len(output_dict[module]) == 0:\n> > +                continue\n> > +\n> > +            if not isinstance(output_dict[module], dict):\n> > +                utils.eprint(f'Error: Output of {module.type} is not a dictionary')\n> > +                continue\n> > +\n> > +            lines = self._stringify_dict(output_dict[module])\n> > +            out_lines += [f'      {line}' for line in lines]\n> > +\n> > +        with open(output_file, 'w') as f:\n> > +            for line in out_lines:\n> > +                f.write(f'{line}\\n')\n> \n> Could we use the libyaml generator instead of reinventing the wheel ?\n\nThe libyaml generator doesn't output arrays in [] and instead puts them\nall on new lines with - prefix, so the lsc tables are like 289 lines\neach...\n\n\nPaul","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id A7E10BE08B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 10 Nov 2022 06:13:24 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 0A57063083;\n\tThu, 10 Nov 2022 07:13:24 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 9978C61F38\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 10 Nov 2022 07:13:22 +0100 (CET)","from pyrite.rasen.tech (h175-177-042-159.catv02.itscom.jp\n\t[175.177.42.159])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 03DAB499;\n\tThu, 10 Nov 2022 07:13:20 +0100 (CET)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1668060804;\n\tbh=5T3NEfZ7lOSsCcRIhpl14FznvlxR48n+WB+3rKRRJDw=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=IoKsfVhcuv+VIb8BsAGF1/XtMwrshkDjVR7xRE3b/cMFWXZjPNdOH3J84m/4F0MQa\n\t2wAxR8VtPDUkztynjciWqoeFKJXjjxcc4Cp1QSc8mhrr6GvkRcT6Ls01q8H0WubOuX\n\tQXt4tebTw6vv+x9cXusX+CxO28n+Sopmmc0fTTzDY6bVQre3729b6gMUTKJ+eQ8Zwg\n\t4OXB8X6u9FZIW5+H0qg0ruXWm7XANsFg2McMYlrPVl59iSfj40xPeC4u0NrDb14jZp\n\tBuWUhOac3qtKL6mmlxxTOLMCRK4lEZA06hsQ1y/4qnyxhD82F2TLb+QeG08OHmsI9k\n\tOH4TsspRKNGOA==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1668060802;\n\tbh=5T3NEfZ7lOSsCcRIhpl14FznvlxR48n+WB+3rKRRJDw=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=Fi2OyOvE2XfwBlwBtdyVBqdGUZmQvgJSjCLVrPrx2pmsWeyS1IJl0dEezLh3tLu6w\n\tgUeNnwGA+/G81Cic+uQJHs1TLSgcbCjrvwD1WXCed6vT+sB5gyxwcaXjF9dFk8wjis\n\t+sHMlLOHuwSqZEdfrt9tTk4MFonAxMWH01pB/7WI="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"Fi2OyOvE\"; dkim-atps=neutral","Date":"Thu, 10 Nov 2022 15:13:15 +0900","To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Message-ID":"<Y2yWe4/FmP2G57EN@pyrite.rasen.tech>","References":"<20221022062310.2545463-1-paul.elder@ideasonboard.com>\n\t<20221022062310.2545463-9-paul.elder@ideasonboard.com>\n\t<Y2uFBVVHYpcYn0sx@pendragon.ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=us-ascii","Content-Disposition":"inline","In-Reply-To":"<Y2uFBVVHYpcYn0sx@pendragon.ideasonboard.com>","Subject":"Re: [libcamera-devel] [PATCH v2 08/11] utils: libtuning:\n\tgenerators: Add yaml output","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Paul Elder via libcamera-devel <libcamera-devel@lists.libcamera.org>","Reply-To":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}},{"id":25853,"web_url":"https://patchwork.libcamera.org/comment/25853/","msgid":"<Y31jyfIw2rwGltvB@pendragon.ideasonboard.com>","date":"2022-11-23T00:05:29","subject":"Re: [libcamera-devel] [PATCH v2 08/11] utils: libtuning:\n\tgenerators: Add yaml output","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"Hi Paul,\n\nOn Thu, Nov 10, 2022 at 03:13:15PM +0900, Paul Elder wrote:\n> On Wed, Nov 09, 2022 at 12:46:29PM +0200, Laurent Pinchart wrote:\n> > On Sat, Oct 22, 2022 at 03:23:07PM +0900, Paul Elder via libcamera-devel wrote:\n> > > Add a generator to libtuning for writing tuning output to a yaml file.\n> > > \n> > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> > > \n> > > ---\n> > > New in 2\n> > > ---\n> > >  utils/tuning/libtuning/generators/__init__.py |   1 +\n> > >  .../libtuning/generators/yaml_output.py       | 121 ++++++++++++++++++\n> > >  2 files changed, 122 insertions(+)\n> > >  create mode 100644 utils/tuning/libtuning/generators/yaml_output.py\n> > > \n> > > diff --git a/utils/tuning/libtuning/generators/__init__.py b/utils/tuning/libtuning/generators/__init__.py\n> > > index 937aff30..f28b6149 100644\n> > > --- a/utils/tuning/libtuning/generators/__init__.py\n> > > +++ b/utils/tuning/libtuning/generators/__init__.py\n> > > @@ -3,3 +3,4 @@\n> > >  # Copyright (C) 2022, Paul Elder <paul.elder@ideasonboard.com>\n> > >  \n> > >  from libtuning.generators.raspberrypi_output import RaspberryPiOutput\n> > > +from libtuning.generators.yaml_output import YamlOutput\n> > > diff --git a/utils/tuning/libtuning/generators/yaml_output.py b/utils/tuning/libtuning/generators/yaml_output.py\n> > > new file mode 100644\n> > > index 00000000..b5e600f1\n> > > --- /dev/null\n> > > +++ b/utils/tuning/libtuning/generators/yaml_output.py\n> > > @@ -0,0 +1,121 @@\n> > > +# SPDX-License-Identifier: GPL-2.0-or-later\n> > > +#\n> > > +# Copyright 2022 Paul Elder <paul.elder@ideasonboard.com>\n> > > +\n> > > +from .generator import Generator\n> > > +\n> > > +from numbers import Number\n> > > +from pathlib import Path\n> > > +\n> > > +import libtuning.utils as utils\n> > > +\n> > > +\n> > > +class YamlOutput(Generator):\n> > > +    def __init__(self):\n> > > +        super().__init__()\n> > > +\n> > > +    def _stringify_number_list(self, listt: list):\n> > > +        line_wrap = 80\n> > > +\n> > > +        line = '[ ' + ', '.join([str(x) for x in listt]) + ' ]'\n> > > +        if len(line) <= line_wrap:\n> > > +            return [line]\n> > > +\n> > > +        out_lines = ['[']\n> > > +        line = ' '\n> > > +        for x in listt:\n> > > +            x_str = str(x)\n> > > +            # If the first number is longer than line_wrap, it'll add an extra line\n> > > +            if len(line) + len(x_str) > line_wrap:\n> > > +                out_lines.append(line)\n> > > +                line = f'  {x_str},'\n> > > +                continue\n> > > +            line += f' {x_str},'\n> > > +        out_lines.append(line)\n> > > +        out_lines.append(']')\n> > > +\n> > > +        return out_lines\n> > > +\n> > > +    # @return Array of lines, and boolean of if all elements were numbers\n> > > +    def _stringify_list(self, listt: list):\n> > > +        out_lines = []\n> > > +\n> > > +        all_numbers = set([isinstance(x, Number) for x in listt]).issubset({True})\n> > > +\n> > > +        if all_numbers:\n> > > +            return self._stringify_number_list(listt), True\n> > > +\n> > > +        for value in listt:\n> > > +            if isinstance(value, Number):\n> > > +                out_lines.append(f'- {str(value)}')\n> > > +            elif isinstance(value, str):\n> > > +                out_lines.append(f'- \"{value}\"')\n> > > +            elif isinstance(value, list):\n> > > +                lines, all_numbers = self._stringify_list(value)\n> > > +\n> > > +                if all_numbers:\n> > > +                    out_lines.append( f'- {lines[0]}')\n> > > +                    out_lines +=     [f'  {line}' for line in lines[1:]]\n> > > +                else:\n> > > +                    out_lines.append( f'-')\n> > > +                    out_lines += [f'  {line}' for line in lines]\n> > > +            elif isinstance(value, dict):\n> > > +                lines = self._stringify_dict(value)\n> > > +                out_lines.append( f'- {lines[0]}')\n> > > +                out_lines +=     [f'  {line}' for line in lines[1:]]\n> > > +\n> > > +        return out_lines, False\n> > > +\n> > > +    def _stringify_dict(self, dictt: dict):\n> > > +        out_lines = []\n> > > +\n> > > +        for key in dictt:\n> > > +            value = dictt[key]\n> > > +\n> > > +            if isinstance(value, Number):\n> > > +                out_lines.append(f'{key}: {str(value)}')\n> > > +            elif isinstance(value, str):\n> > > +                out_lines.append(f'{key}: \"{value}\"')\n> > > +            elif isinstance(value, list):\n> > > +                lines, all_numbers = self._stringify_list(value)\n> > > +\n> > > +                if all_numbers:\n> > > +                    out_lines.append( f'{key}: {lines[0]}')\n> > > +                    out_lines +=     [f'{\" \" * (len(key) + 2)}{line}' for line in lines[1:]]\n> > > +                else:\n> > > +                    out_lines.append( f'{key}:')\n> > > +                    out_lines +=     [f'  {line}' for line in lines]\n> > > +            elif isinstance(value, dict):\n> > > +                lines = self._stringify_dict(value)\n> > > +                out_lines.append( f'{key}:')\n> > > +                out_lines +=     [f'  {line}' for line in lines]\n> > > +\n> > > +        return out_lines\n> > > +\n> > > +    def _write(self, output_file: Path, output_dict: dict, output_order: list):\n> > > +        out_lines = [\n> > > +            '%YAML 1.1',\n> > > +            '---',\n> > > +            'version: 1',\n> > > +            # No need to condition this, as libtuning already guarantees that\n> > > +            # we have at least one module. Even if the module has no output,\n> > > +            # its prescence is sufficient.\n> > > +            'algorithms:'\n> > > +        ]\n> > > +\n> > > +        for module in output_order:\n> > > +            out_lines.append(f'  - {module.out_name}:')\n> > > +\n> > > +            if len(output_dict[module]) == 0:\n> > > +                continue\n> > > +\n> > > +            if not isinstance(output_dict[module], dict):\n> > > +                utils.eprint(f'Error: Output of {module.type} is not a dictionary')\n> > > +                continue\n> > > +\n> > > +            lines = self._stringify_dict(output_dict[module])\n> > > +            out_lines += [f'      {line}' for line in lines]\n> > > +\n> > > +        with open(output_file, 'w') as f:\n> > > +            for line in out_lines:\n> > > +                f.write(f'{line}\\n')\n> > \n> > Could we use the libyaml generator instead of reinventing the wheel ?\n> \n> The libyaml generator doesn't output arrays in [] and instead puts them\n> all on new lines with - prefix, so the lsc tables are like 289 lines\n> each...\n\nThis is controlled by the default_flow_style argument to yaml.dump(),\nbut it applies to all elements. There may be a way to have more control\nusing a custom dumper or representers, but I haven't investigated that.\nLet's keep this code for now, if you come across a way to use standard\nAPIs, feel free to give it a try.","headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id F3C31BDE6B\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed, 23 Nov 2022 00:05:49 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1247363314;\n\tWed, 23 Nov 2022 01:05:49 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id AB43061F2B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 23 Nov 2022 01:05:46 +0100 (CET)","from pendragon.ideasonboard.com (62-78-145-57.bb.dnainternet.fi\n\t[62.78.145.57])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id ED546890;\n\tWed, 23 Nov 2022 01:05:45 +0100 (CET)"],"DKIM-Signature":["v=1; a=rsa-sha256; c=relaxed/simple; d=libcamera.org;\n\ts=mail; t=1669161949;\n\tbh=XqpkuuhSvpmUSiVONNNULpm3dYXEkA+F421JnZrgPXs=;\n\th=Date:To:References:In-Reply-To:Subject:List-Id:List-Unsubscribe:\n\tList-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:\n\tFrom;\n\tb=0UErz0f6vX6cwX+3EUApJnYSvsicw/w8uXys1gpKq1+NWBrlFYKdIIfzCDOFwJvfV\n\tJ5QtFn9mcFuJw0nKsmJmJvKWQFYgCluvReTdbFCFRC/cKrhDuw8OE2GqTAytTgtJGv\n\tX8BnlSTjgN4ihIWeT9n9AFKsXewKtEs8Obn2cu19nqohcdPTJ6eKNVDjK5pHEIpoZv\n\ttg25QpTotfQuXFqCkzHiYv6pU2bItwG/0FXwyrw2Ak4eeU8+Ja0P50cM/K0C4qcplp\n\txiXxTgdxy21Mdw94Smk9fWMX6g08nX5R4ayu1kM36xNw7pXzRsRpQLJmClQMquwgly\n\tR1zRcz6PSzZIQ==","v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1669161946;\n\tbh=XqpkuuhSvpmUSiVONNNULpm3dYXEkA+F421JnZrgPXs=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=WvejVnuTuf9tAGazsBTUqGWIJ/7l9C0sw/acK8Dvf6AYwjIJ1hBj6obj/cSI4aBbP\n\tDlvY8+BWj1pOkXIIGC9pjtIuZKp8Fw0vZbyIJv2kMOWgpX0jMGQLYfKIPd/VnQitbz\n\t3Id4/dsldmqp5LoU0jp/+iGBMWB5I0ZbYd2M5aes="],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key; \n\tunprotected) header.d=ideasonboard.com\n\theader.i=@ideasonboard.com\n\theader.b=\"WvejVnuT\"; dkim-atps=neutral","Date":"Wed, 23 Nov 2022 02:05:29 +0200","To":"Paul Elder <paul.elder@ideasonboard.com>","Message-ID":"<Y31jyfIw2rwGltvB@pendragon.ideasonboard.com>","References":"<20221022062310.2545463-1-paul.elder@ideasonboard.com>\n\t<20221022062310.2545463-9-paul.elder@ideasonboard.com>\n\t<Y2uFBVVHYpcYn0sx@pendragon.ideasonboard.com>\n\t<Y2yWe4/FmP2G57EN@pyrite.rasen.tech>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<Y2yWe4/FmP2G57EN@pyrite.rasen.tech>","Subject":"Re: [libcamera-devel] [PATCH v2 08/11] utils: libtuning:\n\tgenerators: Add yaml output","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","From":"Laurent Pinchart via libcamera-devel\n\t<libcamera-devel@lists.libcamera.org>","Reply-To":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"}}]