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

Running very slow. #366

Open
dkeeney opened this issue Feb 27, 2020 · 12 comments
Open

Running very slow. #366

dkeeney opened this issue Feb 27, 2020 · 12 comments

Comments

@dkeeney
Copy link

dkeeney commented Feb 27, 2020

All messages passing from client to server and back are very slow....about a second for one round trip. These should be less than a millisecond. You don't need a time stamp to see the delay because it is obvious when you see the messages come up in the log display.

I am using version 0.5.5
I am running on Visual Studio 2019, x64, both debug and release modes.

To demonstrate this I wrote a pair of routines that you can use to see the problem by hacking up your client and server examples.

In one Command prompt window, run the server. Then in another Command prompt window run the client.

Here is the Client

//
//  client.cc
//
//  Copyright (c) 2019 Yuji Hirose. All rights reserved.
//  MIT License

#include <httplib.h>
#include <iostream>

using namespace std;

int main(void) {
  cout << "starting client." << endl;
  httplib::Client cli("localhost", 8050);

  // GET /hi
  for (size_t i = 0; i < 5; i++) {
    cout << "GET /hi" << endl;
    auto res = cli.Get("/hi");
    if (res) {
      cout << res->status << endl;
      cout << res->get_header_value("Content-Type") << endl;
      cout << res->body << endl;
    } else {
      cout << "crashed";
      return 1;
    }
  }
  //  POST /network
  std::string config = R"(
   {network: [
       {addRegion: {name: "encoder", type: "RDSERegion", params: {size: 1000, sparsity: 0.2, radius: 0.03, seed: 2019, noise: 0.01}}},
       {addRegion: {name: "sp", type: "SPRegion", params: {columnCount: 2048, globalInhibition: true}}},
       {addRegion: {name: "tm", type: "TMRegion", params: {cellsPerColumn: 8, orColumnOutputs: true}}},
       {addLink:   {src: "encoder.encoded", dest: "sp.bottomUpIn"}},
       {addLink:   {src: "sp.bottomUpOut", dest: "tm.bottomUpIn"}}
    ]})";

  cout << "POST /network" << endl;
  auto res = cli.Post("/network", config, "application/json");
  if (!res || res->status / 100 != 2) 
    cout << "Failed Response to POST /network request." << endl;
  else
    cout << res->body << endl;

  return 0;
}

Here is the server:

//
//  sample.cc
//
//  Copyright (c) 2019 Yuji Hirose. All rights reserved.
//  MIT License
//

#include <chrono>
#include <cstdio>
#include <iostream>
#include <httplib.h>

#define PORT 8050
using namespace httplib;

std::string dump_headers(const Headers &headers) {
  std::string s;
  char buf[BUFSIZ];

  for (auto it = headers.begin(); it != headers.end(); ++it) {
    const auto &x = *it;
    snprintf(buf, sizeof(buf), "%s: %s\n", x.first.c_str(), x.second.c_str());
    s += buf;
  }

  return s;
}

std::string log(const Request &req, const Response &res) {
  std::string s;
  char buf[BUFSIZ];

  s += "================================\n";

  snprintf(buf, sizeof(buf), "%s %s %s", req.method.c_str(),
           req.version.c_str(), req.path.c_str());
  s += buf;

  std::string query;
  for (auto it = req.params.begin(); it != req.params.end(); ++it) {
    const auto &x = *it;
    snprintf(buf, sizeof(buf), "%c%s=%s",
             (it == req.params.begin()) ? '?' : '&', x.first.c_str(),
             x.second.c_str());
    query += buf;
  }
  snprintf(buf, sizeof(buf), "%s\n", query.c_str());
  s += buf;

  s += dump_headers(req.headers);
  if (!req.body.empty()) { s += "Body: " + req.body; }

  s += "--------------------------------\n";

  snprintf(buf, sizeof(buf), "%d %s\n", res.status, res.version.c_str());
  s += buf;
  s += dump_headers(res.headers);
  s += "\n";

  if (!res.body.empty()) { s += "Body: " + res.body; }

  s += "\n";

  return s;
}

int main(void) {
  Server svr;

  if (!svr.is_valid()) {
    std::cout << "server has an error...\n";
    return -1;
  }

  svr.Get("/", [=](const Request & /*req*/, Response &res) {
    res.set_redirect("/hi");
  });

  // GET /hi
  svr.Get("/hi", [](const Request & /*req*/, Response &res) {
    res.set_content("Hello World!\n", "text/plain");
  });
  
  //  POST /network
  //  Configure a Network resource (with JSON configuration in POST body) ==> token
  svr.Post("/network", [&](const Request &req, Response &res, const ContentReader &content_reader) {
      content_reader([&](const char *data, size_t data_length) {
        res.body.append(data, data_length);
        return true;
      });
    // here is where I would normally create the resource.
    std::cout << "Creating resource /network with configuration: " << res.body << std::endl;
    std::string token = "0001";
    res.set_content(token+"\n", "text/plain");
  });

  svr.Get("/stop", [&](const Request & /*req*/, Response & /*res*/) { svr.stop(); });

  svr.set_error_handler([](const Request & /*req*/, Response &res) {
    const char *fmt = "<p>Error Status: <span style='color:red;'>%d</span></p>";
    char buf[BUFSIZ];
    snprintf(buf, sizeof(buf), fmt, res.status);
    res.set_content(buf, "text/html");
  });

  svr.set_logger([](const Request &req, const Response &res) {
    std::cout << log(req, res);
  });

  std::cout << "Starting Server." << std::endl;
  svr.listen("127.0.0.1", PORT);
  std::cout << "Server stopped." << std::endl;

  return 0;
}
@dkeeney
Copy link
Author

dkeeney commented Feb 27, 2020

I will run your benchmark and see what I get.

@yhirose
Copy link
Owner

yhirose commented Feb 27, 2020

@dkeeney, thank you for providing the detail. I build server and client from your source files with x64 setting and checked the performance on my Windows.

As you can see the following results, both client and server seem to perform as normal.
So It seems that my environment is missing something that your environment has...

Client :
(I run it in MSYS2 shell, because I wanted to use 'time' comment to measure the performance.)

[/d/Projects/cpp-httplib/example/x64/Release] (master)
$ time ./client
starting client.
GET /hi
200
text/plain
Hello World!

GET /hi
200
text/plain
Hello World!

GET /hi
200
text/plain
Hello World!

GET /hi
200
text/plain
Hello World!

GET /hi
200
text/plain
Hello World!

POST /network
0001


real    0m0.071s
user    0m0.000s
sys     0m0.015s

Server:

D:\Projects\cpp-httplib\example\x64\Release>server.exe
Starting Server.
================================
GET HTTP/1.1 /hi
Accept: */*
Connection: close
Content-Length: 0
Host: localhost:8050
REMOTE_ADDR: 127.0.0.1
User-Agent: cpp-httplib/0.5
--------------------------------
200 HTTP/1.1
Connection: close
Content-Length: 13
Content-Type: text/plain

Body: Hello World!

================================
GET HTTP/1.1 /hi
Accept: */*
Connection: close
Content-Length: 0
Host: localhost:8050
REMOTE_ADDR: 127.0.0.1
User-Agent: cpp-httplib/0.5
--------------------------------
200 HTTP/1.1
Connection: close
Content-Length: 13
Content-Type: text/plain

Body: Hello World!

================================
GET HTTP/1.1 /hi
Accept: */*
Connection: close
Content-Length: 0
Host: localhost:8050
REMOTE_ADDR: 127.0.0.1
User-Agent: cpp-httplib/0.5
--------------------------------
200 HTTP/1.1
Connection: close
Content-Length: 13
Content-Type: text/plain

Body: Hello World!

================================
GET HTTP/1.1 /hi
Accept: */*
Connection: close
Content-Length: 0
Host: localhost:8050
REMOTE_ADDR: 127.0.0.1
User-Agent: cpp-httplib/0.5
--------------------------------
200 HTTP/1.1
Connection: close
Content-Length: 13
Content-Type: text/plain

Body: Hello World!

================================
GET HTTP/1.1 /hi
Accept: */*
Connection: close
Content-Length: 0
Host: localhost:8050
REMOTE_ADDR: 127.0.0.1
User-Agent: cpp-httplib/0.5
--------------------------------
200 HTTP/1.1
Connection: close
Content-Length: 13
Content-Type: text/plain

Body: Hello World!

Creating resource /network with configuration:
   {network: [
       {addRegion: {name: "encoder", type: "RDSERegion", params: {size: 1000, sparsity: 0.2, radius: 0.03, seed: 2019, noise: 0.01}}},
       {addRegion: {name: "sp", type: "SPRegion", params: {columnCount: 2048, globalInhibition: true}}},
       {addRegion: {name: "tm", type: "TMRegion", params: {cellsPerColumn: 8, orColumnOutputs: true}}},
       {addLink:   {src: "encoder.encoded", dest: "sp.bottomUpIn"}},
       {addLink:   {src: "sp.bottomUpOut", dest: "tm.bottomUpIn"}}
    ]}
================================
POST HTTP/1.1 /network
Accept: */*
Connection: close
Content-Length: 502
Content-Type: application/json
Host: localhost:8050
REMOTE_ADDR: 127.0.0.1
User-Agent: cpp-httplib/0.5
--------------------------------
200 HTTP/1.1
Connection: close
Content-Length: 5
Content-Type: text/plain

Body: 0001

@dkeeney
Copy link
Author

dkeeney commented Feb 27, 2020

You are getting wonderful results.
So I must be doing something different.

When I access your server with the benchmark the results are good too:
0: 370 millisec.
1: 161 millisec.
2: 157 millisec.

I wonder if its a windows thing when both client and server are on the same machine.
Let me put in some timestamps on my client and see what it gets.

@dkeeney
Copy link
Author

dkeeney commented Feb 28, 2020

I ran a our Unit Test for the library we are building. This contains something like the tests I gave you but uses real data and real calls into our library. It starts the server on one thread and then the client in a second thread. The client connects to the server and exchanges messages to try out all of our REST interface.

Under Linux and it was really fast...all messages together was 127ms.
Under macOS it was 40ms.
Under windows the same test took 11330 ms

So I have something setup wrong someplace.

@dkeeney
Copy link
Author

dkeeney commented Feb 28, 2020

I don't know if this is a clue or not, but using my client/server test, on Windows with Visual Studio 2019, each message round trip is taking almost exactly 2000ms. They range from 2003ms to 2015ms.

I wonder if there is a 2 second timeout someplace....perhaps in the thread pool?
I will keep looking for clues.

@yhirose yhirose added the windows Windows specific topic label Feb 28, 2020
@PixlRainbow
Copy link
Contributor

Either that or the delay is coming from Windows attempting to resolve or redirect your connection somewhere else, failing and then falling back to local machine

@dkeeney
Copy link
Author

dkeeney commented Feb 28, 2020

Perhaps. But connection timeouts are a lot longer... I tried setting the client timeout to 30 seconds and it made no difference.

This would not be a DNS lookup delay because I am sending to "Localhost" which resolves locally.

I am still thinking thread pool or some sort of lock but I have no bases for that theory.

@dkeeney
Copy link
Author

dkeeney commented Feb 29, 2020

Found the problem ---
It was the DNS!!! sort of. The connect to "localhost" resolves to two addrinfo entries on my machine. The first is an IP6 address and the second is an IP4 address. The IP6 address does not seem to be fully configured (resolves to "1700:1f72::" rather than "::1") and after 2 seconds it gives up and tries the IP4 address (127.0.0.1) which works. So that is where the 2 second delay was coming from for each message. This was also happening on the GitHub server we are using for unit-tests when building for Windows 10. So this was not just my machine.

The fix then is to use "127.0.0.1" rather than "localhost" as the host in my client app when connecting to my own machine. Or go fix the IP6 configuration.

The problem had nothing to do with this software library.

@yhirose
Copy link
Owner

yhirose commented Mar 1, 2020

Glad to hear you found the solution.

@yhirose yhirose closed this as completed Mar 1, 2020
@15504050355
Copy link

Found the problem --- It was the DNS!!! sort of. The connect to "localhost" resolves to two addrinfo entries on my machine. The first is an IP6 address and the second is an IP4 address. The IP6 address does not seem to be fully configured (resolves to "1700:1f72::" rather than "::1") and after 2 seconds it gives up and tries the IP4 address (127.0.0.1) which works. So that is where the 2 second delay was coming from for each message. This was also happening on the GitHub server we are using for unit-tests when building for Windows 10. So this was not just my machine.

The fix then is to use "127.0.0.1" rather than "localhost" as the host in my client app when connecting to my own machine. Or go fix the IP6 configuration.

The problem had nothing to do with this software library.

you are genius, it help me a lot!!

@alexlyapin
Copy link

Found the problem --- It was the DNS!!! sort of. The connect to "localhost" resolves to two addrinfo entries on my machine. The first is an IP6 address and the second is an IP4 address. The IP6 address does not seem to be fully configured (resolves to "1700:1f72::" rather than "::1") and after 2 seconds it gives up and tries the IP4 address (127.0.0.1) which works. So that is where the 2 second delay was coming from for each message. This was also happening on the GitHub server we are using for unit-tests when building for Windows 10. So this was not just my machine.

The fix then is to use "127.0.0.1" rather than "localhost" as the host in my client app when connecting to my own machine. Or go fix the IP6 configuration.

The problem had nothing to do with this software library.

Saved my day! Got exactly the same ~2sec delay. Was driving me crazy. @yhirose maybe add some info in readme about using 127.0.0.1 vs localhost? I see that this problem arises for many people and it is far from obvious.

@yhirose yhirose added information and removed windows Windows specific topic labels Nov 16, 2024
@yhirose
Copy link
Owner

yhirose commented Nov 16, 2024

Keep it open with 'information', so that others can benefit from the solution.

@yhirose yhirose reopened this Nov 16, 2024
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

5 participants