使用 boost::asio::ip::tcp::iostream 的低带宽性能
Low bandwidth performance using boost::asio::ip::tcp::iostream
我写了一个小测试程序,使用boost::asio::ip::tcp::iostream
传输大约38MiB的数据:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/asio.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
using namespace std;
class Message {
public:
Message() {
}
virtual ~Message() {
}
string text;
std::vector<int> bigLoad;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar &text;
ar &bigLoad;
}
};
BOOST_CLASS_EXPORT(Message)
void runClient() {
// Give server time to startup
this_thread::sleep_for(chrono::milliseconds(3000));
boost::asio::ip::tcp::iostream stream("127.0.0.1", "3000");
// const boost::asio::ip::tcp::no_delay option(true);
// stream.rdbuf()->set_option(option);
Message message;
stringstream ss;
ss << "Hello World!";
message.text = ss.str();
int items = 10000000;
int size = sizeof(int) * items;
std::cout << "Size in Byte = " << size << endl;
std::cout << "Size in KiB = " << size / 1024 << endl;
std::cout << "Size in MiB = " << size / 1024 / 1024 << endl;
for (int i = 0; i < items; i++)
message.bigLoad.push_back(i);
boost::archive::text_oarchive archive(stream);
cout << "Client start to send message" << endl;
try {
archive << message;
} catch (std::exception &ex) {
cout << ex.what() << endl;
}
cout << "Client send message" << endl;
stream.close();
cout << "Client shutdown" << endl;
}
void handleIncommingClientConnection(boost::asio::ip::tcp::acceptor &acceptor) {
boost::asio::ip::tcp::iostream stream;
// const boost::asio::ip::tcp::no_delay option(true);
// stream.rdbuf()->set_option(option);
acceptor.accept(*stream.rdbuf());
boost::archive::text_iarchive archive(stream);
while (true) {
try {
Message message;
archive >> message;
cout << message.text << endl;
} catch (std::exception &ex) {
cout << ex.what() << endl;
if (stream.eof()) {
cout << "eof" << endl;
stream.close();
cout << "Server: shutdown client handling..." << endl;
break;
} else
throw ex;
}
}
}
void runServer() {
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 3000);
boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
handleIncommingClientConnection(acceptor);
}
template <typename TimeT = std::chrono::milliseconds>
struct measure {
template <typename F, typename... Args>
static typename TimeT::rep execution(F &&func, Args &&... args) {
auto start = std::chrono::steady_clock::now();
std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
auto duration = std::chrono::duration_cast<TimeT>(std::chrono::steady_clock::now() - start);
return duration.count();
}
};
void doIt() {
thread clientThread(runClient);
thread serverThread(runServer);
clientThread.join();
serverThread.join();
}
int main(int argc, char **argv) {
std::cout << measure<std::chrono::seconds>::execution(doIt) << std::endl;
return 0;
}
程序在发布模式下的输出如下所示:
Size in Byte = 40000000
Size in KiB = 39062
Size in MiB = 38
Client start to send message
Client send message
Client shutdown
Hello World!
input stream error
eof
Server: shutdown client handling...
148
传输 38 MB 用了 148 秒(超过 2 分钟)。我可以将数据复制到 USB 记忆棒并手动移交,这比 boost::asio
更快。
有什么方法可以提高带宽性能吗?
您的时间可能浪费在序列化 to/from 文本上。
对我来说,删除二进制存档确实将速度从 80Mbit/s 提高到 872MBit/s:
Client start to send message
Client send message
Client shutdown
Received: Hello World!
3
以秒为单位的总时间减少到3s,刚好是初始睡眠:)
概念验证 Live On Coliru
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/asio.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
using namespace std;
class Message {
public:
Message() {}
virtual ~Message() {}
string text;
std::vector<int> bigLoad;
private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &ar, const unsigned int /*version*/) {
ar & text & bigLoad;
}
};
BOOST_CLASS_EXPORT(Message)
void runClient() {
// Give server time to startup
this_thread::sleep_for(chrono::seconds(1));
boost::asio::ip::tcp::iostream stream("127.0.0.1", "3000");
const boost::asio::ip::tcp::no_delay option(false);
stream.rdbuf()->set_option(option);
Message message;
stringstream ss;
ss << "Hello World!";
message.text = ss.str();
int items = 8 << 20;
for (int i = 0; i < items; i++)
message.bigLoad.push_back(i);
boost::archive::binary_oarchive archive(stream);
cout << "Client start to send message" << endl;
try {
archive << message;
} catch (std::exception &ex) {
cout << ex.what() << endl;
}
cout << "Client send message" << endl;
stream.close();
cout << "Client shutdown" << endl;
}
void handleIncommingClientConnection(boost::asio::ip::tcp::acceptor &acceptor) {
boost::asio::ip::tcp::iostream stream;
// const boost::asio::ip::tcp::no_delay option(false);
// stream.rdbuf()->set_option(option);
acceptor.accept(*stream.rdbuf());
boost::archive::binary_iarchive archive(stream);
{
try {
Message message;
archive >> message;
cout << "Received: " << message.text << endl;
} catch (std::exception &ex) {
cout << ex.what() << endl;
if (stream.eof()) {
cout << "eof" << endl;
stream.close();
cout << "Server: shutdown client handling..." << endl;
return;
} else
throw;
}
}
}
void runServer() {
using namespace boost::asio;
using ip::tcp;
io_service ios;
tcp::endpoint endpoint = tcp::endpoint(tcp::v4(), 3000);
tcp::acceptor acceptor(ios, endpoint);
handleIncommingClientConnection(acceptor);
}
template <typename TimeT = std::chrono::milliseconds> struct measure {
template <typename F, typename... Args> static typename TimeT::rep execution(F &&func, Args &&... args) {
auto start = std::chrono::steady_clock::now();
std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
auto duration = std::chrono::duration_cast<TimeT>(std::chrono::steady_clock::now() - start);
return duration.count();
}
};
void doIt() {
thread clientThread(runClient);
thread serverThread(runServer);
clientThread.join();
serverThread.join();
}
int main() { std::cout << measure<std::chrono::seconds>::execution(doIt) << std::endl; }
注意:
有一件事是 "lost",旧版本的代码也没有真正支持它:直接接收多个档案。
您可能想要安装某种成帧协议。参见示例
- Boost Serialization Binary Archive giving incorrect output
- Outputting more things than a Polymorphic Text Archive
- 流不是存档 (http://www.boost.org/doc/libs/1_51_0/libs/serialization/doc/)
我在这里发表了很多 "overhead of Boost Serialization" 篇文章:
- how to do performance test using the boost library for a custom library
- Boost C++ Serialization overhead
我写了一个小测试程序,使用boost::asio::ip::tcp::iostream
传输大约38MiB的数据:
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/asio.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
using namespace std;
class Message {
public:
Message() {
}
virtual ~Message() {
}
string text;
std::vector<int> bigLoad;
private:
friend class boost::serialization::access;
template <class Archive>
void serialize(Archive &ar, const unsigned int version) {
ar &text;
ar &bigLoad;
}
};
BOOST_CLASS_EXPORT(Message)
void runClient() {
// Give server time to startup
this_thread::sleep_for(chrono::milliseconds(3000));
boost::asio::ip::tcp::iostream stream("127.0.0.1", "3000");
// const boost::asio::ip::tcp::no_delay option(true);
// stream.rdbuf()->set_option(option);
Message message;
stringstream ss;
ss << "Hello World!";
message.text = ss.str();
int items = 10000000;
int size = sizeof(int) * items;
std::cout << "Size in Byte = " << size << endl;
std::cout << "Size in KiB = " << size / 1024 << endl;
std::cout << "Size in MiB = " << size / 1024 / 1024 << endl;
for (int i = 0; i < items; i++)
message.bigLoad.push_back(i);
boost::archive::text_oarchive archive(stream);
cout << "Client start to send message" << endl;
try {
archive << message;
} catch (std::exception &ex) {
cout << ex.what() << endl;
}
cout << "Client send message" << endl;
stream.close();
cout << "Client shutdown" << endl;
}
void handleIncommingClientConnection(boost::asio::ip::tcp::acceptor &acceptor) {
boost::asio::ip::tcp::iostream stream;
// const boost::asio::ip::tcp::no_delay option(true);
// stream.rdbuf()->set_option(option);
acceptor.accept(*stream.rdbuf());
boost::archive::text_iarchive archive(stream);
while (true) {
try {
Message message;
archive >> message;
cout << message.text << endl;
} catch (std::exception &ex) {
cout << ex.what() << endl;
if (stream.eof()) {
cout << "eof" << endl;
stream.close();
cout << "Server: shutdown client handling..." << endl;
break;
} else
throw ex;
}
}
}
void runServer() {
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 3000);
boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
handleIncommingClientConnection(acceptor);
}
template <typename TimeT = std::chrono::milliseconds>
struct measure {
template <typename F, typename... Args>
static typename TimeT::rep execution(F &&func, Args &&... args) {
auto start = std::chrono::steady_clock::now();
std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
auto duration = std::chrono::duration_cast<TimeT>(std::chrono::steady_clock::now() - start);
return duration.count();
}
};
void doIt() {
thread clientThread(runClient);
thread serverThread(runServer);
clientThread.join();
serverThread.join();
}
int main(int argc, char **argv) {
std::cout << measure<std::chrono::seconds>::execution(doIt) << std::endl;
return 0;
}
程序在发布模式下的输出如下所示:
Size in Byte = 40000000
Size in KiB = 39062
Size in MiB = 38
Client start to send message
Client send message
Client shutdown
Hello World!
input stream error
eof
Server: shutdown client handling...
148
传输 38 MB 用了 148 秒(超过 2 分钟)。我可以将数据复制到 USB 记忆棒并手动移交,这比 boost::asio
更快。
有什么方法可以提高带宽性能吗?
您的时间可能浪费在序列化 to/from 文本上。
对我来说,删除二进制存档确实将速度从 80Mbit/s 提高到 872MBit/s:
Client start to send message
Client send message
Client shutdown
Received: Hello World!
3
以秒为单位的总时间减少到3s,刚好是初始睡眠:)
概念验证 Live On Coliru
#include <boost/archive/binary_iarchive.hpp>
#include <boost/archive/binary_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/asio.hpp>
#include <boost/serialization/export.hpp>
#include <boost/serialization/shared_ptr.hpp>
#include <boost/serialization/vector.hpp>
#include <chrono>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
using namespace std;
class Message {
public:
Message() {}
virtual ~Message() {}
string text;
std::vector<int> bigLoad;
private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &ar, const unsigned int /*version*/) {
ar & text & bigLoad;
}
};
BOOST_CLASS_EXPORT(Message)
void runClient() {
// Give server time to startup
this_thread::sleep_for(chrono::seconds(1));
boost::asio::ip::tcp::iostream stream("127.0.0.1", "3000");
const boost::asio::ip::tcp::no_delay option(false);
stream.rdbuf()->set_option(option);
Message message;
stringstream ss;
ss << "Hello World!";
message.text = ss.str();
int items = 8 << 20;
for (int i = 0; i < items; i++)
message.bigLoad.push_back(i);
boost::archive::binary_oarchive archive(stream);
cout << "Client start to send message" << endl;
try {
archive << message;
} catch (std::exception &ex) {
cout << ex.what() << endl;
}
cout << "Client send message" << endl;
stream.close();
cout << "Client shutdown" << endl;
}
void handleIncommingClientConnection(boost::asio::ip::tcp::acceptor &acceptor) {
boost::asio::ip::tcp::iostream stream;
// const boost::asio::ip::tcp::no_delay option(false);
// stream.rdbuf()->set_option(option);
acceptor.accept(*stream.rdbuf());
boost::archive::binary_iarchive archive(stream);
{
try {
Message message;
archive >> message;
cout << "Received: " << message.text << endl;
} catch (std::exception &ex) {
cout << ex.what() << endl;
if (stream.eof()) {
cout << "eof" << endl;
stream.close();
cout << "Server: shutdown client handling..." << endl;
return;
} else
throw;
}
}
}
void runServer() {
using namespace boost::asio;
using ip::tcp;
io_service ios;
tcp::endpoint endpoint = tcp::endpoint(tcp::v4(), 3000);
tcp::acceptor acceptor(ios, endpoint);
handleIncommingClientConnection(acceptor);
}
template <typename TimeT = std::chrono::milliseconds> struct measure {
template <typename F, typename... Args> static typename TimeT::rep execution(F &&func, Args &&... args) {
auto start = std::chrono::steady_clock::now();
std::forward<decltype(func)>(func)(std::forward<Args>(args)...);
auto duration = std::chrono::duration_cast<TimeT>(std::chrono::steady_clock::now() - start);
return duration.count();
}
};
void doIt() {
thread clientThread(runClient);
thread serverThread(runServer);
clientThread.join();
serverThread.join();
}
int main() { std::cout << measure<std::chrono::seconds>::execution(doIt) << std::endl; }
注意:
有一件事是 "lost",旧版本的代码也没有真正支持它:直接接收多个档案。
您可能想要安装某种成帧协议。参见示例
- Boost Serialization Binary Archive giving incorrect output
- Outputting more things than a Polymorphic Text Archive
- 流不是存档 (http://www.boost.org/doc/libs/1_51_0/libs/serialization/doc/)
我在这里发表了很多 "overhead of Boost Serialization" 篇文章:
- how to do performance test using the boost library for a custom library
- Boost C++ Serialization overhead