• Как правильно использовать interface в TS + React?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    export type IQuestionsFilter = 'New' | 'Interesting' | 'Not Answer'
    interface позволяют определять только типы для объектов и функций, type в этом плане более гибкий.
    Ответ написан
    Комментировать
  • Что такое WEB api?

    bingo347
    @bingo347
    Crazy on performance...
    API - Application Programming Interface
    То есть это не обязательно что-то сетевое, интерфейс некой библиотеки - это тоже ее API

    Web API - это api браузера
    https://developer.mozilla.org/en-US/docs/Web/API

    Web Forms Api - часть api браузера для работы с формами
    https://developer.mozilla.org/en-US/docs/Web/API/H...
    https://developer.mozilla.org/en-US/docs/Web/API/F...
    Ответ написан
    Комментировать
  • Как скомпилировать отдельно ts исполняеый скрипт?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    tsc собирает только ts в js и ничего больше, ну tsx разве что еще умеет.
    Для сборки vue нужен сборщик, тот же webpack указанный в тегах, ну или vite или rollup или еще что нравится.
    Ответ написан
  • Что нам дает опция declaration если она ничего не меняет в импорте?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    Опция declaration влияет на то, будут ли собираться .d.ts файлы при сборке из js в ts.

    Если у Вас рядом лежат файлы user.js и user.d.ts то при импорте user.js из user.d.ts будут взяты для него типы, при этом содержимое user.js будет полностью проигнорировано компилятором ts, оно пойдет в сборку как есть.

    Глобальные типы можно объявить, если положить .d.ts файл в корень проекта (ну или в ту папку, на которую он должен влиять), при этом не должно быть одного имени с .js файлом, то есть должен быть самостоятельным.
    Но правильнее такое делать через опцию types.
    Ответ написан
    2 комментария
  • Эмулятор веб-камеры для Линукса?

    bingo347
    @bingo347
    Crazy on performance...
    OBS studio
    Ответ написан
    Комментировать
  • Происходит ли блокировка при обращении к асинхронным роутам?

    bingo347
    @bingo347 Куратор тега Node.js
    Crazy on performance...
    async function выполняется синхронно до первого await
    Каждый встреченный по ходу выполнения await прерывает функцию, выкидывая из нее вверх по стектрейсу, в случае если это первый await, то продолжит исполняться функция, вызвавшая нашу, но там обычно тоже await, хотя и не обязательно. Так постепенно мы вывалимся в event-loop. Продолжение работы после await вызывается непосредственно из event-loop, так что в этом случае выше по стектрейсу будет только он.
    Когда исполнение в event-loop, он просто берет следующую задачу из очереди. Притом async function - это абстракция над промисами и их колбэками, а они выполняются в очереди микротасок, которую event-loop разбирает пока она не станет пустой.

    Если ближе к примеру в вопросе, то выполнение будет таким:
    1. event-loop вызовет колбэк http сервера, который часть роутера
    2. роутер вызовет наш колбэк, он это может делать как синхронно так и асинхронно (express делает синхронно)
    3. Наш колбэк отправит запрос в БД, получит его промис и остановится на await
    4. мы вернем управление роутеру, а он модулю http и в итоге все вернется в event-loop
    5. event-loop возьмет следующую таску из очереди, это может быть как запрос к http серверу, так и ответ от БД
    Ответ написан
    2 комментария
  • Возможно ли использовать tauri-plugin-sql в главном коде rust?

    bingo347
    @bingo347
    Crazy on performance...
    Боюсь что только форкнуть и сделать вот эту структуру публичной:
    https://github.com/tauri-apps/tauri-plugin-sql/blo...

    Плюс не уверен как работают инжекты в tauri, возможно еще понадобится заменить инжекты на lazy_static
    Ответ написан
    Комментировать
  • В каких случаях надо испоinterfacce, а не type?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    interface поддерживает extends (который компилируется быстрее чем intersection (&) как верно подметил Василий Банников )
    interface A extends B, C {}
    interface B {}
    type C = {}; // interface вполне может extends из type


    interface поддерживает declaration merging
    interface A {
      a: number;
    }
    interface A {
      b: number;
    }
    const a: A = {a: 1, b: 2};


    type поддерживает вычисления типа на верхнем уровнеtype A = B | C; // на interface такого не выразишь

    В остальном различий нет.
    Declaration merging может подложить жабу в реальном проекте, но полезен при написании библиотек, которые могут быть расширены извне (плагины jQuery например)
    В целом type более универсален и надежен. И не сильно он медленнее. Вся его медленность заключается в том, что компилятор производит вычисления типа, которые так же возможны и в полях интерфейса, а значит присутствуют и там.
    Для Java/C# разрабов, читающих TS, interface более понятная конструкция (хотя и обманчиво).

    В целом все зависит от стайл гайда, который примет команда. Главное чтоб везде одинаково было.
    Ответ написан
    2 комментария
  • За счет чего Bun и Deno работают быстрее Node?

    bingo347
    @bingo347 Куратор тега Node.js
    Crazy on performance...
    У bun потенциально может быть быстрее старт приложения за счет использования движка JavaScriptCore вместо V8 в node/deno. Вот только у V8 на сегодня поддержка всех нюансов ECMAScript самая близкая к 100%. А старт рантайма происходит лишь 1 раз во время жизни приложения. Считать ли это профитом - вопрос философский.
    Ну и ИМХО, если биться за скорость путем смены движка, то я бы лучше взял QuickJS.

    Еще потенциальный выигрыш может быть за счет переписывания тех частей, что в node реализованы на JS. В node на самом деле очень многое на JS сделано. 100% встроенных модулей написано на JS, где уже под капотом дергаются функции из C++. Я не смотрел исходники deno/bun, но экспериментировал с v8 на Rust (спасибо deno за нормальный биндинг). Работать это будет быстрее, вот только обрабатывать JS Promise из нативки то еще приключение, и хочется делать это пореже за счет JS прослоек, как собственно и происходит в node.
    Rust (deno) и Zig (bun) по скорости сравнимы с плюсами (где-то быстрее, а где-то медленнее, но в среднем примерно одинаково). Самый большой оверхед в языках с рантаймом создает рантайм (сборка мусора, JIT компилятор и т.д.), у C++, Rust и Zig подкапотного рантайма почти нет, у JS - целый V8/другие движки.

    Ну и еще момент, что bun, что deno работают с ts из коробки. Вот только TypeScript - это язык у которого нет спеки, есть открытый код компилятора (в котором черт ногу сломит), есть release notes, а спеки нет. А значит гарантировать 100% совместимость с tsc не может никто. Хотя это проблема не только этих сред, но еще и WebStorm и babel-typescript например. Хотя на сколько понял они пошли путем babel - просто затранспайлить (по сути выкинуть все типы), вот только babel в некоторых кейсах все же ломает код...
    Ответ написан
    1 комментарий
  • Как исправить код конвертации vec_u8 to vec_&str?

    bingo347
    @bingo347
    Crazy on performance...
    Сумбурный вопрос, но если правильно понял, то автор хочет получить Vec<&str> элементы которого ссылаются на исходный вектор байт и в каждом элементе строка из 1 символа:
    let u01 = vec![59, 13, 10, 32, 47, 42];
    let u01_str = std::str::from_utf8(&u01).expect("invalid utf8");
    let mut u02 = Vec::with_capacity(u01.len());
    let mut i0 = 0;
    for (i, _) in u01_str.char_indices().skip(1) {
        u02.push(&u01_str[i0..i]);
        i0 = i;
    }
    u02.push(&u01_str[i0..]);
    println!("u02 = {:?}", u02);
    Ответ написан
    Комментировать
  • Как передать из функции значения в разные потоки?

    bingo347
    @bingo347
    Crazy on performance...
    Начну с того, что код представленный автором в комментах к вопросу имеет deadlock между мьютексом и recv() из канала и завершается лишь по тому, что мы не ждем фоновые потоки. Вариант без deadlock будет выглядеть так:
    fn test() {
        let mut channels = Arc::new(Mutex::new(Vec::with_capacity(PAR)));
        let mut joins = Vec::with_capacity(PAR);
        for _ in 0..N / PAR {
            for _ in 0..PAR {
                let mut channels = Arc::clone(&channels);
                joins.push(thread::spawn(move || {
                    get(channels.lock().unwrap());
                }));
            }
        }
        for j in joins {
            j.join().unwrap();
        }
    }
    
    #[inline(always)]
    fn get(mut channels: MutexGuard<Vec<mpsc::Sender<i32>>>) -> i32 {
        let (tx, rx) = mpsc::channel();
        channels.push(tx);
        if channels.len() == PAR {
            exec(channels);
        } else {
            drop(channels); // drop гварда отпускает мьютекс
        }
        rx.recv().unwrap()
    }
    
    #[inline(always)]
    fn exec(mut channels: MutexGuard<Vec<mpsc::Sender<i32>>>) {
        let mut i = 0;
        for c in channels.iter() {
            i += 1;
            c.send(1).unwrap();
        }
        println!("{}", i);
        channels.clear();
        // а здесь гвард дропнется сам
    }

    Вторая проблема в том, что все потоки выполняются по сути по очереди, так как ждут разблокировки мьютекса от других потоков, из-за чего многопоточка тут не дает никаких преимуществ, а лишь создает накладные расходы. Ради эксперимента я попробовал заменить мьютекс на еще один канал:
    fn test() {
        let (tx, rx) = mpsc::channel::<mpsc::Sender<i32>>();
        let mut handles = Vec::with_capacity(N + 1);
        handles.push(thread::spawn(move || exec(rx)));
        for _ in 0..N {
            let tx = tx.clone();
            handles.push(thread::spawn(move || {
                get(tx);
            }))
        }
        drop(tx);
        for handle in handles {
            handle.join().unwrap();
        }
    }
    
    fn get(sender: mpsc::Sender<mpsc::Sender<i32>>) -> i32 {
        let (tx, rx) = mpsc::channel();
        sender.send(tx).unwrap();
        rx.recv().unwrap()
    }
    
    fn exec(receiver: mpsc::Receiver<mpsc::Sender<i32>>) {
        let mut channels = Vec::with_capacity(PAR);
        loop {
            for _ in 0..PAR {
                let Ok(tx) = receiver.recv() else {
                    return;
                };
                channels.push(tx);
            }
            let mut i = 0;
            for c in channels.iter() {
                i += 1;
                c.send(1).unwrap();
            }
            println!("{}", i);
            channels.clear();
        }
    }
    Но особо это профита не дает, так как основной пожиратель перфоманса - switch context в ОС. Тысячи потоков делают только хуже. Запускать потоков сильно больше чем есть ядер - это вообще плохая идея. Просто ради интереса переписал еще раз на асинхронных каналах tokio:
    async fn test() {
        let (tx, rx) = mpsc::unbounded_channel::<mpsc::UnboundedSender<i32>>();
        let mut handles = Vec::with_capacity(N + 1);
        handles.push(tokio::spawn(async move { exec(rx).await }));
        for _ in 0..N {
            let tx = tx.clone();
            handles.push(tokio::spawn(async move {
                get(tx).await;
            }))
        }
        drop(tx);
        for handle in handles {
            handle.await.unwrap();
        }
    }
    
    async fn get(sender: mpsc::UnboundedSender<mpsc::UnboundedSender<i32>>) -> i32 {
        let (tx, mut rx) = mpsc::unbounded_channel();
        sender.send(tx).unwrap();
        rx.recv().await.unwrap()
    }
    
    async fn exec(mut receiver: mpsc::UnboundedReceiver<mpsc::UnboundedSender<i32>>) {
        let mut channels = Vec::with_capacity(PAR);
        loop {
            for _ in 0..PAR {
                let Some(tx) = receiver.recv().await else {
                    return;
                };
                channels.push(tx);
            }
            let mut i = 0;
            for c in channels.iter() {
                i += 1;
                c.send(1).unwrap();
            }
            println!("{}", i);
            channels.clear();
        }
    }
    и запустил в многопоточном рантайме в дефолтной конфигурации (количество воркеров == количеству ядер), работает быстрее в 19 раз.

    P.S. без I/O операций асинхронщина тоже создает ненужные накладные расходы, я ее здесь использовал только из-за простоты переписывания, лучше взять обычный thread pool с синхронными тасками.
    Ответ написан
    Комментировать
  • Почему реджекшн главного промиса не перехватываться блоком .catch?

    bingo347
    @bingo347 Куратор тега Node.js
    Crazy on performance...
    Как я понимаю, этот .catch должен перехватить реджект главного
    промиса, но эта часть в принципе не выполняется и выходит
    ошибка UnhandledPromiseRejection

    Не должен, он с ним никак не связан.
    Ответ написан
    1 комментарий
  • Какой Object pool стоит использовать в Rust?

    bingo347
    @bingo347
    Crazy on performance...
    меня очень смущает постоянно выделять буффер

    Конкретно в данном примере буффер выделяется на стеке, то есть на его выделение не тратится ровным счетом ничего, так как стек и так уже выделен при запуске потока.
    Есть конечно копеечные затраты ресурсов на заполнение буфера нулями. И если в буффер гарантированно сначала идет запись, как в данном случае, то это в принципе можно обойти:
    use std::mem::MaybeUninit;
    let mut buffer = unsafe {
        #[allow(invalid_value)]
        MaybeUninit::<[_; 1024]>::uninit().assume_init()
    };
    Но я бы так не делал. Во-первых чистота кода не стоит этих копеек производительности, а во-вторых немного накосячите с чтением и будет UB.
    Ответ написан
    3 комментария
  • Почему в Docker собирается не тот Rust?

    bingo347
    @bingo347
    Crazy on performance...
    FROM rust:latest as build
    
    WORKDIR /test-tcp
    
    COPY ./Cargo.lock ./Cargo.lock
    COPY ./Cargo.toml ./Cargo.toml
    COPY ./src ./src
    
    RUN cargo build --release
    
    FROM debian:buster-slim
    
    COPY --from=build /test-tcp/target/release/test-tcp /usr/src/test-tcp
    
    CMD ["/usr/src/test-tcp"]

    Проблема в том, что в докере Вы создаете пустой проект из шаблона cargo
    RUN USER=root cargo new --bin test-tcp
    и компилируете его, потом копируете src
    Да, там есть еще один билд после этого, но я попробовал собрать Ваш Dockerfile у себя, и получил на втором билде результат инкрементальной сборки без изменений. То есть cargo не увидел, что папка src поменялась.
    Ответ написан
    4 комментария
  • Как типизировать router.push(pathname)?

    bingo347
    @bingo347 Куратор тега TypeScript
    Crazy on performance...
    if (pathname !== null) {
        router.push(pathname);
    }
    Ответ написан
  • Как правильно принимать данные в потоках?

    bingo347
    @bingo347
    Crazy on performance...
    Arc нужно клонировать до move в замыкание, которое запускается на потоке. Если данные используются только на чтение, то этого будет достаточно, если данные будут меняться из нескольких потоков, то следует дополнительно обернуть их в Mutex/RwLock (или из std::sync или из библиотеки parking_lot).
    use std::sync::Arc;
    
    fn main() {
        let data = Arc::new(vec![1, 2, 3]);
    
        let thread_1 = std::thread::spawn({
            let data = Arc::clone(&data);
            move || {
                println!("Thread 1 data: {:?}", data);
            }
        });
    
        let thread_2 = std::thread::spawn({
            let data = Arc::clone(&data);
            move || {
                println!("Thread 2 data: {:?}", data);
            }
        });
    
        thread_1.join().unwrap();
        thread_2.join().unwrap();
    }

    Передавать так можно хоть вектор, хоть свою структуру, главное чтоб у типа был трейт Send и лайфтайм 'static (все владеющие типы имеют такой лайфтайм).
    Так как передаем мы по сути Arc, то Send должен быть у него, а он будет для любого содержимого реализующего Sync.

    Я обернул создание отдельного потока в функцию и так передавал в поток данные. Удобно, что такую функцию могу вынести в отдельный файл-модуль. Но не смог такое сделать динамически в цикле для группы потоков. Хочу подойти к варианту, когда поток, который закончил выполнение своего кода (раньше других), можно опять запустить из main и передать ему новую задачу (новые данные), - структуру данных, которую привёл в основном вопросе.
    Если правильно понял, то Вам нужен thread pool. Можно использовать из библиотеки rayon: https://docs.rs/rayon/1.7.0/rayon/struct.ThreadPoo...
    Но если хочется повелосипедить, можно нечто такое сделать:
    use std::{
        sync::{
            mpsc::{self, Sender},
            Arc, Mutex,
        },
        thread::{self, JoinHandle},
    };
    
    type Task = Box<dyn FnOnce() + Send + 'static>;
    
    pub struct ThreadPool {
        handles: Vec<JoinHandle<()>>,
        task_sender: Sender<Task>,
    }
    
    impl ThreadPool {
        pub fn new() -> Self {
            let threads_count = thread::available_parallelism()
                .map(|n| n.get())
                .unwrap_or(2);
            let (task_sender, task_receiver) = mpsc::channel::<Task>();
            let task_receiver = Arc::new(Mutex::new(task_receiver));
            let mut handles = Vec::with_capacity(threads_count);
            for _ in 0..threads_count {
                let task_receiver = Arc::clone(&task_receiver);
                handles.push(thread::spawn(move || loop {
                    let task_receiver = task_receiver.lock().unwrap_or_else(|e| e.into_inner());
                    let Ok(task) = task_receiver.recv() else {
                        return;
                    };
                    drop(task_receiver);
                    task();
                }));
            }
            Self {
                handles,
                task_sender,
            }
        }
    
        pub fn spawn_task<F: FnOnce() + Send + 'static>(&self, f: F) {
            self.task_sender.send(Box::new(f)).expect("All threads ended");
        }
    
        pub fn join(self) -> thread::Result<()> {
            drop(self.task_sender);
            for handle in self.handles {
                handle.join()?;
            }
            Ok(())
        }
    }
    
    #[test]
    fn thread_pool() {
        let pool = ThreadPool::new();
        for i in 0..500 {
            pool.spawn_task(move || {
                println!("Task {} working on thread {:?}", i, thread::current().id());
            });
        }
        pool.join().unwrap();
    }
    Ответ написан
    3 комментария
  • Как правильно сложить дату-время в массив?

    bingo347
    @bingo347
    Crazy on performance...
    use chrono::prelude::*;
    use std::sync::mpsc;
    use std::sync::mpsc::{Receiver, Sender};
    use std::{thread, time};
    
    fn main() {
        println!("- - - - -");
        let mut children = Vec::with_capacity(3);
    
        for id in 0..children.capacity() {
            let child = thread::spawn(move || {
                let mut date_times = Vec::with_capacity(5);
                for i in 0..date_times.capacity() {
                    let t: DateTime<Local> = Local::now();
                    date_times.push(t);
                    println!("{:?}_ поток, задача _{:?}, время: {:?}", id, i, t);
                    thread::sleep(time::Duration::from_millis(3));
                }
                (id, date_times)
            });
    
            children.push(child);
        }
    
        for child in children {
            let (id, date_times) = child.join().expect("Дочерний поток паникует");
            println!("thd_{} = {:?}", id, date_times);
        }
        println!("- - - - -");
    }
    Ответ написан
    1 комментарий
  • Как сделать много вставок в HashMap за минимальное время?

    bingo347
    @bingo347
    Crazy on performance...
    Начну с того, что Вы абсолютно не понимаете зачем нужен async. Есть 2 вида нагрузки - CPU-bound и I/O-bound (где I/O - это input/output). Особенность I/O-bound нагрузки в том, что процессор большую часть времени простаивает в ожидании I/O операций (диска, сети, базы данных и т.д.). Async как раз решает эту задачу за счет кооперативной многозадачности, пока одна задача ждет ответ от I/O мы можем нагрузить CPU другой задачей. Естественно все это не бесплатно, но выигрыш тут в отсутствии простоя CPU за счет того, что мы запускаем тысячи задач на небольшом количестве потоков (в некоторых средах, вроде node.js или asyncio в python такой поток вообще 1), например tokio по-умолчанию запускает пул потоков по количеству ядер.
    Из этого уже можно выделить проблемы в Вашем коде:
    #[tokio::main(flavor = "multi_thread", worker_threads = 1024)]
    1024 - потеряли весь профит от небольшого числа потоков, теперь ОС будет распределять 1024 потока на небольшое количество ядер CPU.
    async fn set(&mut self, name: String, value: String) {
        self.data.insert(name, value);
    }
    у этого метода нет ни одной причины быть асинхронным, операции с HashMap - чистый CPU-bound.

    Вообще, данную задачу можно распараллелить, и для этого достаточно обычных потоков:
    fn main() {
        let start = Instant::now();
        let handles: Vec<_> = (0..4)
            .map(|table_index| {
                std::thread::spawn(move || {
                    let mut table = Table::new();
                    for i in (0..3000000).filter(|i| (i % 4 + 1) == table_index) {
                        table.set(format!("{}", i), format!("value{}", i));
                    }
                    table
                })
            })
            .collect();
        for handle in handles {
            let _table = handle.join().unwrap();
            // тут добавляем таблицы в менеджер
        }
        let elapsed = start.elapsed();
    
        println!("Time taken to set 3,000,000 keys: {:?}", elapsed);
    }
    и даже это можно заморочиться и улучшить, например запускать потоков не больше std::thread::available_parallelism() или оптимизировать счетчик для каждой таблицы (
    (0..3000000).filter(|i| (i % 4 + 1) == table_index)
    ), но это я оставлю Вам в качестве д/з.

    В общем, Ваша проблема не в том, что какие-то структуры/библиотеки медленные, а в том, что Вы их используете не по назначению.
    Ответ написан
    2 комментария