Какую Цену Мы Платим За Использование Async/Await В Языках Js/C#/Rust?

Привет, Хабр! Работая с Javascript/Typescript, я давно заметил, что асинхронный API медленнее аналогичного синхронного, и даже знал, что так и должно быть.

Но в последнем проекте узким местом стала асинхронная работа с файловой системой, и я занялся измерениями.

Известно, что Ждите можно использовать только внутри функций или блоков асинхронный , а это означает, что если наш самый низкий уровень API является асинхронным, нам придется использовать асинхронный/ожидание практически везде, даже там, где это явно не нужно.

Например, мы пишем сервисную функцию, которая извлекает объект из хранилища по ключу.

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

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

По мере работы программы реальных обращений к хранилищу становится все меньше, объекты обслуживаются из быстрого кэша, но интерфейс функции остается асинхронным! Какую цену мне придется заплатить за каждый асинхронный вызов? Результаты испытаний удручают. Давайте возьмем простую функцию и назовем ее асинхронный , и мы будем вызывать его в цикле, измеряя общее время и сравнивая его с аналогичным синхронным кодом.

Для сравнения синтаксиса я предоставляю полные тексты на 3-х языках.



Машинопись ( Дено )

Синхронный код
  
  
  
  
  
   

const b = Date.now() let j = 0.0 for (let i = 0; i < 1_000_000_000; i++) { j += f(i) } console.log(j + ', ' + (Date.now() - b)/1000 + 's') function f(i: number): number { return i / 3.1415926 }

Асинхронный код:

(async () => { const b = Date.now() let j = 0.0 for (let i = 0; i < 1_000_000_000; i++) { j += await f(i) } console.log(j + ', ' + (Date.now() - b)/1000 + 's') })() async function f(i: number): Promise<number> { return i / 3.1415926 }



С# (.

NET Core)

Синхронный код

using System; class App { static void Main(string[] args) { var b = DateTime.Now; var j = 0.0; for (var i = 0L; i < 1_000_000_000L; i++) { j += f(i); } Console.WriteLine(j + ", " + (DateTime.Now - b).

TotalMilliseconds / 1000 + "s"); } static double f(long i) { return i / 3.1415926; } }

Асинхронный код:

using System; using System.Threading.Tasks; class App { static async Task Main(string[] args) { var b = DateTime.Now; var j = 0.0; for (var i = 0L; i < 1_000_000_000L; i++) { j += await f(i); } Console.WriteLine(j + ", " + (DateTime.Now - b).

TotalMilliseconds / 1000 + "s"); } static async Task<double> f(long i) { return i / 3.1415926; } }



Ржавчина

Синхронный код

fn main() { let tbegin = std::time::SystemTime::now(); let mut j = 0.0; for i in 0.1_000_000_000i64 { j += f(i); } println!("{:?}, {:?}", j, tbegin.elapsed().

unwrap()); } fn f(i: i64) -> f64 { return i as f64 / 3.1415926 }

Асинхронный код:

//[dependencies] //futures = "0.3" use futures::executor::block_on; fn main() { block_on(async { let tbegin = std::time::SystemTime::now(); let mut j = 0.0; for i in 0.1_000_000_000i64 { j += f(i).

await; } println!("{:?}, {:?}", j, tbegin.elapsed().

unwrap()); }); } async fn f(i: i64) -> f64 { return i as f64 / 3.1415926 }



Результаты

Язык Синхронный код (сек.

)

Асинхронный код (сек.

)

%% потерь
Машинопись 7.48 173 23 раза
С# 7.46 76.2 10 раз
Ржавчина 7.45 19.2 2,6 раза
Мы видим, что все 3 языка одинаково хорошо выполняют арифметику, но накладные расходы составляют Ждите различаются на порядок.

Интересно, какое применение асинхронный/ожидание В большинстве случаев (и даже рекламируемых) стоимость асинхронного вызова просто непомерно высока.

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

Краткое содержание

Не зря разработчики Java не торопись добавление асинхронного синтаксиса непосредственно в язык, и хотя я считаю, что async/await — отличная абстракция, нам необходимо понимать масштаб накладных расходов при его использовании.

ПС Спасибо всем, кто указал на возможность ускорения кода за счет кэширования задач/обещаний (вместо кэширования результатов), а также на то, что в C# есть отличная возможность инструмент , который просто решает мою проблему.

Теги: #программирование #Высокая производительность #C++ #JavaScript #Rust #node.js #async/await #бенчмаркинг #нулевая стоимость

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.