Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# New Video - https://youtu.be/wP1wz6MhxcI
# New Video - https://youtu.be/5-sDEDBJPlY

[<img src="assets/264.png?raw=true">](https://youtu.be/wP1wz6MhxcI)
[<img src="assets/265.png?raw=true">](https://youtu.be/5-sDEDBJPlY)

# Consulting

Expand Down
Binary file removed assets/264.png
Binary file not shown.
Binary file added assets/265.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,4 @@
- [258 - Redis vs Valkey performance](../lessons/258)
- [259 - Rust vs C++ Performance: Can Rust Actually Be Faster?](../lessons/259)
- [260 - ZeroMQ vs Aeron: Best for Market Data? Performance (Latency & Throughput)](../lessons/260)
- [265 - Rust vs C++ Performance: Can Rust Actually Be Faster? (Pt. 2)](../lessons/265)
14 changes: 14 additions & 0 deletions lessons/265/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Rust vs C++ Performance: Can Rust Actually Be Faster? (Pt. 2)

You can find tutorial [here](https://youtu.be/5-sDEDBJPlY).

## Commands

```bash
## C++
cmake --preset default -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS_RELEASE="-O3 -ffast-math"
cmake --build build && ./build/app

## Rust
cargo build --release
```
56 changes: 56 additions & 0 deletions lessons/265/app-cpp/.clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
Language: Cpp
BasedOnStyle: Google
AccessModifierOffset: -4
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignOperands: true
AlignTrailingComments: false
AlwaysBreakTemplateDeclarations: Yes
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterStruct: false
AfterUnion: false
AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
BeforeLambdaBody: false
BeforeWhile: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBraces: Custom
BreakConstructorInitializers: AfterColon
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 0
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ContinuationIndentWidth: 8
IncludeCategories:
- Regex: '^<.*'
Priority: 1
- Regex: '^".*'
Priority: 2
- Regex: '.*'
Priority: 3
IncludeIsMainRegex: '([-_](test|unittest))?$'
IndentCaseLabels: true
IndentWidth: 4
InsertNewlineAtEOF: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 2
NamespaceIndentation: All
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: false
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
TabWidth: 4
44 changes: 44 additions & 0 deletions lessons/265/app-cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Specify the minimum required CMake version
cmake_minimum_required(VERSION 3.28)

# Define the project
project(app-cpp
VERSION 0.1.0
DESCRIPTION "Simple HTTP server."
LANGUAGES CXX
)

# Set C++ standard to C++20 for all targets
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# Add executable
add_executable(app
src/main.cpp
src/http_session.cpp
src/listener.cpp
src/book_ticker.cpp
src/utils.cpp
)

find_package(boost_beast CONFIG REQUIRED)
find_package(boost_json CONFIG REQUIRED)
find_package(spdlog CONFIG REQUIRED)

include_directories(include)

# Set VCPKG triplet based on platform
if(APPLE)
set(VCPKG_TARGET_TRIPLET "arm64-osx")
elseif(UNIX AND NOT APPLE)
set(VCPKG_TARGET_TRIPLET "x64-linux")
endif()

# Link libraries
target_link_libraries(app
PRIVATE
Boost::beast
Boost::json
spdlog::spdlog
)
13 changes: 13 additions & 0 deletions lessons/265/app-cpp/CMakePresets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": 2,
"configurePresets": [
{
"name": "vcpkg",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
}
}
]
}
12 changes: 12 additions & 0 deletions lessons/265/app-cpp/CMakeUserPresets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 2,
"configurePresets": [
{
"name": "default",
"inherits": "vcpkg",
"environment": {
"VCPKG_ROOT": "$env{HOME}/devel/vcpkg"
}
}
]
}
17 changes: 17 additions & 0 deletions lessons/265/app-cpp/include/book_ticker.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#ifndef BOOK_TICKER_HPP
#define BOOK_TICKER_HPP

#include <string>
#include <vector>

struct BookTicker {
std::string symbol;
std::string bidPrice;
std::string bidQty;
std::string askPrice;
std::string askQty;
};

std::string serialize_book_tickers(const std::vector<BookTicker>& tickers);

#endif // BOOK_TICKER_HPP
9 changes: 9 additions & 0 deletions lessons/265/app-cpp/include/http_session.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#ifndef HTTP_SESSION_HPP
#define HTTP_SESSION_HPP

#include <boost/asio/spawn.hpp>
#include <boost/beast.hpp>

void do_session(boost::beast::tcp_stream& stream, boost::asio::yield_context yield);

#endif // HTTP_SESSION_HPP
10 changes: 10 additions & 0 deletions lessons/265/app-cpp/include/listener.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef LISTENER_HPP
#define LISTENER_HPP

#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/beast.hpp>

void do_listen(boost::asio::io_context& ioc, const boost::asio::ip::tcp::endpoint& endpoint, const boost::asio::yield_context& yield);

#endif // LISTENER_HPP
8 changes: 8 additions & 0 deletions lessons/265/app-cpp/include/utils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#ifndef UTILS_HPP
#define UTILS_HPP

#include <boost/beast/core/error.hpp>

void fail(const boost::beast::error_code& ec, char const* what);

#endif // UTILS_HPP
18 changes: 18 additions & 0 deletions lessons/265/app-cpp/src/book_ticker.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#include "book_ticker.hpp"

#include <boost/json.hpp>

#include "spdlog/spdlog.h"

void tag_invoke(const boost::json::value_from_tag&, boost::json::value& jv, const BookTicker& bt) {
jv = {
{"symbol", bt.symbol},
{"bidPrice", bt.bidPrice},
{"bidQty", bt.bidQty},
{"askPrice", bt.askPrice},
{"askQty", bt.askQty}};
}

std::string serialize_book_tickers(const std::vector<BookTicker>& tickers) {
return boost::json::serialize(boost::json::value_from(tickers));
}
75 changes: 75 additions & 0 deletions lessons/265/app-cpp/src/http_session.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
#include "http_session.hpp"

#include <sstream>

#include "book_ticker.hpp"
#include "utils.hpp"

template<class Body, class Allocator>
boost::beast::http::message_generator handle_request(boost::beast::http::request<Body, boost::beast::http::basic_fields<Allocator>>&& req) {
boost::beast::http::response<boost::beast::http::dynamic_body> res;
res.version(req.version());
res.keep_alive(req.keep_alive());

auto const target = req.target();
// Simulate Binance API
if (target == "/api/v3/ticker/bookTicker") {
// Preallocate capacity for 2 elements same as Rust
std::vector<BookTicker> data_array;
data_array.reserve(2);
data_array.emplace_back("LTCBTC", "4.00000000", "431.00000000", "4.00000200", "9.00000000");
data_array.emplace_back("ETHBTC", "0.07946700", "49.00000000", "100000.00000000", "1000.00000000");

res.result(boost::beast::http::status::ok);
res.set(boost::beast::http::field::content_type, "application/json");
boost::beast::ostream(res.body()) << serialize_book_tickers(data_array);
} else {
res.result(boost::beast::http::status::not_found);
res.set(boost::beast::http::field::content_type, "text/plain");
boost::beast::ostream(res.body()) << "Not Found";
}

res.prepare_payload();

return res;
}

void do_session(boost::beast::tcp_stream& stream, boost::asio::yield_context yield) {
boost::beast::error_code ec;

// This buffer is required to persist across reads
boost::beast::flat_buffer buffer;

// This lambda is used to send messages
for (;;) {
// Set the timeout.
stream.expires_after(std::chrono::seconds(30));

// Read a request
boost::beast::http::request<boost::beast::http::string_body> req;
boost::beast::http::async_read(stream, buffer, req, yield[ec]);
if (ec == boost::beast::http::error::end_of_stream)
break;
if (ec)
return fail(ec, "read");

// Handle the request
boost::beast::http::message_generator msg = handle_request(std::move(req));

// Determine if we should close the connection
const bool keep_alive = msg.keep_alive();

// Send the response
boost::beast::async_write(stream, std::move(msg), yield[ec]);

if (ec)
return fail(ec, "write");

if (!keep_alive) {
break;
}
}

// Send a TCP shutdown
stream.socket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec);
}
39 changes: 39 additions & 0 deletions lessons/265/app-cpp/src/listener.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "listener.hpp"

#include "http_session.hpp"
#include "utils.hpp"


void do_listen(boost::asio::io_context& ioc, const boost::asio::ip::tcp::endpoint& endpoint, const boost::asio::yield_context& yield) {
boost::beast::error_code ec;

// Open the acceptor
boost::asio::ip::tcp::acceptor acceptor(ioc);
acceptor.open(endpoint.protocol(), ec);
if (ec)
return fail(ec, "open");

// Allow address reuse
acceptor.set_option(boost::asio::socket_base::reuse_address(true), ec);
if (ec)
return fail(ec, "set_option");

// Bind to the server address
acceptor.bind(endpoint, ec);
if (ec)
return fail(ec, "bind");

// Start listening for connections
acceptor.listen(boost::asio::socket_base::max_listen_connections, ec);
if (ec)
return fail(ec, "listen");

for (;;) {
boost::asio::ip::tcp::socket socket(ioc);
acceptor.async_accept(socket, yield[ec]);
if (ec)
fail(ec, "accept");
else
boost::asio::spawn(acceptor.get_executor(), std::bind(&do_session, boost::beast::tcp_stream(std::move(socket)), std::placeholders::_1), boost::asio::detached);
}
}
31 changes: 31 additions & 0 deletions lessons/265/app-cpp/src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/beast/core.hpp>
#include <cstdlib>
#include <thread>
#include <vector>

#include "listener.hpp"

int main() {
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 8080);

// The io_context is required for all I/O
constexpr auto threads = 2;
boost::asio::io_context ioc{threads};

// Spawn a listening port
boost::asio::spawn(ioc, std::bind(&do_listen, std::ref(ioc), endpoint, std::placeholders::_1), [](const std::exception_ptr& ex) {
if (ex)
std::rethrow_exception(ex);
});

// Run the I/O service on the requested number of threads
std::vector<std::thread> v;
v.reserve(threads - 1);
for (auto i = threads - 1; i > 0; --i)
v.emplace_back([&ioc] { ioc.run(); });
ioc.run();

return EXIT_SUCCESS;
}
7 changes: 7 additions & 0 deletions lessons/265/app-cpp/src/utils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "utils.hpp"

#include <spdlog/spdlog.h>

void fail(const boost::beast::error_code& ec, char const* what) {
spdlog::error("error: {}, {}", what, ec.message());
}
14 changes: 14 additions & 0 deletions lessons/265/app-cpp/vcpkg-configuration.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"default-registry": {
"kind": "git",
"baseline": "b69712e331604d988093981007360257c362c81f",
"repository": "https://github.com/microsoft/vcpkg"
},
"registries": [
{
"kind": "artifact",
"location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip",
"name": "microsoft"
}
]
}
Loading