• Может ли приложение слушать несколько портов?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Drno, вот именно это я и хочу проверить. Если я не забуду, я напишу Вам в ответе результаты теста. Но я бы не ждал этого рано, скорее всего потребуется значительное время для написания такого прототипа и его испытания.
    Написано
  • Может ли приложение слушать несколько портов?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Everything_is_bad, к несчастью гонка за улучшения подходит к концу и ускорять уже почти нечего.
    Написано
  • Может ли приложение слушать несколько портов?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Everything_is_bad, я понимаю Ваше недоумение по этому вопросу, так как не стал описывать Вам контекст. У меня приложение по сути только и делает, что принимает состояние и отдаёт его. Я могу слушать один порт и разбивать состояние на части. Но в таком случае у меня потоки будут "гоняться" за этими частями. Иными словами, при увеличение числа потоков в 2 раза, производительность будет повышаться в меньшее число раз, так как потоки вынуждены "устраивать гонки" за ресурсы. Однако, я могу разбить состояние по числу ядер и отдать каждому ядру свой порт. В таком случае каждый поток работает только с один портом (хотя для одного потока можно выставить и больше, главное чтобы порты были уникальны для каждого потока) и одной частью состояния. В этой схеме у меня нет блокировок, что улучшает способность к масштабированию до почти линейной.
    Написано
  • Может ли приложение слушать несколько портов?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Drno, я хочу отдать каждому ядру уникальный порт. Это позволит разбить приложение, не используя никаких примитивов синхронизации (в такой системе одному потоку нет смысла взаимодействовать с другим).
    Написано
  • Может ли приложение слушать несколько портов?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Everything_is_bad, связь есть. Можно разбить приложение на низком уровне (я не уверен, можно ли так сделать в питоновском asyncio, поэтому не могу привести понятный вам пример) на n самодостаточных потоков и дать каждому потоку свой порт. Это позволит приложению работать без блокировок и межпроцессорного взаимодействия. Можно слушать и один порт с любым event loop и работать быстро, но в таком случае будут накладные расходы на синхронизацию потоков. Вариант с портом на ядро работает быстрее по той причине, что не требует никаких примитивов синхронизации.
    Написано
  • Куда утекает память в read Rust?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Василий Банников, на Windows эта часть приложения не поддерживается, а именно эта ОС стоит на моём компьютере. В целом, приложение и не разрабатывается для использования на таких тяжёлых ОС, поэтому это не является проблемой для меня.
    Написано
  • Куда утекает память в read Rust?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Василий Банников, попробовал сразу сбрасывать файл. Производительность, очевидно, значительно просела, но и утечка никуда не делась. С профайлером мысль хорошая, попробуй научится профайлить Docker-контейнеры.
    Написано
  • Куда утекает память в read Rust?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Василий Банников, во-первых, у меня есть почти идентичная функция, в которой нет работы с файлами и память не утекает. Во-вторых, я начал заменять части функции (вместо чтения сам заполнил буфер, вернул другой тип, не вызывал блокировки и так далее) и обнаружил, что при комментировании этой строки и её симуляции не происходит утечка.

    Я подумал, что ошибка связана с блокировкой (хотя это тоже странно). Для этого я вызвал блокировку, закомментировал работу с файлом и воспользовался значением из кортежа, чтобы Rust не убрал функцию после компиляции. Утечка всё ещё не произошла. Так как у меня стабильный Rust, я не могу использовать black_box, поэтому не могу выражать уверенность в достоверности этого теста.

    Дальше я заменил работу с файлом на типичный seek и read и посмотрел. Утечка снова появилась!

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

    А дальше у меня закончились идеи, где ещё может прятаться ошибка.
    Написано
  • Куда утекает память в read Rust?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Василий Банников, я попробовал применить ваши изменения. Разницы, ожидаемо, нет. Память утекает где-то в reat_at. Я прошёлся по всем вызовам reat_at, но не нашёл там ничего интересного (4 вызова функций с одинаковыми параметрами, последняя вызывает syscall на unix), но именно там пропадает память.
    Написано
  • Куда утекает память в read Rust?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Василий Банников, этот код был покрыт тестами ещё давно. Ошибок он ни в Windows, ни в любом Docker-контейнере не содержит. Память утекает не в этой строчке (у меня есть почти идентичный код, только без чтения из файла, и он работает корректно; все тесты успешно сдаются; методом перебора кода построчно было найдено, что ошибка именно в строке "file.read().unwrap().read_at(info.1, &mut buf).expect("failed to read")")
    Написано
  • Куда утекает память в read Rust?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Василий Банников,
    #[inline(always)]
        pub fn len(&self) -> usize {
            let mut l;
            unsafe {
                // first byte is length
                l = (*self.ptr) as usize | (*self.ptr.offset(1) as usize) << 8;
                if l < 65535 {
                    return l;
                }
                return (*self.ptr.offset(2) as usize) | (*self.ptr.offset(3) as usize) << 8 | (*self.ptr.offset(4) as usize) << 16 | (*self.ptr.offset(5) as usize) << 24;
            }
        }
    Написано
  • Куда утекает память в read Rust?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Василий Банников, потому что я храню слишком много таких вот BinValue. Эти 4-6 сэкономленных байт имеют значение.
    Написано
  • Куда утекает память в read Rust?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Василий Банников, я принимаю срез. Дальше я аллоцирую память на размер среза + размер под его длину (2-6 байт). Мне нужно, чтобы Rust не очистил эту память за меня, так как я имею гарантию, что сам очищу память, когда это будет нужно. Если убрать leak, я встречаю двойное освобождение памяти.
    Написано
  • Куда утекает память в read Rust?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Василий Банников, 1, мне недостаточно знаний, чтобы переписать это без unsafe. 2 Drop точно вызывается (это я тоже проверил). Я возвращаю BinValue выше, где записываю его в буфер, после чего он и исчезает.
    Написано
  • Куда утекает память в read Rust?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Василий Банников, конечно, я могу показывать вам BinValue.

    pub fn new(slice: &[u8]) -> Self {
            let len = slice.len();
            let new_slice: *mut u8;
            let size;
            unsafe {
                if len < 65535 {
                    new_slice = Vec::<u8>::with_capacity(len + 2).leak().as_mut_ptr();
                    *new_slice.offset(0) = len as u8;
                    *new_slice.offset(1) = (len >> 8) as u8;
                    size = 2;
                } else {
                    new_slice = Vec::<u8>::with_capacity(len + 6).leak().as_mut_ptr();
                    *new_slice.offset(0) = 255u8;
                    *new_slice.offset(1) = 255u8;
                    *new_slice.offset(2) = len as u8;
                    *new_slice.offset(3) = (len >> 8) as u8;
                    *new_slice.offset(4) = (len >> 16) as u8;
                    *new_slice.offset(5) = (len >> 24) as u8;
                    size = 6;
                }
                ptr::copy_nonoverlapping(slice.as_ptr(), new_slice.offset(size), len);
            }
            BinValue {
                ptr: new_slice
            }
        }


    Ниже написано:
    impl Drop for BinValue {
        fn drop(&mut self) {
            let len = self.len();
            let size = if len < 65535 { 2 } else { 6 };
            unsafe {
                drop(Vec::from_raw_parts(self.ptr, len + size, len + size));
            }
        }
    }


    Суть этого кода в том, чтобы вместо 16 байт (8 на указатель и 8 на длину) использовать 10-14 (8 на указатель + 2-6 на длину). Но у меня есть другие части кода, которые используют BinValue и не содержат утечек памяти.

    Я тоже подумал на этот участок кода, но ошибка не там. Я пробовал заполнить его без чтения из файла и память не утекала.
    Написано
  • Как открывать много TCP соединений и поддерживать их?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Wexter, спасибо, что прояснили ситуацию. Я не зря попросил вас написать ответ, так как я не могу отметить решением комментарий. Есть ли ещё подводные камни в этом вопросе?
    Написано
  • Как открывать много TCP соединений и поддерживать их?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Wexter, перечитал 3 страницы Гугла. Так и не нашёл подтверждения вашим словам, хотя они звучат логично в контексте ADDR:PORT. Не могли бы вы написать ответ с ссылками (или ссылкой), подтверждающими ваши слова?
    Написано
  • Как открывать много TCP соединений и поддерживать их?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    pfg21, я был бы рад реализовать сообщение через тот же QUIC, но я ограничен требованиями к проекту. Проект обязан гарантировать пользователю безопасность, и как бы я не пытался объяснить, что UDP тоже может гарантировать доставку сообщений, я не могу победить предубеждение.
    Написано
  • Как открывать много TCP соединений и поддерживать их?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Wexter, я где-то описался выше? Естественно, каждый сервер имеет один порт. Но проблема то с клиентской точки зрения. Каждый раз, когда я подключаюсь к другому серверу, я должен занять порт. Порты в этой схеме кончаются у клиента.
    Написано
  • Как открывать много TCP соединений и поддерживать их?

    Eugene-Usachev
    @Eugene-Usachev Автор вопроса
    Сергей Соловьев, в идеале, конечно, поставить один суперкомпьютер с процессором на где-то миллион ядер и с где-то 70 ТБ оперативной памяти и шардировать приложение по одному потоку на каждое ядро, где каждый поток самодостаточен. Но в реальности приходится шардироваться на нескольких машинах. Мне очень интересно узнать, как реализовать кластер быстрее, чем через общение каждой машины с каждой. Но поддержание открытых соединений стоит мало, а использования дополнительных компьютеров в этой схеме порождает слишком много дополнительных IO операций, что дороже.

    Если вы можете хотя бы дать намёк, как это сделать быстрее, я буду благодарен.
    Написано