Пытаюсь загрузить файл с помощью WinINet, в один поток все нормально. Когда делаю 2 и больше потоков, время загрузки увеличивается в 1.5-2 раза. Похоже я делаю что-то неправильно.
Действую примерно так:
1. Через _beginthread создаю поток и передаю указатель на экземпляр класса с веб-адресом, указателем на открытый файл и еще кое-чем.
2. В нем вызываю InternetOpen, InternetConnect, HttpOpenRequest, HttpSendRequest.
3. Далее в цикле функцией InternetSetFilePointer устанавливаю указатель на _нужное_место_, загружаю данные через InternetReadFile и пишу их в файл.
_нужное_место_ передасется в поток по указателю и после каждого InternetSetFilePointer увеличивается на объем буфера. Получается общий для всех потоков файловый указатель на место, которое еще не закачивается ни в одном потоке.
Файл открываю тоже снаружи и передаю указатель.
Запись в файл и вычисление _нужного_места_ производится в CriticalSection'ах.
Примеры с загрузкой в несколько потоков, которые нашел не осилил и, кажется, они принципиально другие.
Скажите, пожалуйста, в какую сторону думать. Примерно, какие функции и в каком порядке, дальше наверна сам разберусь. И с потоками, и с WinINet всего второй день разбираюсь, так что прошу сильно не ругаться, если очень криво сделал.
Подумал, что может быть разные потоки качают одни и те же фрагменты файла и на этом тратится время. Поэтому немного переделал.
Для работы с файлом написал отдельный класс, он выдает указатели, хранит информацию о том, какой поток чего качает и тд. Вся работа с ним производится в CriticalSection'ах. Ведется лог, из него я узнал, что повторных записей нет. Но есть такая странная штука.
Создал 8 потоков. Большую часть данных загружает 1 поток, примерно 120 из 130 записей в файл, еще 1 — 7/130 и 3 по 1/130. 3 потока не загружали ваще ничего!
Такое ощущение что WinINet создает какой-то 1 канал на всех и дает его каждому на какое-то время.
Время загрузки естессно не уменьшилось.
Все таки оказалось, что каждый поток выкачивает файл полностью и сохраняет себе куда-то кэш.
Как можно отключить этот кэш, чтобы InternetSetFilePointer работала? (в MSDN написано, что если в HttpOpenRequest исользовать ключи INTERNET_FLAG_DONT_CACHE и INTERNET_FLAG_NO_CACHE_WRITE, она поломается).
E>Такое ощущение что WinINet создает какой-то 1 канал на всех и дает его каждому на какое-то время.
Это не WinInet, а сетевая среда от вас до сервера. Поймите — физическая пропускная способность этой среды от вас до сервера ограничена.
Ваше количество нитей вообще никого не волнует, ибо сети оно неизвестно. Сети известно только количество коннектов, а не то, сколько нитей их обрабатывают.
Так вот, идея, что, открыв 8 коннектов вместо одного, вы получите 80 мегабит в секунду до сервера вместо 10 — неверна. Точнее, верна только тогда, когда на сервере стоят искусственные ограничения трафика (поконнектно).
Протокол TCP имеет довольно умный алгоритм адаптации ритма передачи к реальной пропускной способности среды, называется — slow start/congestion avoidance. Он достаточно умен, чтобы выжать максимум из одного коннекта, и создание 8 коннектов ничего не ускорит.
В некоторых протоколах на основе TCP — например, iSCSI — использование такого трюка с целью cheating the congestion avoidance algorithm прямо запрещено.
Реально я пробовал это в ReGet (сейчас я с него окончательно перешел на встроенный в ОС BITS) и на диалапе когда-то давно, и на DSL, результат был примерно такой — 5 коннектов дают 95% от скорости одного коннекта. Т.е. маленькое снижение.
Здравствуйте, Maxim S. Shatskih, Вы писали:
E>>Пытаюсь загрузить файл с помощью WinINet, в один поток все нормально. Когда делаю 2 и больше потоков, время загрузки увеличивается в 1.5-2 раза.
MSS>Все правильно, а почему вы вообще посчитали, что это ускорит работу?
Вообще, я надеялся, что скорость хотя бы не понизится.
Здравствуйте, Maxim S. Shatskih, Вы писали:
MSS>Так вот, идея, что, открыв 8 коннектов вместо одного, вы получите 80 мегабит в секунду до сервера вместо 10 — неверна. Точнее, верна только тогда, когда на сервере стоят искусственные ограничения трафика (поконнектно).
Именно на эти ограничения я и рассчитываю.
Большое спасибо за разъяснение.
В итоге по разным причинам решил перейти на libcurl.