init
This commit is contained in:
commit
c9ae8bf0d9
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
build/
|
||||||
31
CMakeLists.txt
Normal file
31
CMakeLists.txt
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
|
||||||
|
# 项目名称
|
||||||
|
project(shm_compare)
|
||||||
|
|
||||||
|
# 设置 C++ 标准
|
||||||
|
set(CMAKE_CXX_STANDARD 23)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# 查找源码文件
|
||||||
|
aux_source_directory(src SOURCES_DIR)
|
||||||
|
set(SOURCES
|
||||||
|
${SOURCES_DIR}
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
set(BENCHMARKS
|
||||||
|
${SOURCES_DIR}
|
||||||
|
benchmark.cpp
|
||||||
|
)
|
||||||
|
# 包含目录
|
||||||
|
include_directories(include third_party)
|
||||||
|
|
||||||
|
# Google Benchmark
|
||||||
|
find_package(benchmark REQUIRED)
|
||||||
|
# 创建可执行文件
|
||||||
|
add_executable(server ${SOURCES})
|
||||||
|
add_executable(benchmark ${BENCHMARKS})
|
||||||
|
|
||||||
|
# 链接线程库
|
||||||
|
target_link_libraries(server pthread)
|
||||||
|
target_link_libraries(benchmark pthread benchmark::benchmark)
|
||||||
14
README.md
Normal file
14
README.md
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
# Share memory Vs Http
|
||||||
|
|
||||||
|
## 结果
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## 测试
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cmake -B build
|
||||||
|
cmake --build build
|
||||||
|
```
|
||||||
|
|
||||||
|
开两个终端分别运行 `./build/server` 和 `./build/benchmark`
|
||||||
281
benchmark.cpp
Normal file
281
benchmark.cpp
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <random>
|
||||||
|
#include <httplib.h>
|
||||||
|
#include <benchmark/benchmark.h>
|
||||||
|
#include <shm.h>
|
||||||
|
const size_t data_length = 32; // 随机数据长度
|
||||||
|
const std::string server_addr = "http://localhost:8080";
|
||||||
|
|
||||||
|
// 生成随机字符串
|
||||||
|
static std::string generate_random_string(size_t length)
|
||||||
|
{
|
||||||
|
static const char alphanum[] =
|
||||||
|
"0123456789"
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
"abcdefghijklmnopqrstuvwxyz";
|
||||||
|
|
||||||
|
static std::random_device rd;
|
||||||
|
static std::mt19937 gen(rd());
|
||||||
|
static std::uniform_int_distribution<> dis(0, sizeof(alphanum) - 2);
|
||||||
|
|
||||||
|
std::string str;
|
||||||
|
str.reserve(length);
|
||||||
|
for (size_t i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
str += alphanum[dis(gen)];
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Http 写操作
|
||||||
|
static void HttpWrite(benchmark::State& state)
|
||||||
|
{
|
||||||
|
httplib::Client cli(server_addr);
|
||||||
|
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
std::string data = generate_random_string(data_length);
|
||||||
|
auto res = cli.Post("/write", data, "text/plain");
|
||||||
|
|
||||||
|
if (res && res->status == 200)
|
||||||
|
{
|
||||||
|
benchmark::DoNotOptimize(res->body);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Write failed!" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Http 读操作
|
||||||
|
static void HttpRead(benchmark::State& state)
|
||||||
|
{
|
||||||
|
httplib::Client cli(server_addr);
|
||||||
|
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
auto res = cli.Get("/read");
|
||||||
|
|
||||||
|
if (res && res->status == 200)
|
||||||
|
{
|
||||||
|
benchmark::DoNotOptimize(res->body);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Read failed!" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void HttpWriteRead(benchmark::State& state)
|
||||||
|
{
|
||||||
|
httplib::Client cli(server_addr);
|
||||||
|
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
// write
|
||||||
|
std::string data = generate_random_string(data_length);
|
||||||
|
auto write_res = cli.Post("/write", data, "text/plain");
|
||||||
|
|
||||||
|
// 写入断言
|
||||||
|
if (!write_res)
|
||||||
|
{
|
||||||
|
state.SkipWithError("Write request failed - no response");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(write_res->status == 200 && "Write operation failed");
|
||||||
|
assert(write_res->body == "OK" && "Unexpected write response");
|
||||||
|
|
||||||
|
// read
|
||||||
|
auto read_res = cli.Get("/read");
|
||||||
|
|
||||||
|
// 读取断言
|
||||||
|
if (!read_res)
|
||||||
|
{
|
||||||
|
state.SkipWithError("Read request failed - no response");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(read_res->status == 200 && "Read operation failed");
|
||||||
|
assert(read_res->body == data && "Read data doesn't match written data");
|
||||||
|
|
||||||
|
// 防止编译器优化掉结果
|
||||||
|
benchmark::DoNotOptimize(write_res);
|
||||||
|
benchmark::DoNotOptimize(read_res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void HttpWriteWithLock(benchmark::State& state)
|
||||||
|
{
|
||||||
|
httplib::Client cli(server_addr);
|
||||||
|
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
std::string data = generate_random_string(data_length);
|
||||||
|
auto res = cli.Post("/write/lock", data, "text/plain");
|
||||||
|
|
||||||
|
if (res && res->status == 200)
|
||||||
|
{
|
||||||
|
benchmark::DoNotOptimize(res->body);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Write failed!" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Http 读操作
|
||||||
|
static void HttpReadWithLock(benchmark::State& state)
|
||||||
|
{
|
||||||
|
httplib::Client cli(server_addr);
|
||||||
|
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
auto res = cli.Get("/read/lock");
|
||||||
|
|
||||||
|
if (res && res->status == 200)
|
||||||
|
{
|
||||||
|
benchmark::DoNotOptimize(res->body);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Read failed!" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void HttpWriteReadWithLock(benchmark::State& state)
|
||||||
|
{
|
||||||
|
httplib::Client cli(server_addr);
|
||||||
|
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
// write
|
||||||
|
std::string data = generate_random_string(data_length);
|
||||||
|
auto write_res = cli.Post("/write/lock", data, "text/plain");
|
||||||
|
|
||||||
|
// 写入断言
|
||||||
|
if (!write_res)
|
||||||
|
{
|
||||||
|
state.SkipWithError("Write request failed - no response");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(write_res->status == 200 && "Write operation failed");
|
||||||
|
assert(write_res->body == "OK" && "Unexpected write response");
|
||||||
|
|
||||||
|
// read
|
||||||
|
auto read_res = cli.Get("/read/lock");
|
||||||
|
|
||||||
|
// 读取断言
|
||||||
|
if (!read_res)
|
||||||
|
{
|
||||||
|
state.SkipWithError("Read request failed - no response");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
assert(read_res->status == 200 && "Read operation failed");
|
||||||
|
assert(read_res->body == data && "Read data doesn't match written data");
|
||||||
|
|
||||||
|
// 防止编译器优化掉结果
|
||||||
|
benchmark::DoNotOptimize(write_res);
|
||||||
|
benchmark::DoNotOptimize(read_res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// =============================================== Shm ===============================================
|
||||||
|
// Shm 写操作
|
||||||
|
static void ShmWrite(benchmark::State& state)
|
||||||
|
{
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
std::string data = generate_random_string(data_length);
|
||||||
|
auto res = shm_write(data);
|
||||||
|
|
||||||
|
if (res)
|
||||||
|
{
|
||||||
|
benchmark::DoNotOptimize(res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Write failed!" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shm 读操作
|
||||||
|
static void ShmRead(benchmark::State& state)
|
||||||
|
{
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
auto res = shm_read();
|
||||||
|
|
||||||
|
if (!res.empty())
|
||||||
|
{
|
||||||
|
benchmark::DoNotOptimize(res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Read failed!" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ShmReadWrite(benchmark::State& state)
|
||||||
|
{
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
// write
|
||||||
|
std::string data = generate_random_string(data_length);
|
||||||
|
auto write_res = shm_write(data);
|
||||||
|
|
||||||
|
if (write_res)
|
||||||
|
{
|
||||||
|
benchmark::DoNotOptimize(write_res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Write failed!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// read
|
||||||
|
auto read_res = shm_read();
|
||||||
|
|
||||||
|
// 读取断言
|
||||||
|
if (!read_res.empty())
|
||||||
|
{
|
||||||
|
benchmark::DoNotOptimize(read_res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Read failed!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(read_res == data && "Read data doesn't match written data");
|
||||||
|
|
||||||
|
// 防止编译器优化掉结果
|
||||||
|
benchmark::DoNotOptimize(write_res);
|
||||||
|
benchmark::DoNotOptimize(read_res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 注册基准测试
|
||||||
|
// HTTP 共享数据
|
||||||
|
BENCHMARK(HttpWrite);
|
||||||
|
BENCHMARK(HttpRead);
|
||||||
|
BENCHMARK(HttpWriteRead);
|
||||||
|
// HTTP 带锁共享数据
|
||||||
|
BENCHMARK(HttpWriteWithLock);
|
||||||
|
BENCHMARK(HttpReadWithLock);
|
||||||
|
BENCHMARK(HttpWriteReadWithLock);
|
||||||
|
// Shm 带锁共享数据
|
||||||
|
BENCHMARK(ShmWrite);
|
||||||
|
BENCHMARK(ShmRead);
|
||||||
|
|
||||||
|
BENCHMARK_MAIN();
|
||||||
3
include/http.h
Normal file
3
include/http.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma region
|
||||||
|
|
||||||
|
void start_server();
|
||||||
4
include/shm.h
Normal file
4
include/shm.h
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#pragma region
|
||||||
|
|
||||||
|
bool shm_write(const std::string& data);
|
||||||
|
std::string shm_read();
|
||||||
7
main.cpp
Normal file
7
main.cpp
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#include "http.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
start_server();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
49
src/http.cpp
Normal file
49
src/http.cpp
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
#include "http.h"
|
||||||
|
#include "httplib.h"
|
||||||
|
using namespace httplib;
|
||||||
|
|
||||||
|
std::string shared_data = ""; // 共享数据
|
||||||
|
std::string shared_mutex_data = ""; // 带锁共享数据
|
||||||
|
std::mutex mtx; // 互斥锁保护共享变量
|
||||||
|
|
||||||
|
|
||||||
|
// 处理写请求
|
||||||
|
void handle_write(const Request& req, Response& res)
|
||||||
|
{
|
||||||
|
shared_data = req.body;
|
||||||
|
res.set_content("OK", "text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理读请求
|
||||||
|
void handle_read(const Request& req, Response& res)
|
||||||
|
{
|
||||||
|
res.set_content(shared_data, "text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void handle_write_with_lock(const Request& req, Response& res)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mtx); // 加锁
|
||||||
|
shared_data = req.body;
|
||||||
|
res.set_content("OK", "text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_read_with_lock(const Request& req, Response& res)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mtx); // 加锁
|
||||||
|
res.set_content(shared_data, "text/plain");
|
||||||
|
}
|
||||||
|
|
||||||
|
void start_server()
|
||||||
|
{
|
||||||
|
Server svr;
|
||||||
|
|
||||||
|
svr.Post("/write", handle_write);
|
||||||
|
svr.Get("/read", handle_read);
|
||||||
|
svr.Post("/write/lock", handle_write_with_lock);
|
||||||
|
svr.Get("/read/lock", handle_read_with_lock);
|
||||||
|
|
||||||
|
|
||||||
|
std::cout << "Server started at http://localhost:8080" << std::endl;
|
||||||
|
svr.listen("localhost", 8080);
|
||||||
|
}
|
||||||
89
src/shm.cpp
Normal file
89
src/shm.cpp
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <cstring>
|
||||||
|
#include <shm.h>
|
||||||
|
|
||||||
|
#define SHM_NAME "/shm"
|
||||||
|
#define SHM_SIZE 1024
|
||||||
|
|
||||||
|
|
||||||
|
bool shm_write(const std::string& data)
|
||||||
|
{
|
||||||
|
if (data.size() + sizeof(std::size_t) > SHM_SIZE)
|
||||||
|
{
|
||||||
|
std::cout << "out of size" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开/创建共享内存
|
||||||
|
int fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
perror("shm_open");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftruncate(fd, SHM_SIZE) == -1)
|
||||||
|
{
|
||||||
|
perror("ftruncate");
|
||||||
|
close(fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* addr = mmap(nullptr, SHM_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);
|
||||||
|
if (addr == MAP_FAILED)
|
||||||
|
{
|
||||||
|
perror("mmap");
|
||||||
|
close(fd);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::byte* ptr = static_cast<std::byte*>(addr);
|
||||||
|
std::size_t len = data.size();
|
||||||
|
std::memcpy(ptr, &len, sizeof(len));
|
||||||
|
std::memcpy(ptr + sizeof(len), data.data(), len);
|
||||||
|
|
||||||
|
munmap(addr, SHM_SIZE);
|
||||||
|
close(fd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string shm_read()
|
||||||
|
{
|
||||||
|
int fd = shm_open(SHM_NAME, O_RDONLY, 0666);
|
||||||
|
if (fd == -1)
|
||||||
|
{
|
||||||
|
perror("shm_open failed for receive()");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void* addr = mmap(nullptr, SHM_SIZE, PROT_READ, MAP_SHARED, fd, 0);
|
||||||
|
if (addr == MAP_FAILED)
|
||||||
|
{
|
||||||
|
perror("mmap failed in receive()");
|
||||||
|
close(fd);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::byte* ptr = static_cast<std::byte*>(addr);
|
||||||
|
|
||||||
|
std::size_t len = 0;
|
||||||
|
std::memcpy(&len, ptr, sizeof(len)); // 读取前缀长度
|
||||||
|
|
||||||
|
if (len > SHM_SIZE - sizeof(len))
|
||||||
|
{
|
||||||
|
munmap(addr, SHM_SIZE);
|
||||||
|
perror("data length exceeds shared memory size");
|
||||||
|
close(fd);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string result(reinterpret_cast<char*>(ptr + sizeof(len)), len);
|
||||||
|
|
||||||
|
munmap(addr, SHM_SIZE);
|
||||||
|
close(fd);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
11556
third_party/httplib.h
vendored
Normal file
11556
third_party/httplib.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user