Replace VLA with thread local vectors

This commit is contained in:
2025-08-22 18:04:12 -04:00
parent f51f257df6
commit c58a00a34f
3 changed files with 27 additions and 35 deletions

View File

@@ -88,27 +88,6 @@ FetchContent_MakeAvailable(llhttp)
include_directories(src) include_directories(src)
# Check for VLA (Variable Length Array) support
include(CheckCXXSourceCompiles)
check_cxx_source_compiles(
"
int main() {
int n = 10;
char arr[n];
return 0;
}
"
HAVE_VLA_SUPPORT)
if(NOT HAVE_VLA_SUPPORT)
message(
FATAL_ERROR
"Compiler must support Variable Length Arrays (VLA). Please use GCC or Clang."
)
endif()
message(STATUS "Compiler supports Variable Length Arrays")
find_package(weaseljson REQUIRED) find_package(weaseljson REQUIRED)
# Generate JSON token hash table using gperf # Generate JSON token hash table using gperf

View File

@@ -1,10 +1,14 @@
#include "connection.hpp" #include "connection.hpp"
#include "server.hpp" // Need this for releaseBackToServer implementation #include "server.hpp" // Need this for releaseBackToServer implementation
#include <climits>
#include <cstdio> #include <cstdio>
#include <cstdlib> #include <cstdlib>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
// Static thread-local storage for iovec buffer
static thread_local std::vector<struct iovec> g_iovec_buffer{IOV_MAX};
Connection::Connection(struct sockaddr_storage addr, int fd, int64_t id, Connection::Connection(struct sockaddr_storage addr, int fd, int64_t id,
size_t epoll_index, ConnectionHandler *handler, size_t epoll_index, ConnectionHandler *handler,
Server &server) Server &server)
@@ -62,8 +66,9 @@ int Connection::readBytes(char *buf, size_t buffer_size) {
bool Connection::writeBytes() { bool Connection::writeBytes() {
while (!messages_.empty()) { while (!messages_.empty()) {
// Build iovec array up to IOV_MAX limit // Build iovec array up to IOV_MAX limit using thread-local vector
struct iovec iov[IOV_MAX]; assert(g_iovec_buffer.size() == IOV_MAX);
struct iovec *iov = g_iovec_buffer.data();
int iov_count = 0; int iov_count = 0;
for (auto it = messages_.begin(); for (auto it = messages_.begin();

View File

@@ -17,6 +17,9 @@
#include <unistd.h> #include <unistd.h>
#include <vector> #include <vector>
// Static thread-local storage for read buffer (used across different functions)
static thread_local std::vector<char> g_read_buffer;
std::shared_ptr<Server> Server::create(const weaseldb::Config &config, std::shared_ptr<Server> Server::create(const weaseldb::Config &config,
ConnectionHandler &handler, ConnectionHandler &handler,
const std::vector<int> &listen_fds) { const std::vector<int> &listen_fds) {
@@ -285,15 +288,16 @@ void Server::start_io_threads(std::vector<std::thread> &threads) {
// Each thread uses its assigned epoll instance (round-robin) // Each thread uses its assigned epoll instance (round-robin)
int epollfd = get_epoll_for_thread(thread_id); int epollfd = get_epoll_for_thread(thread_id);
struct epoll_event events[config_.server.event_batch_size]; std::vector<epoll_event> events(config_.server.event_batch_size);
std::unique_ptr<Connection> batch[config_.server.event_batch_size]; std::vector<std::unique_ptr<Connection>> batch(
int batch_events[config_.server.event_batch_size]; config_.server.event_batch_size);
std::vector<int> batch_events(config_.server.event_batch_size);
std::vector<int> std::vector<int>
ready_listen_fds; // Reused across iterations to avoid allocation ready_listen_fds; // Reused across iterations to avoid allocation
for (;;) { for (;;) {
int event_count = int event_count = epoll_wait(epollfd, events.data(),
epoll_wait(epollfd, events, config_.server.event_batch_size, -1); config_.server.event_batch_size, -1);
if (event_count == -1) { if (event_count == -1) {
if (errno == EINTR) { if (errno == EINTR) {
continue; continue;
@@ -336,8 +340,9 @@ void Server::start_io_threads(std::vector<std::thread> &threads) {
// Process existing connections in batch // Process existing connections in batch
if (batch_count > 0) { if (batch_count > 0) {
process_connection_batch(epollfd, {batch, (size_t)batch_count}, process_connection_batch(
{batch_events, (size_t)batch_count}); epollfd, std::span(batch).subspan(0, batch_count),
std::span(batch_events).subspan(0, batch_count));
} }
// Only accept on listen sockets that epoll indicates are ready // Only accept on listen sockets that epoll indicates are ready
@@ -393,8 +398,9 @@ void Server::start_io_threads(std::vector<std::thread> &threads) {
// Process batch if full // Process batch if full
if (batch_count == config_.server.event_batch_size) { if (batch_count == config_.server.event_batch_size) {
process_connection_batch(epollfd, {batch, (size_t)batch_count}, process_connection_batch(
{batch_events, (size_t)batch_count}); epollfd, {batch.data(), (size_t)batch_count},
{batch_events.data(), (size_t)batch_count});
batch_count = 0; batch_count = 0;
} }
} // End inner accept loop } // End inner accept loop
@@ -402,8 +408,9 @@ void Server::start_io_threads(std::vector<std::thread> &threads) {
// Process remaining accepted connections // Process remaining accepted connections
if (batch_count > 0) { if (batch_count > 0) {
process_connection_batch(epollfd, {batch, (size_t)batch_count}, process_connection_batch(
{batch_events, (size_t)batch_count}); epollfd, std::span(batch).subspan(0, batch_count),
std::span(batch_events).subspan(0, batch_count));
batch_count = 0; batch_count = 0;
} }
} }
@@ -417,7 +424,8 @@ void Server::process_connection_reads(std::unique_ptr<Connection> &conn,
// Handle EPOLLIN - read data and process it // Handle EPOLLIN - read data and process it
if (events & EPOLLIN) { if (events & EPOLLIN) {
auto buf_size = config_.server.read_buffer_size; auto buf_size = config_.server.read_buffer_size;
char buf[buf_size]; g_read_buffer.resize(buf_size);
char *buf = g_read_buffer.data();
int r = conn->readBytes(buf, buf_size); int r = conn->readBytes(buf, buf_size);
if (r < 0) { if (r < 0) {