﻿# Опухший C\+\+ код

Есть такая старая программистская байка, что нельзя платить программистам за строки кода, так как тогда они будут писать длинный бестолковый код и любить метод copy\-paste\. Будущее наступило\. Только теперь этими "программистами" является генеративный ИИ \(GenAI\), которому как раз платят за строки кода\. Иронично\.

![1402_vibetensor_ru/image1.png](https://import.viva64.com/docx/blog/1402_vibetensor_ru/image1.png)

Один из моих интересов — изучение сгенерированного С\+\+ кода, чтобы понимать, как развивается индустрия создания ПО, какие проблемы уходят, а какие наоборот возникают\. После заметки "[Дайте посмотреть на нормальный С\+\+ проект, созданный вайб\-кодингом](https://pvs-studio.ru/ru/blog/posts/cpp/1379/)" мне предложили заглянуть в проект [VibeTensor](https://github.com/NVlabs/vibetensor), что я и сделал\.


> VibeTensor: System Software for Deep Learning, Fully Generated by AI Agents

Я проверил его с помощью статического анализатора PVS\-Studio, а также посмотрел С\+\+ код глазами\. Было интересно узнать, как много ошибок в нём можно найти с помощью классического обзора кода и статического анализа\.

Так вот, у меня нет ответа на этот вопрос\. Непонятно, потому что главная проблема этого кода в том, что он ужасно раздут\. Это сильно мешает его обзору\. Мне тяжело продираться сквозь это болото, а вместе со мной "вязнет" и статический анализатор\.

Впрочем, ожидать большого количества ошибок здесь тоже не стоит: по\-настоящему полезного кода в этом проекте кот наплакал\. Как же так? Проект вроде не такой уж маленький\. Проанализированных C\+\+ файлов более 400, а количество строк кода — около 100,000\.

Да, но это только кажется, что там есть что смотреть и анализировать\. Повторюсь: основная проблема качества этого кода — его избыточность\. Она выражена как в постоянном повторении блоков кода, так и просто в бессмысленных лишних действиях, растягивающих код\.

Раньше бы сказали, что этот проект писался методом [copy\-paste](https://pvs-studio.ru/ru/blog/terms/0068/)\. В данном случае это не так, но генерация кода приводит ровно к таким же последствиям\. Вместо выноса обобщённой функциональности в функции, вновь и вновь генерируется код для решения схожих проблем\.

Можете полистать файлы, и через некоторое время вас начнёт преследовать дежавю, что вы вновь и вновь видите одни и те же блоки кода\. Они вроде как и разные, а вроде как и нет\. [Вот что я имею в виду](https://github.com/NVlabs/vibetensor/blob/fe85461faca02ba95d7bf1f8289002ff8cf91652/src/vbt/dispatch/dispatcher.cc#L760-L853):

![1402_vibetensor_ru/image3.png](https://import.viva64.com/docx/blog/1402_vibetensor_ru/image3.png)

Например, я уже писал в статье "[C\+\+: Пиши, сокращай, оптимизируй](https://pvs-studio.ru/ru/blog/posts/cpp/1395/)", что этот блок кода можно встретить 9 раз в разных тестах:

```cpp
const std::size_t nd = sizes.size();
std::vector<int64_t> strides(nd, 0);
int64_t acc = 1;
for (std::ptrdiff_t i = static_cast<std::ptrdiff_t>(nd) - 1; i >= 0; --i) {
  strides[static_cast<std::size_t>(i)] = acc;
  const auto sz = sizes[static_cast<std::size_t>(i)];
  acc *= (sz == 0 ? 1 : sz);
}

int64_t ne = 1;
bool any_zero = false;
for (auto s : sizes) {
  if (s == 0) {
    any_zero = true;
    break;
  }
  ne *= s;
}
if (any_zero) {
  ne = 0;
}
```

Однако это ещё не всё\. PVS\-Studio сыплет предупреждениями про постоянную избыточность\. Иногда это касается мелочей:

```cpp
for (int i = 0; i < dl.ndim; ++i) {
  int64_t n = (dl.ndim == 0) ? 1 : dl.shape[i];
  int64_t d = n > 0 ? (n - 1) : 0;
  if (d == 0) continue;
  int64_t st = (dl.ndim == 0) ? 1 : strides[static_cast<std::size_t>(i)];
```

PVS\-Studio дважды выдаёт V547 Expression 'dl\.ndim \=\= 0' is always false\. Действительно, если цикл выполняется, то `dl.ndim` не может быть равен нулю\. Код упрощается до:

```cpp
for (int i = 0; i < dl.ndim; ++i) {
  int64_t d = std::max(0ll, dl.shape[i] - 1);
  if (d == 0) continue;
  int64_t st = strides[i];
```

В других местах пухлость кода мелочью уже не назовёшь\. Там PVS\-Studio выдаёт сразу группы предупреждений:

* V547 \[CWE\-570\] Expression 'is\_empty' is always false\. tensor\_bindings\.cc 3007
* V547 \[CWE\-570\] Expression 'print\_size' is always false\. tensor\_bindings\.cc 3015
* V547 \[CWE\-571\] Expression '\!parts\.empty\(\)' is always true\. tensor\_bindings\.cc 3023

Код, на который выданы предупреждения, на первый взгляд умный, с массивом, с циклом\.\.\. А если присмотреться — лабуда\.

```cpp
bool is_empty = false; // handled above; always false here
bool print_size = is_empty && (self.sizes().size() != 1);
bool suppress_dtype_non_empty = (!is_empty) &&
  (self.dtype() == ScalarType::Float32 ||
   self.dtype() == ScalarType::Int64 ||
   self.dtype() == ScalarType::Bool);
bool print_dtype = !suppress_dtype_non_empty;
if (is_empty) {
  // For empty tensors, only print dtype when dtype != default float32
  print_dtype = (self.dtype() != ScalarType::Float32);
}

std::string out = "tensor(";
out += body;
std::vector<std::string> parts;
if (print_size) {
  parts.push_back(std::string("size=") + format_sizes(self.sizes()));
}
if (print_dtype) {
  parts.push_back(std::string("dtype=") + dtype_name(self.dtype()));
}
// Always include device suffix for CUDA tensors
parts.push_back(std::string("device='cuda:") +
                std::to_string((int)self.device().index) + "'");
if (!parts.empty()) {
  out += ", ";
  for (std::size_t i = 0; i < parts.size(); ++i) {
    if (i) out += ", ";
    out += parts[i];
  }
}
out += ")";
return out;
```

Как минимум, ручной цикл формирования сообщения можно сразу заменить на:

```cpp
return std::format("tensor({})", parts | std::views::join_with(", "sv));
```

Если присмотреться получше, то вообще всю эту избыточную фиговину можно сократить в три раза:

```cpp
std::string out = "tensor(" + body + ", ";

if (self.dtype() != ScalarType::Float32 &&
    self.dtype() != ScalarType::Int64 &&
    self.dtype() != ScalarType::Bool)
{
  out += std::string("dtype=") + dtype_name(self.dtype()) + ", ";
}
out += "device='cuda:" + std::to_string((int)self.device().index) + "')";
return out;
```

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

Когда же код схлопывается до своей сути, то понимаешь, что там и ошибаться\-то негде\. Он просто написан длиннее\. Не к чему тут относиться с почтением\.

Итого: нет в проекте никаких настоящих 100,000 строк С\+\+ кода\. Думаю, что если вынести дубликаты в функции и провести рефакторинг, количество кода сократится раз в 5\. Проект на 20,000 строк кода — это баловство\. Вот и вижу в нём не ошибки, а проблему раздутого кода и предупреждения анализатора про большое количество ложных/истинных условий и т\.п\.

Ну получается код длиннее, и что? Он и не предназначен для рефакторинга человеком\. Если надо — новый сгенерируем\.

Если хочется продать GenAI, а на судьбу проекта всё равно, то ради бога\. Если же вам нужен проект, то "плата за строки кода" куда выше, чем кажется\.

Следствия раздутого кода:

1. Больше строк кода — больше плата за их генерацию\.
1. Если Pull Requests ревьювит другой ИИ, то и ему больше плати\.
1. Раздутые функции — дороже генерация юнит\-тестов\.
1. Любая модификация кода с помощью ИИ дороже, так как требуется больше строк кода принять и отдать\.
1. Много контекста — больше вероятность ошибок при внесении изменений \(например, можно просто что\-то не исправить в одном из 100500 похожих мест\)\.
1. Если человеку самому придётся править код или искать баг — у него вытекут глаза\. Очень тяжело продираться сквозь нагромождение избыточных сущностей и конструкций\.
1. Когда код сложнее, чем нужно, компилятор будет хуже его оптимизировать\.
1. Проще сгенерировать ещё одну функцию, похожую на другие, чем найти и переработать уже существующие десятки однотипных функций\.
1. Лишние конструкции мешают не только человеку, но и статическому анализатору искать ошибки\.
1. Код медленнее компилируется\.
1. Излишнее "словоблудие" увеличивает вероятность столкнуться с неопределённым поведением, или что код будет работать не так, как задумывалось\.
1. Можете сами продолжить список\.

Под пунктом №11 я имел в виду, что если не понимаешь смысл слов, то не надо их использовать для красоты\. Использованный GenAI не знает суть `noexcept`, но считает, что с ним "красивее"\. Результат — множество мест в коде, где кидается исключение там, где его быть не должно:

```cpp
vt_status vt_tensor_iter_binary_cpu_host(const vt_iter_config* cfg,
                                         vt_tensor out_h,
                                         vt_tensor a_h,
                                         vt_tensor b_h,
                                         vt_tensor_iter_loop1d_fn loop,
                                         void* user_ctx) noexcept {

  ....
  if (effective.check_mem_overlap != VT_ITER_OVERLAP_DISABLE &&
      effective.check_mem_overlap != VT_ITER_OVERLAP_ENABLE) {
    throw std::invalid_argument(
        "vt_tensor_iter_binary_cpu: invalid vt_iter_overlap_mode");
    }
  ....
}
```

При этом проблема разбухшего кода — это ваша проблема, а не продавцов ИИ\. Вам платить за токены\.

Что можно сделать? Готовых решений предложить не могу\. Но, по крайней мере, предупреждён — значит вооружён\.

Я же всё больше склоняюсь к мнению, что нужно развить в статическом анализаторе PVS\-Studio направление по выявлению схожих фрагментов кода\. Тогда можно будет замкнуть GenAI и PVS\-Studio в петлю обратной связи\. Тогда код будет считается доделанным, если анализатор не только молчит про баги, но и нет попыток дублирования функциональности\.

Пока это ещё не роадмап развития PVS\-Studio, но уже вырисовывается картина новых бед, и как инструмент сможет помочь с ними справиться\.

**Дополнительные ссылки:**

1. [Давайте заглянем в этот самый вайб\-код](https://pvs-studio.ru/ru/blog/posts/cpp/1354/)\.
1. [Ревью вайб\-кода с гнильцой, который притворяется оптимизированным С\+\+ кодом](https://pvs-studio.ru/ru/blog/posts/cpp/1366/)\.
1. [Что скрывает код: от поверхности атаки до производительности\. Первый вебинар серии "Качество и безопасность ПО в эпоху GenAI"](https://pvs-studio.ru/ru/blog/video/11769/)\.