LINUX.ORG.RU
ФорумTalks

Rust в объектом Фибоначчи.

 , ,


1

7

Давно я к этому своему синтесту работы с объектами не обращался. Но вчера, после годичного забвения, обновлял данные по PHP7 (очень интересен был его рывок) и в голову пришла мысль измерить Rust. Заодно, решил посмотреть, что за язык получился :) На знакомство с языком и написание теста ушло 15 минут, так что, возможно, его качество далеко от идеала. Потому тему и завёл, может, где-то грубо ляпнул. Я даже не знаю до сих пор, как дело в Rust со сборкой мусора обстоит и он эквивалент по этой классификации Java/C-хипового или C-стекового.

Важно! Это не программа для вычисления чисел Фибоначчи, это тест для скорости создания объектов, обращений к свойствам и вызовов методов. Поэтому не нужно делать «алгоритмическую оптимизацию», типа вынесения локальных переменных, отказа от объектов вообще или введения хвостовой рекурсии — просто тогда сразу по формуле Бине посчитать :D

Вот мой первый вариант: https://github.com/Balancer/benchmarks-fib-obj/blob/f6474cbbca83b8ae376c27d5e...

Получилось 0.603 сек., что почти вдвое хуже стекового Си (0.350) и немного лучше Java (0.685) со сборкой мусора.

Результат после оптимизации в топике — 0.329 и у Rust первое место.



Обновлённая таблица: https://github.com/Balancer/benchmarks-fib-obj/wiki/Результат-теста:-i3-2.2ГГц

★★★★★

Последнее исправление: KRoN73 (всего исправлений: 1)

Я даже не знаю до сих пор, как дело в Rust со сборкой мусора обстоит

Её там нет.

и он эквивалент по этой классификации Java/C-хипового или C-стекового.

С-стекового.

Legioner ★★★★★
()

стекового Си

А что это за зверь такой? Какая-то хитрая помесь Форта и Си? Что-то двухсекундное гугление не дало результата...

Stahl ★★☆
()
Ответ на: комментарий от Stahl

стекового Си

А что это за зверь такой?

Когда в Си создаётся объект, то он может быть создан на стеке вызова. Получается очень быстро. Но нельзя вернуть ссылку на такой объект из функции — перед возвратом из функции всё, созданное на стеке освобождается. В Си так создаются простые переменные или объекты, создаваемые как Complex j = Complex(0,1);. В этом случае объект будет создан на стеке и будет уничтожен при выходе из функции.

Если нужно вернуть ссылку на объект, его нужно создавать где-то отдельно в памяти. В классическом Си это «куча» («heap»). Объекты создаются по new и должны быть удалены потом вручную по delete. Ссылку на такой объект можно вернуть, но операция выделения памяти очень медленная. И нужно быть осторожным, чтобы не забыть потом удалит объект, иначе будет утекать память. Complex *j = new Complex(0,1);

KRoN73 ★★★★★
() автор топика
Последнее исправление: KRoN73 (всего исправлений: 1)
Ответ на: комментарий от DELIRIUM

«Мы говорим Си, подразумеваем Си++» :)

На классическом Си я последнюю программу писал лет почти 25 назад :D

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от Stahl

Т.е. ты просто работаешь с локальными переменными, которые хранятся в стековом кадре. Ок.

В Си++ есть оба варианта. В Rust, как писал выше, без понятия, но, как меня выше дополнили, там именно стековый вариант. Надо будет попробовать вариант с выделением памяти.

...

Сейчас посмотрел — в Rust аж три вида указателей. Можно будет сравнить их производительность :)

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от Weres

Вы собирали с --release, я надеюсь?

Нет, только с -O. Во избежание неоднозначностей у меня по всем тестам лежит и запускалка соответствующая. rust я тестировал так: https://github.com/Balancer/benchmarks-fib-obj/blob/a8bcdbf1664adaeedefe99d7d...

И, да:

$ rustc --release
error: Unrecognized option: 'release'.
KRoN73 ★★★★★
() автор топика

Получилось 0.603 сек., что почти вдвое хуже стекового Си (0.350)

Только вариант на С++ у тебя с -O3 собран, а версия на Rust без оптимизаций.

У меня такие результаты (для 10 итераций, вариант на Rust собран с ключом --release): Rust — 3.09, C++ — 2.79.

theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 1)
Ответ на: комментарий от KRoN73

Это секунды :) Я поправил число итераций и опции сборки для версии на Rust и получил такие результаты.

theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 1)
Ответ на: комментарий от KRoN73

g++ -O3
rustc -O

C++: for(int i=0; i<10; i++)
Rust: for _ in 0..3

На знакомство с языком и написание теста ушло 15 минут

Вы прям мастер избегания неоднозначностей. Зачем вы вообще это делаете? Вы понимаете, что практическая ценность вашего бенчмарка равна нулю? Зачем приносить это на ЛОР? Вы троллите что ли?

Weres ★★★
()

во всех тестах имеет смысл убрать printf и ему подобные - то есть вообще весь IO..просто заменить на сравнение с константой и exit(1) при несовпадении

MKuznetsov ★★★★★
()
Ответ на: комментарий от theNamelessOne

Аналог без использования Cargo будет выглядеть так: rustc -C opt-level=3 fib.rs

Результат стал 0.329 сек. и Rust вышел на первое место. Обновил таблицы и скрипт запускалки.

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от MKuznetsov

во всех тестах имеет смысл убрать printf и ему подобные

Оверхед от них ничтожный. Даже время стартапа и то куда больше сказывается (хотя тоже пренебрежимо).

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от Joe_Bishop

выхлоп убери, как советовали

Вот специально проверил :) Изменение — в 4-м знаке после запятой. Я вывожу по три знака. Так что пофиг.

...

Кстати, забавно — для С++11 мне в своё время пришлось аргумент с stdin вводить. А то для константы компилятор на этапе компиляции всю эту бодягу с объектами раскручивает до константы же.

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

Оверхед от них ничтожный. Даже время стартапа и то куда больше сказывается (хотя тоже пренебрежимо).

ты столько лет забавляешься этим тестом, но так и не смог его довести до корректных, неоспоримых результатов :-(

создание/удаление среды исполнения очевидно влияет но не учитывается (в каком-то тесте __отняты__ магические 3 секунды)

организация итераций не учитывается, и самого числа итераций недостаточно чтобы делать выводы (3-10 шт на вызов теста это ни о чём и отчего-то это разные числа для разных тестов)

зачем-то в бенчемарке присутствует IO

MKuznetsov ★★★★★
()
Ответ на: комментарий от theNamelessOne

Ясно, спасибо. Я ссылки с вики открывал.

Weres ★★★
()
Ответ на: комментарий от MKuznetsov

создание/удаление среды исполнения очевидно влияет но не учитывается

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

в каком-то тесте __отняты__ магические 3 секунды

О чём я честно и предупредил :) Это было года три назад и для маргинального языка. Точный результат, тем более, что он не отличался бы более чем на десяток процентов, никому не интересен. Достаточно оценки. Хотя можно и обновить сейчас попробовать, наверняка Fantom за эти годы развивался :)

организация итераций не учитывается

Поясни.

самого числа итераций недостаточно чтобы делать выводы

Число итераций в тесте не для того, чтобы получить какой-то средний результат. А для того, чтобы минимизировать влияние стартапа. Число итераций в скрипте — это не общее число тестов, это некое значение для условного продолжительного теста. Реальное число итераций зависит от значения времени вычисления, спорности позиции, влияния времени работы на прогрев JIT и т.п. Поэтому, скажем, PHP+xdebug я измерял всего два раза (чтобы убедиться, что цифры совпадают — два измерения это уже почти по-часа), а вот при сравнении JVM делал по 4-5 запусков скрипта с 10 измерениями в каждом из которых по 10 вычислений. Т.е. 40-50 тестов :)

самого числа итераций недостаточно чтобы делать выводы

Его влияние в 4-м знаке, см. выше комментарий по rust.

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

А почему бы просто не замерять время исполнения конкретного куска кода, а не всей програмы? Тогда время на стартап можно будет просто не учитывать.

theNamelessOne ★★★★★
()
Ответ на: комментарий от theNamelessOne

А почему бы просто не замерять время исполнения конкретного куска кода, а не всей програмы?

Не во всех языках есть возможность узнать текущее время в виде микросекундного таймстампа, чтобы посчитать время исполнения. Сейчас сходу не вспомню, но где-то я на это натыкался :)

...

Ну и традиция уже. Стараюсь, чтобы код был максимально идентичен. Можно, конечно, и io выкинуть, и время цикла измерять, но тогда придётся всё переписывать для единообразия. Время придётся потратить заметное, а результат в худших случаях на проценты только изменится :)

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

а сделать общий интерфейс для кейсов "./a.out число_для_расчёта кол_во_итераций" и гонять скриптом их для сбора статистики: за N-лет руки/голова не дошли никак? Пока-что как в классике «тут читаем, тут не читаем, тут мы рыбу завариваем»

Это-ж просто было сделать и все надуманные «Не учитывается, поскольку не принципиально» разрешаются и доказываются просто обработкой результатов прогонов. Никаких фантазий,только цифры - это-ж бенчмарк

ps/ кстати постановка кейса подразумевает скрытую рекурсию и отчасти замеряет скорость организации call-фреймов и размотки стека (для чистых интерпретаторов и jit это может быть смертельно по скорости). На фоне new/delete это возможно теряется, а может и нет - бенч не позволяет это проверить.

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

и гонять скриптом их для сбора статистики

А нафига? Преждевременная оптимизация — зло.

все надуманные «Не учитывается, поскольку не принципиально» разрешаются и доказываются

И что надуманного и недоказанного? Что 1% (или, тем паче, 0.1%) сильно влияют на результат? Ты утверждаешь — ты и доказывай. Пока надуманное утверждение без доказательства — заметное влияние на результат :)

На фоне new/delete это возможно теряется, а может и нет - бенч не позволяет это проверить

А мне не шашечки нужные, а интересна скорость практической езды. Производительность языков как платформ объектных фреймворков. И new/delete тут один из краеугольных камней. До чистого эквивалента практике только можно бы было добавить на порядок больше обращений к методам/свойствам. Но это сильно усложнит задачу, тут интерес именно в простоте.

Вот бенчмарк варианта с наследованием давно пора сделать. Как-нибудь выкрою время.

KRoN73 ★★★★★
() автор топика

Ruby rocks!

Хм-м, Ruby выполняет тест быстрее чем Python. Откуда взялся миф про то что Ruby тормозит сильнее чем Python?

Camel ★★★★★
()
Ответ на: комментарий от KRoN73

И что надуманного и недоказанного? Что 1% (или, тем паче, 0.1%) сильно влияют на результат? Ты утверждаешь — ты и доказывай. Пока надуманное утверждение без доказательства — заметное влияние на результат :)

из нас двоих - это ты представил тест на ЛОР`е и утверждаешь что он работает - вот и доказывай что это действительно тест, что это бенчмарк, что измеряет то что заявлено и он хоть что-то значит при сравнении языков. При неплохой задумке тест пока остаётся ни-о-чём

А мне не шашечки нужные, а интересна скорость практической езды.

но дело в том измерены как-раз шашечки и в зелёных попугаях :-) Задай тему «как правильно в моём случае замерить именно скорость езды» - тут есть/быват неплохие специалисты.

MKuznetsov ★★★★★
()
Ответ на: Ruby rocks! от Camel

Откуда взялся миф про то что Ruby тормозит сильнее чем Python?

Это не миф. Ruby 1.8 был вдвое тормознее, чем Python 2.5 (на моём тесте на старой машине — 662 секунды против 313). Вот Ruby 1.9.0 уже сравнялся с Python (формально обходил, но разница минимальна — проценты), а 1.9.3 — уверенно обошёл. Старые цифры со старой машины: http://www.balancer.ru/g/p1618859

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от MKuznetsov

это ты представил тест на ЛОР`е

Так.

и утверждаешь что он работает

А вот этого я не утверждал. Я только предоставил свои результаты и описание, как они получены. Без каких-либо утверждений. Вот если ты по ним что-то утверждаешь — ты и доказывай. Бремя доказательства лежит на...

Задай тему «как правильно в моём случае замерить именно скорость езды»

Это абсолютно невозможно. Можно сделать только тесты по измерению одной конкретной задачи. И чем сложнее задача, чем больше разброс вариантов и количество некорректных её решений. Поэтому я решаю одну конкретную задачу, вычисление 40-го числа последовательности Фибоначчи объектным способом и не более того.

KRoN73 ★★★★★
() автор топика

rustc -C opt-level=3 fib.rs

real    0m1.762s
user    0m1.760s
sys     0m0.000s
rustc -C opt-level=3 -C lto fib.rs
real    0m0.178s
user    0m0.177s
sys     0m0.000s

shaiZaigh
()
Ответ на: комментарий от KRoN73

«Мы говорим Си, подразумеваем Си++»

Лучше так не делать, потому что получается «Мы говорим Си, подразумеваем Си++, а форум исходит на говно о том, что в 21ом веке не стоит писать на неэффективном и провоцирующем баги С, когда можно писать на С++, в котором лучше управление памятью, сильнее оптимизации за счет шаблонов и даже сраный std::sort работает быстрее qsort»

vertexua ★★★★★
()
Ответ на: комментарий от KRoN73

Спасибо

Большое спасибо за сбор статистики и этот замечательный экскурс в историю.

Camel ★★★★★
()
Ответ на: комментарий от KRoN73

В первоисточнике constexpr до самого конца. Компилятор заменяет все вычисление числом-результатом. Читинг - да, фейковый бенчмарк - да, правильное использование constexpr - да.

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

vertexua ★★★★★
()

это тест для скорости создания объектов

тогда сделай размер структурки хотя бы 128 бит, а то у тебя там инты гоняются безо всяких объектов

MyTrooName ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.