sprinfall / webcc

Lightweight C++ HTTP client and server library based on Asio for embedding purpose.
GNU Lesser General Public License v3.0
269 stars 61 forks source link

Very slow response #18

Open MyraBaba opened 3 years ago

MyraBaba commented 3 years ago

Hi,

I am testing example/form_server both my Mac Pro and also raspi4 . its super slower than the python (fastapi) ? almost 10 times slower

Why is this ? am I missed something ?

Best

MyraBaba commented 3 years ago

Any idea @sprinfall

sprinfall commented 3 years ago

Any idea @sprinfall

Because the file data is all cached in the memory. What is the size of your test file?

MyraBaba commented 3 years ago

its 10K image file . almost 1 second for each post very slow. There should be something that missed . Test yourself please

MyraBaba commented 3 years ago

I am posting with curl for test :

curl -X POST "http://127.0.0.1:8000/predict/image" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "name=mmm" -F "file=@/Users/xxx/Charles_Bronson/Charles_Bronson_0003.jpg;type=image/jpg"

MyraBaba commented 3 years ago

@sprinfall if you advise I would love to use your solution in our small non profit org. production.

Best

sprinfall commented 3 years ago

@MyraBaba Please feel free to use. I just pushed the code with a small improvement for the parsing of form data. I'm still investigating the performance issue.

MyraBaba commented 3 years ago

Ok I will test the new code . But you wll see the slowness of the response.. Something stalling / delaying.

Best

sprinfall commented 3 years ago

Ok I will test the new code . But you wll see the slowness of the response.. Something stalling / delaying.

Best

Hi, I use Python Requests library to post a JPG file, the 1s delay disappears. Just for your information.

BTW, if you want to post large files, please consider to NOT use multipart form data. Please use a normal post with streaming instead.

MyraBaba commented 3 years ago

Hi @sprinfall

My files mostly around 10K to 30K so small ones. What is the curl post and python post differences. ? Would you mind to share your python / Post code?

Best

sprinfall commented 3 years ago

@MyraBaba

import requests

url = 'http://127.0.0.1:8000/upload'
files = {'file': open('path/to/jpg', 'rb')}

r = requests.post(url, files=files)
print(r.text)
MyraBaba commented 3 years ago

@sprinfall can you test in aloop ?

for i in range(100): r = requests.post(url, files=files) print(i , " " ,r.text)

gives error

sprinfall commented 3 years ago

@MyraBaba Sorry, that issue was introduced by my last small optimization. I have reverted the change. I will keep you informed when I find a better solution to optimize the performance of form data parsing.

MyraBaba commented 3 years ago

@sprinfall any perf fix?

sprinfall commented 3 years ago

@sprinfall any perf fix?

I have two suggestions:

  1. Use a larger buffer to read the requests. I tested using 10240, it did improve the speed. Please see new added method of Server: set_buffer_size().
  2. Set log level to USER to avoid logging VERB and INFO logs.
MyraBaba commented 3 years ago

I will test right today and let you know. Best

MyraBaba commented 3 years ago

Hi @sprinfall

I tested with :

import requests
from time import sleep
url = 'http://127.0.0.1:8000/upload'
files = {'file': open('/Users/tulpar/Projects/build-drogon-Desktop_Qt_5_13_2_clang_64bit-Debug/Charles_Bronson_0003.jpg', 'rb') , 'name':"ALALU"}
i= 99

for i in range(100):
    r = requests.post(url, files=files)
    print(i , "    " ,r.text)

Got errors:

form parts: 2 name: file name: name 2020-12-21 13:38:50.697, WARN, main, connection.cc, 44, Socket shutdown error (Socket is not connected). 2020-12-21 13:38:57.077, ERRO, main, request_parser.cc, 131, Invalid part data. 2020-12-21 13:38:57.077, ERRO, main, connection.cc, 114, Failed to parse HTTP request. 2020-12-21 13:38:57.077, ERRO, main, connection.cc, 101, Socket read error (Bad file descriptor). 2020-12-21 13:38:57.077, WARN, main, connection.cc, 44, Socket shutdown error (Bad file descriptor). 2020-12-21 13:39:17.295, ERRO, main, request_parser.cc, 131, Invalid part data. 2020-12-21 13:39:17.295, ERRO, main, connection.cc, 114, Failed to parse HTTP request. 2020-12-21 13:39:17.296, ERRO, main, connection.cc, 101, Socket read error (Bad file descriptor). 2020-12-21 13:39:17.296, WARN, main, connection.cc, 44, Socket shutdown error (Bad file descriptor). 2020-12-21 13:39:36.575, ERRO, main, request_parser.cc, 131, Invalid part data. 2020-12-21 13:39:36.575, ERRO, main, connection.cc, 114, Failed to parse HTTP request. 2020-12-21 13:39:36.576, ERRO, main, connection.cc, 101, Socket read error (Bad file descriptor). 2020-12-21 13:39:36.576, WARN, main, connection.cc, 44, Socket shutdown error (Bad file descriptor).

sprinfall commented 3 years ago

I will look into it tonight.

sprinfall commented 3 years ago

@MyraBaba I found that from the second request in the loop, the file data sent was empty. Please try to move the files definition into inside of the loop. Meanwhile, I made a quickfix to handle this empty form part data. Please pull the code. But I will do more test tomorrow.

MyraBaba commented 3 years ago

@sprinfall Hi again,

I love the webcc its fantastic work. Thanks.

Do you think that we can embed it to our Qt Project ? Do you Qt ? It will be great if we get data as signal/slot mechanism

MyraBaba commented 3 years ago

@sprinfall do you know Qt platform ?

sprinfall commented 3 years ago

@sprinfall Hi again,

I love the webcc its fantastic work. Thanks.

Do you think that we can embed it to our Qt Project ? Do you Qt ? It will be great if we get data as signal/slot mechanism

For signal/slot mechanism, you have to use QNetworkAccessManager: https://doc.qt.io/qt-5/qnetworkaccessmanager.html But it only has client API. Is your Qt project a client or server?

MyraBaba commented 3 years ago

Hi,

I wrote the Qt forum but not have. Complete answer : https://forum.qt.io/topic/122927/c-rest-api-3rd-party-integration-question/8 https://forum.qt.io/topic/122927/c-rest-api-3rd-party-integration-question/8

BC

PS: QNetworkAccessManager is ok for doing REST SERVEr?

On 21 Jan 2021, at 04:45, Chunting Gu notifications@github.com wrote:

@sprinfall https://github.com/sprinfall Hi again,

I love the webcc its fantastic work. Thanks.

Do you think that we can embed it to our Qt Project ? Do you Qt ? It will be great if we get data as signal/slot mechanism

For signal/slot mechanism, you have to use QNetworkAccessManager: https://doc.qt.io/qt-5/qnetworkaccessmanager.html https://doc.qt.io/qt-5/qnetworkaccessmanager.html — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sprinfall/webcc/issues/18#issuecomment-764171103, or unsubscribe https://github.com/notifications/unsubscribe-auth/AEFRZH7ROCBGM6YMQ2QM273S26BM5ANCNFSM4UIFZJUQ.

MyraBaba commented 3 years ago

Hi,

I tried to use your server rest example in the Qt . Could find a way to integrate with signal slot .. It is creating a new FileUploadView instance inside the Qt thread so can’t access the signal cut emit the received data to the main thread.

Yes I am a newbie c++ world. Most mathematician algorithm person . If you show me a sample ie one is suggested "to pass the shared pointer to the start_Rest() function and therefore you can do the connection from where you call this function.”

BEst

include "restapi.h"

include

RestApi::RestApi(QObject *parent) : QObject(parent) { qDebug() << "xx restapi.cc REST API started " << endl;

}

void RestApi::start_Rest() {

int PORT = 7000;
WEBCC_LOG_INIT("", webcc::LOG_CONSOLE);

std::uint16_t port = static_cast<std::uint16_t>(PORT);

try {
    webcc::Server server{ boost::asio::ip::tcp::v4(), port };

    server.set_buffer_size(webcc::kBufferSize * 10);

    server.Route("/upload", std::make_shared<RestApi>(), { "POST" });

    server.Run();

} catch (const std::exception& e) {
    std::cerr << e.what() << std::endl;

// return 1; } }

webcc::ResponsePtr RestApi::Handle(webcc::RequestPtr request) {

if (request->method() == "POST") {
    return Post(request);
}

return {};

}

webcc::ResponsePtr RestApi::Post(webcc::RequestPtr request) { emit test_signal (); std::cout << "form parts: " << request->form_parts().size() << std::endl; std::string resp ; for (auto& part : request->form_parts()) { std::cout << "name: " << part->name() << std::endl; resp = part->name(); if (part->file_name().empty()) { std::cout << "data: " << part->data() << std::endl; } else { size_t size=part->data().size();

        if(true){
            std::ofstream myFile ("/Users/tulpar/Projects/webcc-21Dec/build/webccData-QT"+ std::to_string (image_counter++) +".jpeg", std::ios::out | std::ios::binary);
            myFile << part->data();

            myFile.close ();
            QByteArray byteArray(part->data().c_str(), part->data().length());

            QPixmap img;
            img.loadFromData (byteArray);
            RestApi::sendPix (img);
        }

        // Save part->data() as binary to file.
        // ...
    }
}

return webcc::ResponseBuilder{}.Created().Body(" ..  OK " + resp)();

}

void RestApi::sendPix(QPixmap &pix) { emit rest_image_received (pix); }

On 21 Jan 2021, at 04:45, Chunting Gu notifications@github.com wrote:

@sprinfall https://github.com/sprinfall Hi again,

I love the webcc its fantastic work. Thanks.

Do you think that we can embed it to our Qt Project ? Do you Qt ? It will be great if we get data as signal/slot mechanism

For signal/slot mechanism, you have to use QNetworkAccessManager: https://doc.qt.io/qt-5/qnetworkaccessmanager.html https://doc.qt.io/qt-5/qnetworkaccessmanager.html — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sprinfall/webcc/issues/18#issuecomment-764171103, or unsubscribe https://github.com/notifications/unsubscribe-auth/AEFRZH7ROCBGM6YMQ2QM273S26BM5ANCNFSM4UIFZJUQ.

sprinfall commented 3 years ago

@MyraBaba

  1. Run the REST server in a thread in your Qt application (see the server_states.cc example).
  2. In RestApi (the View), emit signals of some global (or Singleton) QObject after the requests have been handled (in your case, images are uploaded).
  3. In your Qt widgets, connect the signals of that global (or Singleton) object to some slots. I will come back to you later.
MyraBaba commented 3 years ago

Hi,

@sprinfall

Here a simple Thread example.

İf its work in Thread and result is usable via Qt signals very helpful for the community.

Best

SimpleRestWebcc.zip

On 21 Jan 2021, at 14:48, Chunting Gu notifications@github.com wrote:

@MyraBaba https://github.com/MyraBaba Run the REST server in a thread in your Qt application (see the server_states.cc example). In RestApi (the View), emit signals of some global (or Singleton) QObject after the requests have been handled (in your case, images are uploaded). In your Qt widgets, connect the signals of that global (or Singleton) object to some slots. I will come back to you later. — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/sprinfall/webcc/issues/18#issuecomment-764589441, or unsubscribe https://github.com/notifications/unsubscribe-auth/AEFRZHYCKTI5FCDLICC4YX3S3AIAPANCNFSM4UIFZJUQ.

sprinfall commented 3 years ago

@MyraBaba Just added an example for Qt based on my understanding: example/qt_app_server. Hope it helps.

MyraBaba commented 3 years ago

@sprinfall Thanks a lot. I tested.

Now I try to put same way to in my QTHREAD logic. Do you think it is also work in the QTHREAD created by the mainwindow ?

Best

MyraBaba commented 3 years ago

@sprinfall Thanks a lot .

what is the proper way to pass below objects which I can use inside the


`webcc::ResponsePtr Handle(webcc::RequestPtr request) override {

}`

the objects :


`. bc::Landmarker *Landarker = new bc::Landmarker;
    bc::facenet *Recognizer = new bc::facenet;
    bc::Aligner *Aligner          = new bc::Aligner;
    Recognizer->Init("/home/alp2080/Projects//data/models");
    Landarker->Init("/home/alp2080/Projects//data/models");

`

how can I do this to use also inside webcc::ResponsePtr Handle(webcc::RequestPtr request) override ?

whenever I start to think I am something at c++ always hitting such a wall.

Appreciate your help.

I mentioned and advise your webcc in Qt forum.

Best

sprinfall commented 3 years ago

@MyraBaba You want to access Landmarker, Facenet and Aligner from the overridden Handle() method, right? These objects look like some algorithm based on pre-trained models. So I think they are shared by all requests and related to each other. I suggest you to put them into a new class and create a single global variable (or singleton) of this class, then you can access it from everywhere. You should initialize this new global variable in your main() function.

sprinfall commented 3 years ago

@sprinfall Thanks a lot. I tested.

Now I try to put same way to in my QTHREAD logic. Do you think it is also work in the QTHREAD created by the mainwindow ?

Best

I suggest to use C++ std::thread to run the HTTP server. QThread is quite a different thing. It has extra event loop? So, keep your Views (sub-classes of webcc::View) away from QObject. Do the server part in pure C++ way. Emit signals, if necessary, from some other QObject for notifying the GUI thread.