2010-01-11 9 views
22

Tengo una prueba de rendimiento de servidor/cliente muy simple usando boost :: asio en Windows y parece estar funcionando muy mal. Espero que esté usando la biblioteca de forma incorrecta y agradecería cualquier consejo.Pobre impulso. Rendimiento de ASIO

Tengo una clase de sesión que escribe un mensaje de longitud y luego escribe un mensaje, y luego espera para leer un mensaje de longitud y luego leer un mensaje, y lo sigue haciendo una y otra vez sin parar. Sin embargo, cuando lo ejecuto localmente en mi propia computadora obtengo un rendimiento increíblemente rápido; cuando ejecuto un servidor en una computadora y un cliente en otra computadora, incluso en la misma red, el rendimiento se ralentiza, demorando tanto como 1 segundo para que ocurra una operación de lectura/escritura.

El archivo de código fuente del servidor es la siguiente:

#include <cstdlib> 
#include <iostream> 
#include <boost/asio.hpp> 
#include <boost/bind.hpp> 

using namespace boost; 
using namespace boost::asio; 
using namespace boost::asio::ip; 
using namespace std; 

class Session { 
    public: 

    Session(io_service& ioService) 
     : m_socket(ioService) {} 

    tcp::socket& GetSocket() { 
     return m_socket; 
    } 

    void StartRead() { 
     m_messageSizeIterator = reinterpret_cast<char*>(&m_messageSize); 
     async_read(m_socket, buffer(m_messageSizeIterator, sizeof(m_messageSize)), 
     bind(&Session::HandleSizeRead, this, placeholders::error, 
     placeholders::bytes_transferred)); 
    } 

    void StartWrite(const char* message, int messageSize) { 
     m_messageSize = messageSize; 
     m_message = new char[m_messageSize]; 
     memcpy(m_message, message, m_messageSize); 
     async_write(m_socket, buffer(&m_messageSize, sizeof(int)), 
     bind(&Session::HandleSizeWritten, this, placeholders::error)); 
    } 

    void HandleSizeRead(const system::error_code& error, 
     size_t bytes_transferred) { 
     if(!error) { 
     m_message = new char[m_messageSize]; 
     async_read(m_socket, buffer(m_message, m_messageSize), 
      bind(&Session::HandleMessageRead, this, placeholders::error, 
      placeholders::bytes_transferred)); 
     } else { 
     delete this; 
     } 
    } 

    void HandleMessageRead(const system::error_code& error, 
     size_t bytes_transferred) { 
     if(!error) { 
     cout << string(m_message, m_messageSize) << endl; 
     async_write(m_socket, buffer(&m_messageSize, sizeof(int)), 
      bind(&Session::HandleSizeWritten, this, placeholders::error)); 
     } else { 
     delete this; 
     } 
    } 

    void HandleSizeWritten(const system::error_code& error) { 
     if(!error) { 
     async_write(m_socket, buffer(m_message, m_messageSize), 
      bind(&Session::HandleMessageWritten, this, placeholders::error)); 
     } else { 
     delete this; 
     } 
    } 

    void HandleMessageWritten(const system::error_code& error) { 
     if(!error) { 
     delete m_message; 
     m_messageSizeIterator = reinterpret_cast<char*>(&m_messageSize); 
     async_read(m_socket, buffer(m_messageSizeIterator, 
      sizeof(m_messageSize)), bind(&Session::HandleSizeRead, this, 
      placeholders::error, placeholders::bytes_transferred)); 
     } else { 
     delete this; 
     } 
    } 

    private: 
    tcp::socket m_socket; 
    int m_messageSize; 
    char* m_messageSizeIterator; 
    char* m_message; 
}; 

class Server { 
    public: 

    Server(io_service& ioService, short port) 
     : m_ioService(ioService), 
      m_acceptor(ioService, tcp::endpoint(tcp::v4(), port)) { 
     Session* new_session = new Session(m_ioService); 
     m_acceptor.async_accept(new_session->GetSocket(), bind(&Server::HandleAccept, 
     this, new_session,asio::placeholders::error)); 
    } 

    void HandleAccept(Session* new_session, const system::error_code& error) { 
     if(!error) { 
     new_session->StartRead(); 
     new_session = new Session(m_ioService); 
     m_acceptor.async_accept(new_session->GetSocket(), bind(
      &Server::HandleAccept, this, new_session, placeholders::error)); 
     } else { 
     delete new_session; 
     } 
    } 

    private: 
    io_service& m_ioService; 
    tcp::acceptor m_acceptor; 
}; 

int main(int argc, char* argv[]) { 
    try { 
    if(argc != 2) { 
     cerr << "Usage: server <port>\n"; 
     return 1; 
    } 
    io_service io_service; 
    Server s(io_service, atoi(argv[1])); 
    io_service.run(); 
    } catch(std::exception& e) { 
    cerr << "Exception: " << e.what() << "\n"; 
    } 
    return 0; 
} 

Y el código de cliente es el siguiente:

#include <cstdlib> 
#include <cstring> 
#include <iostream> 
#include <boost/bind.hpp> 
#include <boost/asio.hpp> 

using namespace boost; 
using namespace boost::asio; 
using namespace boost::asio::ip; 
using namespace std; 

class Session { 
    public: 

    Session(io_service& ioService) 
     : m_socket(ioService) {} 

    tcp::socket& GetSocket() { 
     return m_socket; 
    } 

    void StartRead() { 
     m_messageSizeIterator = reinterpret_cast<char*>(&m_messageSize); 
     async_read(m_socket, buffer(m_messageSizeIterator, sizeof(m_messageSize)), 
     bind(&Session::HandleSizeRead, this, placeholders::error, 
     placeholders::bytes_transferred)); 
    } 

    void StartWrite(const char* message, int messageSize) { 
     m_messageSize = messageSize; 
     m_message = new char[m_messageSize]; 
     memcpy(m_message, message, m_messageSize); 
     async_write(m_socket, buffer(&m_messageSize, sizeof(int)), 
     bind(&Session::HandleSizeWritten, this, placeholders::error)); 
    } 

    void HandleSizeRead(const system::error_code& error, 
     size_t bytes_transferred) { 
     if(!error) { 
     m_message = new char[m_messageSize]; 
     async_read(m_socket, buffer(m_message, m_messageSize), 
      bind(&Session::HandleMessageRead, this, placeholders::error, 
      placeholders::bytes_transferred)); 
     } else { 
     delete this; 
     } 
    } 

    void HandleMessageRead(const system::error_code& error, 
     size_t bytes_transferred) { 
     if(!error) { 
     cout << string(m_message, m_messageSize) << endl; 
     async_write(m_socket, buffer(&m_messageSize, sizeof(int)), 
      bind(&Session::HandleSizeWritten, this, placeholders::error)); 
     } else { 
     delete this; 
     } 
    } 

    void HandleSizeWritten(const system::error_code& error) { 
     if(!error) { 
     async_write(m_socket, buffer(m_message, m_messageSize), 
      bind(&Session::HandleMessageWritten, this, placeholders::error)); 
     } else { 
     delete this; 
     } 
    } 

    void HandleMessageWritten(const system::error_code& error) { 
     if(!error) { 
     delete m_message; 
     m_messageSizeIterator = reinterpret_cast<char*>(&m_messageSize); 
     async_read(m_socket, buffer(m_messageSizeIterator, 
      sizeof(m_messageSize)), bind(&Session::HandleSizeRead, this, 
      placeholders::error, placeholders::bytes_transferred)); 
     } else { 
     delete this; 
     } 
    } 

    private: 
    tcp::socket m_socket; 
    int m_messageSize; 
    char* m_messageSizeIterator; 
    char* m_message; 
}; 

int main(int argc, char* argv[]) { 
    try { 
    if(argc != 3) { 
     cerr << "Usage: client <host> <port>\n"; 
     return 1; 
    } 
    io_service io_service; 
    tcp::resolver resolver(io_service); 
    tcp::resolver::query query(tcp::v4(), argv[1], argv[2]); 
    tcp::resolver::iterator iterator = resolver.resolve(query); 
    Session session(io_service); 
    tcp::socket& s = session.GetSocket(); 
    s.connect(*iterator); 
    cout << "Enter message: "; 
    const int MAX_LENGTH = 1024; 
    char request[MAX_LENGTH]; 
    cin.getline(request, MAX_LENGTH); 
    int requestLength = strlen(request); 
    session.StartWrite(request, requestLength); 
    io_service.run(); 
    } catch (std::exception& e) { 
    cerr << "Exception: " << e.what() << "\n"; 
    } 
    return 0; 
} 

Cualquier ayuda se agradece, gracias.


Para mis propósitos, el envío de mensajes realmente muy pequeñas y con ganas de respuestas en tiempo real virtuales, deshabilitar el algoritmo de Nagle resultó ser la causa de los malos resultados.

+0

¿Ha descartado las posibilidades de que algo que el enrutador podría estar causando el problema? ¿Cómo es el uso de la CPU en cada máquina? – bobber205

+0

El uso de la CPU es 0 en ambas máquinas. En cuanto al enrutador que es el problema, escribí un programa similar sin usar ASIO y funcionó muy rápido. – Kranar

+3

Comience probando su enlace usando iperf. Luego, instrumente todo el proceso: ¿se creó el zócalo? ¿El enlace tuvo éxito? ¿Funcionó la resolución? ¿Funcionó la conexión al servidor? ¿El primer envío funcionó? ¿El primero recibe trabajo? ¿Alguna llamada a la API de red devuelve algún error? ¿Tomó algo más de lo esperado? Mira el tráfico de la red. ¿Hay un firewall en el servidor? ¿Está habilitado el algoritmo de Nagle? ¿El servidor tardó mucho tiempo en responder? ¿Hay alguna demora que no esperaba en el código que no es de red en el cliente? – Permaquid

Respuesta

36

Debe apagar el Nagle algorithm. Llame:

m_socket.set_option(tcp::no_delay(true)); 

Donde corresponda para su código.

+0

+1 para sugerencia y para vincular en nagle en wiki. Sin embargo, me hubiera gustado ver una advertencia de que el cambio de Nagle probablemente reducirá el rendimiento general. – quixver

+2

@quixver No nagle dará lugar a más paquetes en la red si está enviando bytes con un pequeño espacio entre ellos. Un temporizador (es decir, Nagle) para unirse generará menos paquetes y, por lo tanto, mejorará el rendimiento general de la red. Esto es cierto para el tráfico de teclado interactivo (por ejemplo, telnet, ssh) y fue una gran parte del tráfico de Ethernet hace veinte años. Para la comunicación de programa a programa, Nagle conduce a un rendimiento general más bajo (como en el caso de la pregunta original) en lugar de un rendimiento superior. Ver, por ejemplo, que todo el mensaje se pasó a async_write(), por lo que no es necesario esperar para enviarlo. – janm

6

Para mis propósitos, enviar mensajes realmente muy pequeños y querer respuestas virtuales en tiempo real, deshabilitar el algoritmo de Nagle resultó ser la causa del bajo rendimiento.

Cuestiones relacionadas