cpprestsdk
cpprestsdk copied to clipboard
Need a way to change the CHUNK_SIZE value at runtime.
Or am I using cpprest in the wrong way?
Our application normally transfers data (using HTTP GET) of several hundred megabytes, the default 64 Kb chunk size seems too small for optimal download rate. Changing the value to 5 Mb can reduce download time of 2 Gb data from 2minutes to 28 seconds.
The demo code, which just allocates the requested data in-memory and send them:
#include <Windows.h>
#include <cpprest/http_listener.h>
#include <cpprest/json.h>
#include <cpprest/streams.h>
#include <cpprest/filestream.h>
#include <cpprest/producerconsumerstream.h>
#include <algorithm>
#include <chrono>
#include <iostream>
#include <string>
using namespace concurrency::streams;
using namespace web;
using namespace http;
using namespace http::experimental::listener;
int main(int argc, char *argv[]) {
http_listener listener(L"http://*:8080/bytes");
listener.support(methods::GET, [](http_request &request) {
auto q = web::uri::split_query(request.request_uri().query());
// default: 100 MB of data
std::size_t bytes_to_write = 100 * 1048576;
if (q.find(L"b") != std::end(q)) {
bytes_to_write = std::stoul(q[L"b"]);
}
if (q.find(L"kb") != std::end(q)) {
bytes_to_write = std::stoul(q[L"kb"]) * 1024;
}
if (q.find(L"mb") != std::end(q)) {
bytes_to_write = std::stoul(q[L"mb"]) * 1024 * 1024;
}
request.reply(status_codes::OK, std::string(bytes_to_write, '+'));
std::cout << "Sent " << bytes_to_write << "bytes\n";
});
listener.open().wait();
std::wcout << "Listening on " << listener.uri().port() << std::endl;
while (true) {
try {
Sleep(1);
}
catch (...) {
break;
}
}
listener.close().wait();
return 0;
}
And using curl for testing:
curl -o NUL http://localhost:8080/bytes?mb=2000
Using 64Kb chunk size:
[root@localhost ~]# curl -o NUL 10.50.10.51:8080/bytes?mb=2000
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2000M 100 2000M 0 0 15.7M 0 0:02:06 0:02:06 --:--:-- 14.8M
Using 5Mb chunck size:
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2000M 100 2000M 0 0 69.2M 0 0:00:28 0:00:28 --:--:-- 77.1M
I brought this up in the old discussion forums too.
CHUNK_SIZE as currently a #define, but could the code be changed to use that #define as the default value to a data member, which is in turn used by the code and exposed to developers through access methods? That way, we can use this to throttle (or un-throttle) network usage.
Alternatively, if you changed windows_request_context's methods: transmit_body() and read_request_body_chunk() to virtual methods, we can create a derived class to use our own chunk value data member.
You could also expose the chunk_size option in http_listener_config class. This is how chunk size is provided as an option on the http_client.
Our goal is to stay uniform across all the platforms that we support. So ideally we would prefer solutions that can be implemented across the board.
The discussion thread on codeplex also has some details https://casablanca.codeplex.com/discussions/646631
Thanks Kavya.
I'll see if we can allocate some time for me to do this change in the current (or next) milestone. It's definitely something worth investing time in.
hey wongpsw
Please note, to contribute to the project, you will need to sign our contributor license agreement (CLA). Once you've signed you can email it directly to me at kavyako at Microsoft dot com or askcasablanca at Microsoft dot com.
Thanks Kavya
I believe the correct fix to this issue is actually to add the HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA flag to the HttpSendResponse and HttpSendResponseEntityBody calls in http_server_httpsys.cpp. Adding this flag increased the throughput from 15 mbit/s to 300 mbit/s for my test transfer with a 40 ms ping to the server.
This flag makes http.sys automatically size the send buffer based on the ideal send backlog (ISB). To quote the MSDN documentation for SIO_IDEAL_SEND_BACKLOG_CHANGE:
"When sending data over a TCP connection using Windows sockets, it is important to keep a sufficient amount of data outstanding (sent but not acknowledged yet) in TCP in order to achieve the highest throughput. The ideal value for the amount of data outstanding to achieve the best throughput for the TCP connection is called the ideal send backlog (ISB) size. The ISB value is a function of the bandwidth-delay product of the TCP connection and the receiver's advertised receive window (and partly the amount of congestion in the network)."
Or in other words, manually adjusting the chunk size will never completely work as you can't predict the ideal size. Luckily we can opt-in to the kernel managing this for us by adding the HTTP_SEND_RESPONSE_FLAG_BUFFER_DATA flag.