Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need a way to change the CHUNK_SIZE value at runtime. #8

Open
infgeoax opened this issue Oct 26, 2015 · 5 comments
Open

Need a way to change the CHUNK_SIZE value at runtime. #8

infgeoax opened this issue Oct 26, 2015 · 5 comments

Comments

@infgeoax
Copy link

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
@ghost
Copy link

ghost commented Oct 26, 2015

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.

@kavyako
Copy link
Contributor

kavyako commented Oct 29, 2015

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.

@ghost
Copy link

ghost commented Oct 29, 2015

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.

@kavyako
Copy link
Contributor

kavyako commented Nov 2, 2015

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

@dpjudas
Copy link

dpjudas commented Feb 3, 2022

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants