Стоимость инференса: цена тысячи кадров на Python

Быстрая видеокарта может большую часть времени простаивать. Рассчитайте стоимость тысячи кадров с учётом батча, потока, количества устройств, амортизации и разных схем оплаты аренды.

Программа курса «Computer Vision Engineer: от детекции и Doc-AI до продакшна».

Стоимость тысячи кадров

Задание

Варианты железа сравнивают по одной величине — во что обходится обработка тысячи кадров. Считать её руками для каждой строки таблицы утомительно, поэтому расчёт оформляют функцией, которой на вход подают описание варианта.

Реализуйте функцию cost_per_1000(spec). Аргумент spec — словарь с описанием варианта. Общие ключи есть всегда:

  • frame_ms — время одного вызова engine в миллисекундах, дробное;
  • batch — сколько кадров обрабатывается за этот вызов, целое от единицы;
  • fps — поток, который надо обслужить, кадров в секунду, дробное;
  • utilization — до какой доли пропускной способности разрешено загружать одно устройство, дробное от нуля до единицы;
  • billing — как оплачивается железо: "own" или "rent".

У своего железа ("own") добавляются price — цена одного устройства в рублях, life_years — срок службы в годах, power_load и power_idle — мощность под нагрузкой и в простое в ваттах, kwh — цена киловатт-часа в рублях. У аренды ("rent") добавляются hour_rate — цена часа одного устройства в рублях и billed — что именно оплачивается: "wall" (все часы, пока устройство закреплено за камерой) или "compute" (только время вычислений).

Как считать

Время на один кадр — frame_ms / batch: вызов на батч из восьми кадров стоит дороже одиночного, но кадров в нём восемь. Отсюда пропускная способность одного устройства в кадрах в секунду: 1000 / время на кадр.

Устройств берётся столько, чтобы поток укладывался в их рабочую загрузку, а не в предел: одно устройство считается способным принять пропускная способность × utilization кадров в секунду. Поток ровно по этой величине укладывается в одно устройство, а меньше одного устройства не бывает. Тысяча кадров при потоке fps набирается за 1000 / fps секунд — это то время, за которое железу платят.

Дальше utilization нигде не участвует: доля времени под нагрузкой, от которой зависит электричество, считается по настоящей пропускной способности и по тому числу устройств, которое получилось.

Своё железо оплачивается за всё это время целиком, а не за время вычислений. Камера отдаёт кадры круглосуточно, машина рядом с ней включена круглосуточно, и амортизация с электричеством начисляются, даже когда загрузка — три процента. Амортизация: цена всех устройств, делённая на часы срока службы (life_years × 365 × 24) и помноженная на оплачиваемые часы. Электричество: каждое устройство занято счётом долю времени (fps / число устройств) / пропускная способность, остальное время потребляет мощность простоя, поэтому средняя мощность одного устройства — power_load × доля + power_idle × (1 − доля). Дальше мощность всех устройств переводится в киловатты, умножается на оплачиваемые часы и на kwh.

Аренда при billed = "wall" считается так же по времени: цена часа, помноженная на число устройств и на оплачиваемые часы. При billed = "compute" платят только за работу: тысяча кадров занимает 1000 × время на кадр миллисекунд, и эти миллисекунды переводятся в часы. Число вызовов при этом не округляется вверх, даже когда тысяча на размер батча нацело не делится. Разложить ту же работу по нескольким устройствам дешевле от этого не станет, так что число устройств на такую оплату не влияет.

Что вернуть. Кортеж (стоимость, статьи, число устройств): стоимость тысячи кадров в рублях, словарь статей и целое число устройств. У своего железа статьи — amortization и power, у аренды одна статья rent. Сумма статей равна стоимости. Округлять ничего не нужно, проверка сравнивает с допуском.

Пример

spec = {
    "billing": "own",
    "frame_ms": 3.39,
    "batch": 1,
    "fps": 1.0,
    "utilization": 0.7,
    "price": 180000.0,
    "life_years": 3,
    "power_load": 204.0,
    "power_idle": 107.0,
    "kwh": 11.0,
}
cost_per_1000(spec)
# (2.2305, {"amortization": 1.9026, "power": 0.3279}, 1)

Значения в комментарии округлены для показа. Карта считает кадр за 3,39 мс и тянет 295 кадров в секунду, рабочих 206, так что на поток в один кадр в секунду хватает одного устройства с загрузкой в треть процента. Тысяча кадров при таком потоке набирается 1000 секунд, и все эти секунды оплачены: из 2,23 ₽ на амортизацию простаивающей машины пришлось 1,90.

Примечание

Вход уже разобран и приходит одним словарём; ключи, относящиеся к выбранному способу оплаты, есть всегда. Менять словарь нельзя. Импорты пишите сами: из стандартной библиотеки здесь может пригодиться math. Вызывать функцию вручную не нужно, проверка запускает её сама.

Что проверяется

Код проверок скрыт, так что вот полный список того, что в них разбирается:

  • формат ответа: кортеж из дробной стоимости, словаря статей и целого числа устройств;
  • состав статей у своего железа и у аренды;
  • сумма статей равна возвращённой стоимости;
  • словарь spec после вызова не изменился;
  • пример из условия;
  • амортизация и электричество считаются за всё время потока, а не за время вычислений;
  • простой оплачивается по мощности простоя, а счёт — по мощности под нагрузкой;
  • батч: время на кадр делится на размер батча;
  • поток ровно по рабочей загрузке одного устройства;
  • поток чуть выше рабочей загрузки и поток, которому нужно несколько устройств;
  • загрузка, равная единице: запас не закладывается;
  • рост числа устройств в амортизации, в электричестве и в почасовой аренде;
  • доля времени под нагрузкой у каждого устройства, когда устройств несколько;
  • аренда по времени вычислений не зависит ни от потока, ни от числа устройств;
  • аренда по всем часам при том же потоке дороже аренды по вычислениям;
  • очень редкий и очень плотный поток на одном и том же железе.
РешениеPython

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Связанные задачи