Здравствуйте, Went, Вы писали:
W>Ну в этом же и суть, что в интерполяции нет разбора строки на этапе исполнения. То есть это ни в коем случае не сахар поверх format, а наиболее оптимальный из возможных способ сборки строки из базы и плейсхолдеров, потому что всё то, что можно, компилятор может вытянуть на этапе компиляции.
Что можно собрать на этапе компиляции? Только то, что фиксированного размера в выводе. Так что строки и числа не фиксированной длины отпадают. Например:
std::string str = std::format("a = {var} - ok");
особо оптимизировать не получится. Если var, скажем, int, то он может быть и 4 и 444. А значит в результирующую строку всё равно придётся складывать минимум из двух частей: "a = 444" и " — ok". То есть на каждую числовую переменную теоретически можно сэкономить одну операцию конкатенации строк. А если var — это строка, то сэкономить уже ничего не получится. Однако я сильно сомневаюсь, что кто-то будет заниматься такой мелочной оптимизацией.
W>std::format же принципиально другой подход, напротив, где оптимальность уступается в угоду гибкости (например, в разных локализациях плейсхолдеры могут идти в разном порядке). О чем же мы спорим?
Поправьте меня, если я ошибаюсь, но std::format первым параметром берёт форматную строку — объект, который компилятор может построить из строки на этапе компиляции.
Короче, можете объяснить: где вы предполагаете возможность оптимизации?
Здравствуйте, B0FEE664, Вы писали:
BFE>Короче, можете объяснить: где вы предполагаете возможность оптимизации?
Выражение $"Get {count} of {fruit}.".
По отношению к конкатенации ("Get " + std::to_string(count) + " of " + fruit + ".") — нет необходимости многократно перевыделять буферы и копировать.
По отношению к форматированию (std::format("Get {0} of {1}", count, fruit)) — нет необходимости парсить строку на этапе исполнения, нет необходимости создавать обобщенный механизм под произвольное количество аргументов-плейсхолдеров; все разворачивается в линейный код: сконвертили в строки все не строки (хотя для чисел можно взять максимум и конвертить уже in-place), оценили суммарный размер буфера, выделили один раз строку нужной длины, скопировали всё один раз.
Даже если сделать constexpr-версию std::format, где первый аргумент разбирается на этапе компиляции, то я очень сомневаюсь, что оптимизатор компилятора сможет превратить эту шаблонную магию в что-то соизмеримое с тем, что напишут разработчики компилятора.
Здравствуйте, Went, Вы писали:
W>Даже если сделать constexpr-версию std::format, где первый аргумент разбирается на этапе компиляции, то я очень сомневаюсь, что оптимизатор компилятора сможет превратить эту шаблонную магию в что-то соизмеримое с тем, что напишут разработчики компилятора.
сделать то можно. Та же библиотека fmt такое делает и работает ощутимо быстрее. В STL видимо других проблем хватает.
Хотя не исключаю, что std::format доведут до ума. Эта функциональность молодая ещё, подождём годков 10...
Здравствуйте, Hоmunculus, Вы писали:
BFE>>А зачем? BFE>>Чем это лучше:
H>Тем что форматипованную строку можно целиком сохранить, например для переводчиков, и потом целиком же единую строку и выводить, а не разбивать на кучу подстрок
Единственная проблема — std::format принимает строки времени компиляции, и не работает с рантайм форматными строками
Здравствуйте, Went, Вы писали:
W>Ну в этом же и суть, что в интерполяции нет разбора строки на этапе исполнения. То есть это ни в коем случае не сахар поверх format, а наиболее оптимальный из возможных способ сборки строки из базы и плейсхолдеров, потому что всё то, что можно, компилятор может вытянуть на этапе компиляции. std::format же принципиально другой подход, напротив, где оптимальность уступается в угоду гибкости (например, в разных локализациях плейсхолдеры могут идти в разном порядке). О чем же мы спорим?
Чем же подход std::format принципиально другой, когда он тоже на этапе компиляции работает?
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, Went, Вы писали:
W>>Ну в этом же и суть, что в интерполяции нет разбора строки на этапе исполнения. То есть это ни в коем случае не сахар поверх format, а наиболее оптимальный из возможных способ сборки строки из базы и плейсхолдеров, потому что всё то, что можно, компилятор может вытянуть на этапе компиляции. std::format же принципиально другой подход, напротив, где оптимальность уступается в угоду гибкости (например, в разных локализациях плейсхолдеры могут идти в разном порядке). О чем же мы спорим?
M>Чем же подход std::format принципиально другой, когда он тоже на этапе компиляции работает?
В неком пределе оптимальности шаблонного кода и качества оптимизатора, наверное, ничем (ну, кроме наглядности и очевидности синтаксиса, подсветки, качества ошибок, расширяемости и т.п.)
Здравствуйте, Went, Вы писали:
BFE>>Короче, можете объяснить: где вы предполагаете возможность оптимизации? W>Выражение $"Get {count} of {fruit}.". W>По отношению к конкатенации ("Get " + std::to_string(count) + " of " + fruit + ".") — нет необходимости многократно перевыделять буферы и копировать.
Ну ка же нет?
результат преобразования {count} в строку может иметь разную длину:
Get 1 of apple.
Get 22 of apple.
Значит копирование строки " of " в заранее выделенный буфер всё равно придётся делать.
{fruit} — строка переменной длины, значит заранее под неё заказать буфер не получится. Следовательно, так или иначе будет копирование строк "Get 1 of " и "apple" в некий третий буфер, который динамически придётся выделять. Да в этом третьем буфере можно заранее учесть завершающую точку и зарезервировать под неё место, но такого рода оптимизации выглядят сомнительно, особенно учитывая, что std::string хорошо оптимизированы для коротких строк и заранее выделяют память под возможное увеличение, ЕМНИП.
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, Went, Вы писали:
BFE>>>Короче, можете объяснить: где вы предполагаете возможность оптимизации? W>>Выражение $"Get {count} of {fruit}.". W>>По отношению к конкатенации ("Get " + std::to_string(count) + " of " + fruit + ".") — нет необходимости многократно перевыделять буферы и копировать.
BFE>Ну ка же нет? BFE>результат преобразования {count} в строку может иметь разную длину: BFE>Get 1 of apple. BFE>Get 22 of apple. BFE>Значит копирование строки " of " в заранее выделенный буфер всё равно придётся делать. BFE>{fruit} — строка переменной длины, значит заранее под неё заказать буфер не получится. Следовательно, так или иначе будет копирование строк "Get 1 of " и "apple" в некий третий буфер, который динамически придётся выделять. Да в этом третьем буфере можно заранее учесть завершающую точку и зарезервировать под неё место, но такого рода оптимизации выглядят сомнительно, особенно учитывая, что std::string хорошо оптимизированы для коротких строк и заранее выделяют память под возможное увеличение, ЕМНИП.
Я так понимаю. Интерполяция:
1. Приводим count к строке (1 выделение).
2. Суммируем длину строки count, длину строки fruit и длину строки подстановки.
3. Выделяем строку нужного размера (2 выделение).
4. Копируем в неё "Get ", строку count, " of ", строку fruit, "." (15 копирований).
Строго говоря, у нас 2 выделения и 15 копирований.
Теперь сложение.
1. Приводим count к строке (1 выделение).
2. Выделяем строку под "Get " и строку count. Копируем туда (2 выделения и 5 копирований).
3. Выделяем строку под "Get 1 of ", и копируем туда "Get 1" и " of " (уже 3 выделения и 5 + 5 + 4 = 14 копирований)
4. Аналогично для fruit (уже 4 выделения и 14 + 14 + 5 = 33 копирования)
5. Аналогично для "." (уже 5 выделений и 33 + 33 + 1 = 67 копирований).
То есть, у нас 5 выделений и 67 знаков скопировать.
Мог где-то ошибиться, но суть, я думаю, ясна. Если строка с оптимизацией для малых строк, выделений можно избежать, но разница в количестве копирований останется. Однако в реале, интерполяция на очень коротких строках используется редко, так что выделения будут. Кстати, я опустил запись терминального нуля. А с ним копирований будет еще больше (ведь программа не знает, что это промежуточный вариант и ноль можно не писать).
Если я где-то ошибаюсь, подскажите где именно.