Некоторые из технологий, используемых в геопространственном мире, довольно старомодны. История некоторых проектов восходит к 1960-м годам. В то время люди не были избалованы выбором языков программирования, как сегодня, поэтому неудивительно, что многие API-интерфейсы маршрутизации написаны на C/C++.

История изначально была опубликована ЗДЕСЬ

В NextBillion.ai мы модифицируем и настраиваем некоторые из этих проектов для использования в наших предложениях API маршрутизации. Как и следовало ожидать, это не простое предложение из-за использования C/C++. Наиболее очевидным вариантом для рассмотрения было продолжение написания API полностью на C/C++.

Однако при этом возникают трудности:

  1. В наши дни трудно нанять инженеров с опытом работы с C/C++.
  2. Трудно обеспечить сохранность памяти и качество кода, а также использовать современные методы и фреймворки программирования.

Итак, вот как мы это сделали.

Мы разделили нашу разработку на две части. Первая часть была связана с изменениями, которые обязательно должны быть внесены в код C/C++; вторая часть может быть закодирована с использованием чего-то менее болезненного, что интегрируется с кодом C/C++ через FFI.

Затем мы спросили себя, как лучше всего вызвать библиотеку C/C++ во время выполнения? Вот тут-то и появился Rust.

Существует так много инструментов/языков программирования, которые предоставляют FFI как функцию, но по-разному. Rust, новая блестящая игрушка, о которой все говорят, — одна из них.

В наши дни Rust довольно популярен; есть ряд историй успеха и удовлетворенности пользователей, таких как: https://discord.com/blog/why-discord-is-switching-from-go-to-rust

Возвращаясь к дилемме NextBillion.ai, мы теперь должны были спросить себя: может ли что-то столь же новое, как Rust, сочетаться с механизмами маршрутизации/API старой школы?

Давайте рассмотрим некоторые плюсы и минусы Rust и почему мы выбрали его для API маршрутизации NextBillion.ai.

Плюсы

Rust называет библиотеку C так же, как C называет библиотеку C

Rust не имеет неотъемлемых накладных расходов при вызове библиотеки C. Это не относится к Java/Python/Node или даже к Golang. Во-первых, Rust не является языком сборки мусора и разумно управляет безопасностью памяти во время компиляции. Таким образом, он достигает почти идеального баланса между накладными расходами во время выполнения и безопасностью памяти. Это означает, что мы можем наслаждаться его производительностью во время выполнения, не слишком беспокоясь о гонках данных, ошибках сегментации и утечках памяти.

В Rust есть бесстрашный параллелизм

Rust применяет свою модель владения, которая решает большинство проблем параллелизма во время выполнения. Как правило, вам будет значительно сложнее написать фрагмент кода с ошибками параллелизма в Rust по сравнению с почти всеми другими языками программирования. Кроме того, в Rust нет встроенной абстракции для многозадачности. Вы можете выбрать то, что имеет для вас наибольшее значение, будь то CSP (Golang), акторная модель, Async Await или Raw Multi-Threading. Вы избалованы выбором!

Ржавчина быстро разгорается

Фреймворки Rust занимают очень высокие места в списках рейтинга производительности веб-API. На самом деле, Rust часто имеет аналогичную производительность по сравнению с программами, написанными на C/C++.

Rust поддерживает современные цепочки инструментов

Попрощайтесь с make-файлами, потому что в Rust есть Cargo. Кроме того, приятно тестировать код Unity Rust.

Минусы

Rust «медленный» во время компиляции

Каждый современный язык программирования обеспечивает безопасность памяти и удобные для программиста функции за определенную плату. Например, Golang предоставляет горутину и сборщик мусора, но позволяет вам узнать, в каком потоке выполняется ваш код. Python дает вам многопоточность, но блокирует все с помощью GIL. Node.js не имеет блокировки, но полностью исключает многопоточность. В случае с Rust он дает невероятную производительность во время выполнения и безопасность памяти, но заставляет вас страдать во время компиляции. Новичкам-растообразцам приходится учиться справляться с крутой кривой обучения и упорствовать в тот период времени, когда им просто хочется разбить свои клавиатуры.

Rust слишком гибок и мощен для новичков

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

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

Наше путешествие с Rust до сих пор

В API/движках маршрутизации есть два набора проблем:

  1. Меньше или нет гибкости, но зато есть возможность очень быстро рассчитывать!
  2. Больше гибкости, но со снижением производительности.

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

В первые дни, когда мы быстро прототипировали решение для набора задач номер 1, мы напрямую модифицировали библиотеку C/C++ там, где это было абсолютно необходимо, а все остальное делали с помощью языка GC. В результате получился работающий API с приемлемой производительностью.

А затем возникли новые вызовы из запросов клиентов.

Некоторые примеры:

  1. Рассчитать расстояние A→B, ETA и маршрут с очень хорошей задержкой P95 при определенных одновременных запросах
  2. Рассчитайте ETA для 4000 × 4000 пунктов отправления и назначения (или матрица больших расстояний) как можно быстрее.

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

Языки GC по своей природе медленнее; мы также хотели сохранить полный контроль над тем, как работает наша программа, поэтому мы решили, что нам нужны мощь и контроль, которые предоставляет Rust.

Хотя это был нелегкий путь. Мы сделали все с нуля, например, изучили Rust, прошли его строгие проверки времени компиляции, написали правильный слой FFI, выбрали правильную модель параллелизма и многое другое. В конце концов, мы смогли разработать решение с задержкой 5 мс P95 для простых запросов маршрутизации, как того требовали некоторые клиенты. Очевидно, что во всем уравнении задержки есть больше переменных, но объединение нашего механизма маршрутизации с Rust проложило нам путь к достижению общей цели.

Вы оцениваете различные инструменты программирования для проекта, требующего C FFI? Надеюсь, плюсы и минусы, перечисленные в этом блоге, могут стать для вас хорошей отправной точкой!

Начните с NextBillion.ai сегодня!