Как вы ограничиваете скорость операции ввода-вывода?

StackOverflow https://stackoverflow.com/questions/94997

  •  01-07-2019
  •  | 
  •  

Вопрос

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

Это было полезно?

Решение

На прикладном уровне (используя API в стиле сокетов Berkeley) вы просто смотрите на часы и считываете или записываете данные со скоростью, которую вы хотите ограничить.

Если вы читаете в среднем всего 10 Кбит / с, но источник отправляет больше, то в конечном итоге все буферы между ним и вами заполнятся.TCP / IP допускает это, и протокол обеспечит замедление работы отправителя (на прикладном уровне, вероятно, все, что вам нужно знать, это то, что на другом конце блокирующие вызовы записи будут блокироваться, неблокирующая запись завершится ошибкой, а асинхронная запись не завершится, пока вы не прочитаете достаточно данных, чтобы разрешить это).

На прикладном уровне вы можете быть только приблизительными - вы не можете гарантировать жестких ограничений, таких как "не более 10 кб будет проходить через данную точку сети в любую секунду".Но если вы будете отслеживать то, что получили, то сможете получить правильное среднее значение в долгосрочной перспективе.

Другие советы

Предполагая сетевой транспорт, основанный на TCP / IP, пакеты отправляются в ответ на пакеты ACK / NACK, идущие другим путем.

Ограничивая скорость передачи пакетов, подтверждающих получение входящих пакетов, вы, в свою очередь, снизите скорость отправки новых пакетов.

Это может быть немного неточно, поэтому, возможно, оптимальным будет отслеживать скорость нисходящего потока и адаптивно регулировать скорость отклика до тех пор, пока она не опустится ниже комфортного порога.(Однако это произойдет очень быстро, вы отправляете количество подтверждений в секунду)

Это похоже на ограничение игры определенным количеством кадров в секунду.

extern int FPS;
....    
timePerFrameinMS = 1000/FPS;

while(1) {
time = getMilliseconds();
DrawScene();
time = getMilliseconds()-time;
if (time < timePerFrameinMS) {
   sleep(timePerFrameinMS - time);
}
}

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

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

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

Если вы действительно хотите читать только такое количество данных за раз, вы можете сделать что-то вроде этого:

ReadFixedRate() {
  while(Data_Exists()) {
    t = GetTime();
    ReadBlock();
    while(t + delay > GetTime()) {
      Delay()'
    }
  }
}

wget, похоже, управляет этим с помощью опции --limit-rate.Вот со справочной страницы:

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

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

Если вы хотите замедлить весь трафик, поступающий на компьютер, вам нужно пойти и настроить размеры ваших входящих буферов TCP.В Linux вы могли бы повлиять на это изменение, изменив значения в /proc/sys/net/ipv4/tcp_rmem (размеры буфера чтения памяти) и других файлах tcp_ *.

Чтобы добавить к ответу Бранана:

Если вы добровольно ограничите скорость чтения на стороне получателя, в конечном итоге очереди заполнятся на обоих концах.Затем отправитель либо заблокирует свой вызов send(), либо вернется из вызова send() с значением sent_length, меньшим ожидаемой длины, переданной вызову send().

Если отправитель не готов справиться с этим случаем, перейдя в спящий режим и пытаясь повторно отправить то, что не поместилось в буферы ОС, у вас в конечном итоге возникнут проблемы с подключением (отправитель может определить это как ошибку) или потеря данных (отправитель может неосознанно отбросить данные, которые не поместились в буферы ОС).

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top