среда, 29 ноября 2023 г.

[prog.c++.flame] Насколько эффективно реализована split_path в C++ REST SDK от Microsoft?

Для многих анонимных экспертов, громогласно и авторитетно заявляющих о том, что C++у не место в современном мире, может быть удивительно, но C++ применяется в Web-е не так уж и редко. Может и не напрямую в Web-е, но вот для отдачи JSON-ов и прочей дряни в RESTful-микросервисной архитектуре C++ используется регулярно. И C++ных фреймворков для этих целей, разной степени продвинутости, можно сходу назвать с полдюжины (а если подумать-повспоминать, то и еще больше).

Не мне анализировать причины этого, т.к. я, так уж получилось, лицо заинтересованное и мне выгодно, чтобы C++ применялся в такого рода проектах еще активнее.

Но, полагаю, одна из причин -- это эффективность.

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

Ну а раз эффективность -- это одна из причин использовать C++, то, полагаю, C++ные фреймворки для поддержки HTTP/REST, должны бы подходить к этому аспекту с тщанием, ведь так?

Ага, я тоже так думал.

Пока не довелось заглянуть в потроха C++ REST SDK от самого Microsoft.

В частности, в реализацию вспомогательной функции split_path.

Был, мягко говоря, удивлен:

std::vector<utility::string_t> uri::split_path(const utility::string_t& path)
{
    std::vector<utility::string_t> results;
    utility::istringstream_t iss(path);
    iss.imbue(std::locale::classic());
    utility::string_t s;

    while (std::getline(iss, s, _XPLATSTR('/')))
    {
        if (!s.empty())
        {
            results.push_back(s);
        }
    }

    return results;
}

Т.е. для того, чтобы уже имеющуюся строку разбить на фрагменты, мы эту самую строку сперва копирую внутрь объекта std::istringstream, потом используем std::getline для того, чтобы разделить ее в местах '/'.

Мне кажется, что у всех, кто хоть сколько-нибудь программировал на C++ и хоть что-то слышал про производительность std::istream-ов, должен был бы возникнуть вопрос WTF.

Между тем, реализация аналогичного split_path в лоб посредством простого цикла пишется буквально за 20 минут вместе с набором unit-тестов. У меня в рамках C++17 получилось вот так:

std::vector<std::string> split_path(std::string_view path)
{
   std::vector<std::string> result;
   while(!path.empty())
   {
      if(auto p = path.find('/'); p != std::string_view::npos)
      {
         auto fragment = path.substr(0u, p);
         path = path.substr(p + 1u);
         if(!fragment.empty())
            result.emplace_back(fragment);
      }
      else
      {
         result.emplace_back(path);
         path = std::string_view{};
      }
   }

   return result;
}

По общему количеству строк не сильно больше. По производительности -- преимущество раза в три:

Да, я понимаю, что string_view из C++17 -- это немного читерство. Но даже в рамках C++03 можно написать не менее эффективно, просто кода будет чуть побольше.


А теперь позвольте мне не сдерживаться, а побомбить.

У проекта больше 7k звезд на GitHub.

Проектом занималась большая корпорация, с поставленными процессами, тщательным отбором на входе (несколько раундов собеседований и т.д.).

А в коде вот такое.

Как так то?

Вот ей богу, если придется искать работу наемным программистом и потребуется пройти несколько стадий собеседований с online-кодингом, то вряд ли получится сдержаться и не послать куда подальше. Впрочем, на эту тему в блоге уже были соответствующие размышлизмы.

2 комментария:

Stanislav Mischenko комментирует...

А тем временем программист на Java просто пишет:
String[] parts = string.split("/")

;)

Stanislav Mischenko комментирует...

@еао197

Да, там будут пустые строки тоже.