Добивая тему про строчки(std::to_string без создания новой строки) внезапно обнаружил, что компилятор C++ от Microsoft(Microsoft (R) C/C++ Optimizing Compiler Version 19.14.26430 for x86
) генерирует бинарник, в котором std::to_string
быстрее snprintf
более чем в 3 раза.
Пробовал запускать тот же код на ideone.com, onlinegdb.com (т.к. расчитывал, что там стоит g++), где snprintf
оказалось шустрее примерно в 1.4 раза.
Я понимаю, что всё зависит от реализации стандартной библиотеки разными компиляторами, но такую пропасть я увидеть никак не ожидал. В чём загвоздка, кто посвящён в детали реализации stl от microsoft(или у меня в коде ошибка?) и объяснит совсем запутавшемуся.
Вот код:
#include <iostream>
#include <string>
#include <chrono>
using namespace std;
template<typename Callback>
float getElapsedTime(Callback cb) {
auto start = chrono::system_clock::now();
cb();
auto end = chrono::system_clock::now();
return chrono::duration_cast<chrono::milliseconds>(end - start).count() / 1000.f;
}
int main() {
ios::sync_with_stdio(false);
uint64_t start = 0;
uint64_t end = 10'000'000;
cout << "0 .. 10\'000\'000\n";
cout << "snprintf: " << getElapsedTime([&]() {
char buffer[1024] = {};
for (uint64_t i = start; i < end; ++i)
snprintf(buffer, 1024, "%llu", i);
}) << "s\n";
cout << "to_string: " << getElapsedTime([&]() {
for (uint64_t i = start; i < end; ++i) {
auto s = to_string(i);
}
}) << "s\n";
start = 999'999'999'995'000'000;
end = 1'000'000'000'010'000'000;
cout << "\n999\'999\'999\'995\'000\'000 .. 1\'000\'000\'000\'010\'000\'000\n";
cout << "snprintf: " << getElapsedTime([&]() {
char buffer[1024] = {};
for (uint64_t i = start; i < end; ++i)
snprintf(buffer, 1024, "%llu", i);
}) << "s\n";
cout << "to_string: " << getElapsedTime([&]() {
for (uint64_t i = start; i < end; ++i) {
auto s = to_string(i);
}
}) << "s\n";
cin.get();
}
Вывод программы, сгенерированной компилятором microsoft(на домашнем компьютере с Windows 10):
0 .. 10'000'000
snprintf: 2.103s
to_string: 0.26s
999'999'999'995'000'000 .. 1'000'000'000'010'000'000
snprintf: 5.26s
to_string: 2.115s
Вывод с ideone:
0 .. 10'000'000
snprintf: 0.747s
to_string: 0.746s
999'999'999'995'000'000 .. 1'000'000'000'010'000'000
snprintf: 1.389s
to_string: 1.884s
В GCC & Clang, вызов std::to_string
в конце концов вырождается в snprintf
, тогда как в MSVC (по крайней мере в 2017) идёт прямой вызов внутренней функции _Integral_to_string
. Эта функция выполняет обычный цикл, который делит число на 10 и составляет таким образом строку.
Учитывая тот факт, что snprintf
, вероятно, имеет схожую реализацию конвертирования числа в строку, получается, что std::to_string
будет быстрее т.к.:
snprintf
вызывается через call
.snprintf
вынужден каждый раз разбирать строку форматирования, чтобы понять, что за число (или числа) и как его преобразовывать.Именно второй пункт, скорее всего, и даёт такую разницу в производительности.
Не оправдывая VC++ :) - есть у меня подозрение, что, помимо платы за универсальность, дело еще и в том, что snprintf
вызывается, а to_string
встраивается. Покопайтесь там, у кого GCC под рукой - там snprintf
случайно не встраивается?...
Для интереса добавлен вот такой простейший код вместо snprintf
(да, да, я знаю, что для 0 он не сработает :))
void hands(char * buffer, uint64_t i)
{
char * c = buffer;
while(i) { *c++ = i%10 + '0'; i/=10; }
for(--c; c > buffer; ++buffer, --c)
{
char s = *c;
*c = *buffer;
*buffer = s;
}
}
Вызов встроился, и у меня результаты для этого варианта, snprintf
и to_string
соответственно около 730, 4830 и 1450 мс. Забавно, что никакие танцы с бубном - типа заставить s
каждый раз переаллоцировать память - особо на время работы to_string
не повлияли...
Заказал себе rfid-RC522 считыватель для ардуино, подключил к меге, подключил библиотеку(MFRC522), залил скетч, но он работает только с брелком и карточкой...
Собственно, вопрос в заголовкеПожалуйста, дайте подробный ответ
помогите разобраться с сортировкой и выводом всего этого в файл по возрастанию, пробовал реализовывать через динамические массивы, но так...