Я играю с Rust уже около месяца, и я действительно впечатлен.

Rust позиционирует себя как невероятно быстрый и эффективный с точки зрения памяти: без среды выполнения или сборщика мусора. Он реализует это с помощью концепции заимствования, когда владение переменными заимствуется вместо самих переменных, когда они передаются через несколько модулей. Если какое-либо из правил времени компиляции для владения не выполняется, код не компилируется.

Это может показаться утомительной парадигмой, о которой нужно помнить при программировании, но на самом деле она весьма удобна, так как размер вашего кода увеличивается. Первые несколько раз, когда я экспериментировал с Rust, я обнаружил, что делаю так много ошибок, особенно при передаче переменных через несколько функций — довольно хороший способ узнать о безопасности памяти. Однако самым большим преимуществом является то, что это устраняет необходимость в ручном освобождении памяти — поэтому, когда вы совершенствуетесь в том, как вы программируете на Rust, вы начинаете быстрее создавать код и не беспокоиться об утечках памяти. Так что по мере роста моей кодовой базы у меня не было этих проблем — компилятор делал это за меня.

Это эффективное использование памяти, хорошо. Но как насчет скорости? Rust часто упоминается как намного более быстрый, чем его конкуренты, в большом количестве тестов производительности, а его производительность сравнима с производительностью C/C++. Существует довольно много тестов, которые соответствуют этим результатам, например, этот.



Глядя на эти результаты, совершенно очевидно, что Rust превзойдет интерпретируемый язык, такой как Python. Я хотел посмотреть, насколько хорошо он выдержит, и решил реформировать мой старый проект — путь Rust.

Настраивать

Идея заключалась в том, чтобы создать приложение для музыкальных рекомендаций, которое подбирает песни на основе двух основных факторов:

  1. Насколько близко звуковые характеристики песен совпадают
  2. Сетевые предположения — они встречаются в одном и том же плейлисте? Если да, то вполне вероятно, что они в чем-то похожи.

Первый фактор используется для присвоения оценки песне.

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

Серверная часть представляет собой простой одноразовый алгоритм кластеризации, который группирует песни на основе схожести звуковых характеристик. Данные хранятся в базе данных postgres, которая в настоящее время настроена на Amazon RDS. Набор данных был взят из этой бумаги (Ching-Wei Chen, Paul Lamere, Markus Schedl, and Hamed Zamani. Recsys Challenge 2018: Automatic Music Playlist Continuation. In Proceedings of the 12th ACM Conference on Recommender Systems (RecSys ' 18), 2018 г.)

Фактическая функция рекомендации работает следующим образом: при получении входной песни она находит возможные совпадения на основе всех списков воспроизведения, к которым принадлежит входная песня, и собирает все песни во всех кластерах, которые встречаются в этих списках воспроизведения. Затем они оцениваются на основе их звуковых характеристик и ранжируются. Возможно, это не лучший из возможных алгоритмов для эффективного поиска, так как он сильно зависит от того, насколько хорошо сгруппированы треки, но он дает нам хорошее входное пространство для сравнения операций.

Испытания

Версия 1 этого проекта была написана на Python. Как и ожидалось, функция подсчета очков была самой медленной: для некоторых песен требовалось около минуты. Это была та часть, которую я переключил на Rust, и отличия были радикальными:

Еще немного о функции подсчета очков — для этого конкретного ввода функция сравнила введенную песню примерно с 171 тыс. дорожек по 14 функциям — это более 2,3 миллиона операций, за которыми следует 171 тыс. оценок для сравнения и сортировки! И Rust сделал это чуть более чем за полсекунды, а Python — почти за 40 секунд. Впечатляющий.

Если вы хотите попробовать это сами, зайдите на мой сайт.

Заключение

Веб-сайт настроен с интерфейсом React, а приложение работает на лямбда-выражении AWS, что приводит к собственным временным факторам, связанным с холодным запуском и т.п. При этом приложение работает довольно хорошо, как показано в этом видео.

Это всего лишь вторая версия приложения, и есть много способов улучшить ее. Сейчас в моей базе данных около 280 000 песен, разбросанных по 21 000 спискам воспроизведения — по мере увеличения размера данных нам придется распараллеливать вычисления везде, где это возможно. Rust переместил шаг ограничения скорости на вызов пространства поиска базы данных GET — среди прочего, нам нужно было улучшить это. Кроме того, сбор этих данных из API Spotify занял у меня около 21 часа вычислительного времени. Кроме того, есть часть UI/UX приложения, промежуточное ПО rust, фактический алгоритм кластеризации, факторы настройки для определения приоритетов атрибутов — вы понимаете, многое нужно сделать.

Тем не менее, это было забавное приложение для создания! И мы даже не изучили возможности Rust для обеспечения безопасности потоков и параллелизма, которые становятся важными, когда мы масштабируем приложения для тысяч пользователей; или как в Rust нет типа NULL, но есть концепция типа NULL, которая становится весьма полезной при обработке ошибок, связанных с неожиданными значениями NULL (или, как выразился Тони Хоар, Ошибка на миллиард долларов»). Темы для другого дня, возможно!

Посмотрите демо на https://manishlapasi.github.io/react-spotify/ и дайте мне знать, что вы думаете! А если вас интересуют исходники — https://github.com/ManishLapasi/Spotify-Demo. Я не включил лямбда-интеграцию AWS, настройку инфраструктуры AWS terraform, настройку рабочего процесса github Actions и т. д., но вы можете проверить внешний интерфейс и приложение Rust!