Так сколько на самом деле строк на C нужно, чтобы выполнить a + b в Python?

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Это ответ на статью «Сколько строк на C нужно, чтобы выполнить a + b в Python?» где количество строк так и не указано. Сразу оговорюсь покрывать все сценарии для python по количеству строк я не собираюсь — слишком много вариантов, слишком лень. Но вот вопрос, сколько уйдет питона нужно строк, что бы сложить 2 числа? Будет ли разница сохранять эти числа в переменные или разницы никакой?

Постановка задачи

Я решил что буду сравнивать разницу не в интерактивном режиме, а при чтении из файла. Потому что в это случае, можно избавиться от лишних процедур ввода вывода для интерактивного режима. Соответственно питон будет исполнять файл. И вот будем сравнивать разницу между выполнением пустого файла и файла где это самое сложение надо будет произвести. И так определимся с тем что нам для этого нужно:

  • узнать сколько строк питону нужно вообще, что бы исполнить пустой файл и выйти без ошибок

  • узнать сколько строк питону нужно, что бы исполнить файл где надо сложить два числа не сохраняя никуда результат

  • узнать сколько строк питону нужно вообще, что бы исполнить файл где идет сложение двух переменных содержащих числа

Содержимое файлов для сравнения.

Сравнивать будем пустой файл с двумя случаями

4+5

и

a=4
b=5
a+b

Исходный код пустого файла выставлять не буду =)

Что для этого нужно?

Собрать дебажный питон, в котором будет включен code coverage, после чего запустить генерацию отчета. Я очень хотел сделать это всё на Windows, но покрытие кода доступно только в версии Visual Studio Enterprise, а у меня Community Edition. Так что время устанавливать виртуалку с Linux (выбрал Ubuntu 22.04 LTS, потому что не люблю работать только из консоли). Дальше пошаговая инструкция как это сделать. Если интересны только результаты, можно смело переходить в конец статьи.

Как получить билд.

Идем на официальную страницу, как нам собрать билд сюда. Находим нужную нам операционную систему и идём по шагам. Ставим зависимости:

vim /etc/apt/sources.list
// вставляем в конец файла
deb-src http://archive.ubuntu.com/ubuntu/ jammy main
//сохраняемся.
sudo apt-get update
sudo apt-get build-dep python3
sudo apt-get install pkg-config
sudo apt-get install git

//вот это уже не обазательно, но уберем лишние варнинги при сборке,
//что питону чего то не хватает
sudo apt-get install build-essential gdb lcov pkg-config \
libbz2-dev libffi-dev libgdbm-dev libgdbm-compat-dev liblzma-dev \
libncurses5-dev libreadline6-dev libsqlite3-dev libssl-dev \
lzma lzma-dev tk-dev uuid-dev zlib1g-dev

Ок, теперь можно качать питон.

git clone https://github.com/python/cpython
cd cpython

Нам нужен дебажный питон, соответственно:

./configure --with-pydebug
make -j8
// где 8 количество ядер, которые нам нужны для компиляции, по умолчанию будет 2

Итак сборка прошла успешно и в корне появился волшебный файлик "python". Запустим и проверим, что мы загружаемся и выходим без ошибок, а ещё что 4+5 всё еще 9.Теперь надо собрать сборку с code coverage. По логу сборки видим, что python собирается в gcc с ключом оптимизации "-Og", не смотря на нашу конфигурацию с --with-pydebug (она конечно никакая, но лучше совсем без неё, иначе репорт станет ещё менее точным. Открываем configure в текстовом редакторе и смотри, где выставляется флажок "-Og". В моем случае это:

PYDEBUG_CFLAGS="-O0"
if test "x$ac_cv_cc_supports_og" = xyes
then :
  PYDEBUG_CFLAGS="-Og"
fi

Убирем -Og, оставлем кавычки пустыми.

Ключ для gcc "--coverage" включает code coverage. Что бы эти ключи gcc увидел во время сборки, понадобится установить два environment variable(переменные окружения). Об это написано в справке скрипта configure:

./configure --help
// вот эти строчки
CFLAGS      C compiler flags
LDFLAGS     linker flags, e.g. -L<lib dir> if you have libraries in a
            nonstandard directory <lib dir>

На Linux это:

export СFLAGS="--coverage"
export LDFLAGS="--coverage"

env
// проверяем что они поставились

Запускаем configure и make, как уже один раз сделали. Запускаем питон, и проверяем, что всё работет и ничего не крешится. Если всё прошло правильно, то у нас должено быть в папке куча файлов с раширением gcno и gcda.
Проверяем.

find . | grep gcda
find . | grep gcno

Файлики gcno содержат статическую информацию. А вот gcda указывают, какие строки во время вызова были пройдены. Соотвественно, перед каждым отчетом и запуском питона в с тремя разными скриптами их было бы не плохо удалить.Делается это так.

find . |grep gcda |xargs rm

Теперь gcda файлов нет и можно запускать наши файлы.

Генерируем отчет о покрытии

Итак, перед каждым отчетом, надо избавлятся от gcda файлов.Первый прогон идет просто с пустым файлом:

./python empty.py

mkdir report_empty
gcovr --html-details report_empty/index.html
// генерируем отчет

Удаляем gcda файлы, и повторяем процедуру по кругу с другими папками для отчетов и файлами.
Типичный отчёт выглядит вот так:

Итоги

Открываем index.html из каждой папочки с отчетами и видим сколько строк было запущено из каждого прогона. Мои результаты следующие.

Пустой файл:

Exec

Total

Coverage

Lines:

33241

253928

13.1%

Branches:

15536

190265

8.2%

4+5


Exec

Total

Coverage

Lines:

34261

253928

13.5%

Branches:

16136

190265

8.5%

a=4
b=5
a+b

Exec

Total

Coverage

Lines:

34724

253928

13.7%

Branches:

16483

190265

8.7%

Осталось только сделать операцию минус и получаем что:

  • 4+5 стоит 1020 строк

  • сохранение в переменную стоит 1480 строк

Послесловие

Что говорят эти цифры, много это или мало? Я бы сказал это нормально, это питон. Если два вас это дорого, то есть молотилки чисел оптом, типа scipy или numpy. Там такие процедурки могут стоить один такт процессора.
Да и вообще, результаты эксперимента к реальной жизни ничего общего не имеют. Потому что, это всё собрано без оптимизации. С оптимизацией строк, конечно, меньше не станет, а вот ассемблер может очень прилично сбросить в весе.

Более того, я рассматривал только сложение обычных целых чисел. Сложение классов уже будет стоить по другому, причем для каждого класса по разному. Да и каждая строка в программирование имеют разную цену и сложность. Потому куда корректнее было бы сравнивать сколько ассемблерных инструкций бы ушло на это. Но беда в том, что в зависимости от платформы, ключей компиляции типа и размера складываемых данных а так же положения луны и игривости настроения меркурия результат может варьировать и очень сильно. Я уже молчу о том, что числодробилки, вообще могут и используют совсем другой ассемблер.

Источник: https://habr.com/ru/articles/782034/


Интересные статьи

Интересные статьи

У всех компаний есть разнообразные данные: о клиентах, транзакциях, закупках, оборудовании, доходах и расходах. Но для одних компаний данные – драйвер роста, а другие несут убытки, полагаясь на них. Р...
Привет! Меня зовут Иван Аксенов, я Ruby-разработчик в компании Домклик. Расскажу о своём подходе к анализу причин выпуска неудачных релизов.Человек склонен совершать ошибки в любой деятельности. Иногд...
Библиотека hsdis удобна для преобразования листинга машинного кода, создаваемого JIT-компилятором, в читаемый ассемблерный код. Просмотр ассемблерных инструкций может пригодиться для проверки и отладк...
Привет! Я Антон Антонов, Full Stack Developer из Plarium Krasnodar. Расскажу, что помогает мне исправлять ошибки в коде.
Сейчас активно развиваются голосовые помощники и прочий звуковой дизайн. Компании подбирают тон голоса для помощников, делают их даже несколько, чтобы каждый нашел себе п...