From 73aa4808f2eb0ea759c3a370a824bae61277c4ea Mon Sep 17 00:00:00 2001 From: amir Date: Sat, 2 May 2026 16:17:17 +0100 Subject: [PATCH] Reworking process_completion() function --- README.md | 53 +++++++++--- build.bat | 96 ++++++++++++++++----- build.sh | 35 ++++++-- config.h | 1 + platform.c | 248 +++++++++++++++++++++++++++-------------------------- 5 files changed, 269 insertions(+), 164 deletions(-) diff --git a/README.md b/README.md index 56fe817..b39e93d 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,38 @@ It is a high performance cross platform Windows and Linux compatible program, it UCRT64 uses the modern Universal C Runtime (ucrtbase.dll), which supports the newest APIs, the standard MSYS2 uses the legacy msvcrt.dll and does not support IO Ring. To install: -pacman -S mingw-w64-ucrt-x86_64-gcc pacman -S mingw-w64-ucrt-x86_64-clang -pacman -S mingw-w64-ucrt-x86_64-cmake +or: +pacman -S mingw-w64-ucrt-x86_64-gcc + pacman -Syu And add to path: C:\msys64\ucrt64\bin +Additionally, to use clang-cl install the latest version of Windows SDK and MSVC, or at least select these in Visual Studio Installer: + * MSVC Build tools fo x64/86. + * C++ Build tools core features. + * MSBuild support for LLVM (clang-cl) toolset. + * Windows Universal C runtime. + * Windows Universal CRT SDK. + * Windows 11 SDK. + +And use the MSVC command prompt or run a script to add MSVC environment variables to current session. +Ex: for PowerShell Terminal save as .ps1 (not persistent): +```ps1 +# Add MS visual studio environment variables +cmd /c '"C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Auxiliary\Build\vcvarsall.bat" x64 && set' | +ForEach-Object { + if ($_ -match "^(.*?)=(.*)$") { + Set-Item -Path "Env:$($matches[1])" -Value $matches[2] + } +} +``` + +Optional: to use the build system +pacman -S mingw-w64-ucrt-x86_64-cmake +The build system uses Ninja and fallsback to make, in Windows it prefers clang-cl > gcc > clang, and in Linux gcc > clang. + ### Using a build system | Command | Description| | :--- | :--- | @@ -52,7 +77,7 @@ clang -g -O0 file_hasher.c xxhash.c xxh_x86dispatch.c -o filehasher clang-cl /Zi /Od file_hasher.c xxhash.c xxh_x86dispatch.c ## Linux -**Requirements**: GCC, CMake and Ninja +**Requirements**: GCC or clang, optional CMake, Ninja or make. ### Using a build system | Command | Description| @@ -109,13 +134,16 @@ While both systems share the same core concept, their APIs and management styles | **Partial Updates** | Supports `IORING_REGISTER_FILES_UPDATE` to swap specific indices. | No partial updates; a new registration replaces the entire table. | | **Scope of Operations** | Extremely broad (files, sockets, timers, signals, etc.). | Primarily focused on file storage (read, write, flush). | -### Completion Wait count +### Completion Wait count and peek To avoid busy waiting when receiving CQEs, we can use io_uring_submit_and_wait() in Linux by entering a wait count, -the threads sleeps until the count of CQEs are received, in windows the wait_count is present in SubmitIoRing() +the threads sleep until the count of CQEs are received, in windows the wait_count is present in SubmitIoRing() but is not implemented yet, so we wait with a completion event for a single completion. Another limitation on the completion event is that the kernel will waik up the thread only when receiving the first CQE, after that we need to drain the completion -queue completely before sleeping again, or we enter an eternal slumber. And my config, each time the thread wakes up -it receives rarely more than 3 to 5 CQEs and most of the time only one CQE. +queue completely before sleeping again, or we enter an eternal slumber. +In the other hand, in Linux we can batch pop completions with io_uring_peek_batch_cqe() + io_uring_cq_advance(), +in Windows we can only pop one completion at a time with PopIoRingCompletion() (equivalent to io_uring_peek_cqe() + io_uring_cqe_seen()). +To simulate the same behavior as the Linux functions we use a double loop, an outer loop to control how much we wait +and in inner loop to drain all the available completions. ### Filtering CQEs @@ -149,12 +177,15 @@ IO Ring implementation. "Increase the limit to solve this warning.\n"); ``` -The Memlock limit in Linux restricts the amount of memory a process can -"lock" into physical RAM using the mlock() family of system calls. This +The Memlock limit in Linux restricts the amount of memory that can be +"locked" into physical RAM using the mlock() family of system calls. This prevents the operating system from swapping that memory out to disk. And registering buffers will lock the buffers memory so the hardware can access it directly without kernel intervention and prevents the kernel from -swapping it to the SSD or HDD. Increase the limit to be able to register the buffers. +swapping it to the SSD or HDD. +This limit does not apply to a single process, but it applies to what all the runnig processes can lock, so in order +to be able to register the buffers, we need to set it to unlimited or increase it to at least: + num_hash_threads * NUM_BUFFERS_PER_THREAD * IORING_BUFFER_SIZE + extra memory reserved for other processes. #### *Modifying the Limit* The method for changing the memlock limit depends on whether you are @@ -166,7 +197,7 @@ the /etc/security/limits.conf file. Add the following lines: ```conf # Example for a specific user (replace 'username'), unlimited or a custom value in KB username soft memlock unlimited - username hard memlock unlimited + username hard memlock unlimitedhttps://wiki.postgresql.org/wiki/AIO ``` ```conf # Example for all users diff --git a/build.bat b/build.bat index fddea92..ccef9ce 100644 --- a/build.bat +++ b/build.bat @@ -2,14 +2,23 @@ setlocal enabledelayedexpansion :: ============================================================================ -:: build.bat - Build script with compiler preference: clang-cl > gcc > clang -:: Usage: build [Release|Debug] [clean] +:: build.bat :: ============================================================================ +:: Get script directory (project root) +set SCRIPT_DIR=%~dp0 +set SCRIPT_DIR=%SCRIPT_DIR:~0,-1% + +:: --------------------------------------------------------------------------- +:: Default values +:: --------------------------------------------------------------------------- + set BUILD_TYPE=Release set CLEAN_BUILD=0 +:: -------------------------------------------------------------------------- :: Parse arguments +:: -------------------------------------------------------------------------- :parse_args if "%~1"=="" goto :main @@ -29,27 +38,34 @@ if /i "%~1"=="clean" ( goto :parse_args ) -:: Unknown argument fallback (the *) echo Unknown argument: %~1 -echo Usage: .\%~nx0 [Release^|Debug] [clean] +echo Usage: build [Release^|Debug] [clean] exit /b 1 :main -set BUILD_DIR=build\windows\%BUILD_TYPE% +set BUILD_DIR=%SCRIPT_DIR%\build\windows\%BUILD_TYPE% + echo === Building filehasher (%BUILD_TYPE%) === +:: -------------------------------------------------------------------------- :: Clean if requested +:: -------------------------------------------------------------------------- if %CLEAN_BUILD%==1 ( echo Cleaning... if exist "%BUILD_DIR%" rmdir /s /q "%BUILD_DIR%" 2>nul ) -:: Create build dir +:: -------------------------------------------------------------------------- +:: Create build directory +:: -------------------------------------------------------------------------- if not exist "%BUILD_DIR%" mkdir "%BUILD_DIR%" pushd "%BUILD_DIR%" -:: Find compiler in preferred order +:: -------------------------------------------------------------------------- +:: Compiler selection +:: -------------------------------------------------------------------------- set CC= + where clang-cl >nul 2>&1 if !ERRORLEVEL! equ 0 ( echo Compiler: clang-cl ^(preferred^) @@ -71,12 +87,14 @@ if !ERRORLEVEL! equ 0 ( goto :find_generator ) -echo ERROR: No suitable compiler found! (clang-cl, gcc, or clang required) +echo ERROR: No suitable compiler found! popd exit /b 1 +:: -------------------------------------------------------------------------- +:: Generator selection (prefer ninja) +:: -------------------------------------------------------------------------- :find_generator -:: Find Ninja for build system set GEN= where ninja >nul 2>&1 if !ERRORLEVEL! equ 0 ( @@ -86,35 +104,67 @@ if !ERRORLEVEL! equ 0 ( echo Generator: Default ) +:: -------------------------------------------------------------------------- :: Configure +:: -------------------------------------------------------------------------- echo. echo Configuring CMake... -set CMD=cmake ../../.. %GEN% %CC% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + +:: -------------------------------------------------------------------------- +:: compile_commands.json logic +:: -------------------------------------------------------------------------- +set EXPORT_COMPILE_COMMANDS=OFF + +if /i "%BUILD_TYPE%"=="Release" ( + if exist "%SCRIPT_DIR%\compile_commands.json" ( + echo compile_commands.json already exists - skipping generation + ) else ( + echo compile_commands.json will be generated + set EXPORT_COMPILE_COMMANDS=ON + ) +) + +set CMD=cmake "%SCRIPT_DIR%" %GEN% %CC% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DCMAKE_EXPORT_COMPILE_COMMANDS=%EXPORT_COMPILE_COMMANDS% + echo !CMD! !CMD! -if !ERRORLEVEL! neq 0 (echo ERROR: Configuration failed & popd & exit /b 1) +if !ERRORLEVEL! neq 0 ( + echo ERROR: Configuration failed + popd + exit /b 1 +) +:: -------------------------------------------------------------------------- :: Build +:: -------------------------------------------------------------------------- echo. echo Building... cmake --build . --config %BUILD_TYPE% -if !ERRORLEVEL! neq 0 (echo ERROR: Build failed & popd & exit /b 1) +if !ERRORLEVEL! neq 0 ( + echo ERROR: Build failed + popd + exit /b 1 +) -:: Check if compile_commands.json exists in the current build directory -if exist "compile_commands.json" ( - echo. - echo clangd: compile_commands.json generated - - :: Copy from current build dir up two levels to the project root - copy /Y "compile_commands.json" "..\..\..\compile_commands.json" >nul 2>&1 - if !ERRORLEVEL! equ 0 ( - echo clangd: Copied to project root - ) else ( - echo clangd: Could not copy to project root +:: -------------------------------------------------------------------------- +:: Copy compile_commands.json (only if generated) +:: -------------------------------------------------------------------------- +if /i "%EXPORT_COMPILE_COMMANDS%"=="ON" ( + if exist "compile_commands.json" ( + echo. + echo clangd: compile_commands.json generated + + copy /Y "compile_commands.json" "%SCRIPT_DIR%\compile_commands.json" >nul 2>&1 + if !ERRORLEVEL! equ 0 ( + echo clangd: Copied to project root + ) else ( + echo clangd: Copy failed + ) ) ) popd + echo. echo === Build Complete === echo Executable: %BUILD_DIR%\filehasher.exe \ No newline at end of file diff --git a/build.sh b/build.sh index 9734457..8321d80 100644 --- a/build.sh +++ b/build.sh @@ -74,7 +74,7 @@ mkdir -p "${BUILD_DIR}" cd "${BUILD_DIR}" # --------------------------------------------------------------------------- -# Find compiler (prefer gcc, fallback to clang) +# Compiler selection (prefer gcc, fallback to clang) # --------------------------------------------------------------------------- echo -e "${YELLOW}Detecting compiler...${NC}" @@ -135,7 +135,7 @@ fi echo # --------------------------------------------------------------------------- -# Find build system (prefer ninja) +# Generator selection (prefer ninja) # --------------------------------------------------------------------------- echo -e "${YELLOW}Selecting build system...${NC}" @@ -165,6 +165,22 @@ echo # Configure # --------------------------------------------------------------------------- echo -e "${YELLOW}Configuring CMake...${NC}" + +# -------------------------------------------------------------------------- +# compile_commands.json logic +# -------------------------------------------------------------------------- + +EXPORT_COMPILE_COMMANDS=OFF + +if [[ "$BUILD_TYPE" == "Release" ]]; then + if [[ -f "${SCRIPT_DIR}/compile_commands.json" ]]; then + echo -e " compile_commands.json already exists - skipping generation" + else + echo -e " compile_commands.json will be generated" + EXPORT_COMPILE_COMMANDS=ON + fi +fi + echo -e " Build type: ${BUILD_TYPE}" echo -e " Compiler: ${CC_NAME}" echo -e " Generator: ${GENERATOR_NAME}" @@ -173,7 +189,7 @@ cmake "${SCRIPT_DIR}" \ -G "${GENERATOR}" \ -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ -DCMAKE_C_COMPILER="${CC_BINARY}" \ - -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + -DCMAKE_EXPORT_COMPILE_COMMANDS=${EXPORT_COMPILE_COMMANDS} if [[ $? -ne 0 ]]; then echo -e "${RED}CMake configuration failed!${NC}" @@ -243,12 +259,13 @@ echo -e " Output: ${BUILD_DIR}/" # --------------------------------------------------------------------------- # Copy compile_commands.json for clangd # --------------------------------------------------------------------------- -if [[ -f "${BUILD_DIR}/compile_commands.json" ]]; then - echo -e " clangd: compile_commands.json generated" - - # Always copy the latest version - cp "${BUILD_DIR}/compile_commands.json" "${SCRIPT_DIR}/compile_commands.json" - echo -e " clangd: Copied to project root" +if [[ "${EXPORT_COMPILE_COMMANDS}" == "ON" ]]; then + if [[ -f "${BUILD_DIR}/compile_commands.json" ]]; then + echo -e " clangd: compile_commands.json generated" + + cp "${BUILD_DIR}/compile_commands.json" "${SCRIPT_DIR}/compile_commands.json" + echo -e " clangd: Copied to project root" + fi fi echo diff --git a/config.h b/config.h index 5cd6f04..9f5a1dc 100644 --- a/config.h +++ b/config.h @@ -15,6 +15,7 @@ #define IORING_BUFFER_SIZE KiB(256) #define NUM_BUFFERS_PER_THREAD 32 #define MAX_ACTIVE_FILES 16 +#define MAX_WAIT_COUNT (NUM_BUFFERS_PER_THREAD / 2) #define SUBMIT_TIMEOUT_MS 10000 #define IORING_DEBUG_PRINTS 0 diff --git a/platform.c b/platform.c index 0ea8108..c313235 100644 --- a/platform.c +++ b/platform.c @@ -5,6 +5,7 @@ #include "lf_mpmc.h" #include "arena.c" +#include #include // xxhash include @@ -1149,12 +1150,6 @@ typedef struct { uint32_t MaxVersion; } IoRingCapabilities; -typedef struct { - BUILD_READ_RETURN_VALUE ResultCode; - uint32_t Information; - uintptr_t UserData; -} IoRingCQE; - // ------------------------ IO Ring Abstraction ------------------------- #if defined(_WIN32) || defined(_WIN64) @@ -1192,13 +1187,20 @@ static int close_ioring(ThreadIoContext *thread_ctx) { #define MAKE_BUF_INFO(a, l) \ (IORING_BUFFER_INFO) { .Address = (a), .Length = (uint32_t)(l) } -static int ioring_submit(ThreadIoContext *thread_ctx, uint32_t wait_count, - uint32_t *submitted) { - HRESULT hr = SubmitIoRing(thread_ctx->ring, 0, SUBMIT_TIMEOUT_MS, submitted); - // HRESULT hr = SubmitIoRing(ring, wait_count, SUBMIT_TIMEOUT_MS, submitted); +static int ioring_submit(ThreadIoContext *thread_ctx, uint32_t *submitted) { - // The wait_count in windows is not implemented yet, so we wait with a - // completion event for a single completion + // uint32_t wait_count = MIN(thread_ctx->num_submissions, MAX_WAIT_COUNT); + + // The wait_count in windows is not implemented yet, so we wait in + // ioring_pop_completion() + + HRESULT hr; + + // if (wait_count > 0) { + // hr = SubmitIoRing(ring, wait_count, SUBMIT_TIMEOUT_MS, submitted); + // } else { + hr = SubmitIoRing(thread_ctx->ring, 0, SUBMIT_TIMEOUT_MS, submitted); + // } if (thread_ctx->num_submissions > 0) { WaitForSingleObject(thread_ctx->completion_event, SUBMIT_TIMEOUT_MS); } @@ -1222,7 +1224,7 @@ static void ioring_register_buffers(ThreadIoContext *thread_ctx, error_msg, (unsigned int)hr); } // Submit registration - ioring_submit(thread_ctx, 0, NULL); + ioring_submit(thread_ctx, NULL); } #if USE_REGISTERED_FILES @@ -1293,52 +1295,69 @@ static BUILD_READ_RETURN_VALUE ioring_build_read(ThreadIoContext *thread_ctx, return hr; } -static int ioring_pop_completion(IoRingHandle ring, IoRingCQE *cqe) { - IORING_CQE win_cqe; +static void ioring_process_completions(ThreadIoContext *thread_ctx) { + uint32_t waited = 0; + uint32_t target = MIN(thread_ctx->num_submissions, MAX_WAIT_COUNT); - while (1) { - HRESULT hr = PopIoRingCompletion(ring, &win_cqe); + while (waited < target) { - if (hr == S_FALSE) - // No CQE available - return 0; + // ---- Drain all available CQEs (non-blocking) ---- + while (1) { + IORING_CQE win_cqe; - if (FAILED(hr)) - return -1; + HRESULT hr = PopIoRingCompletion(thread_ctx->ring, &win_cqe); - // Unlike linux, The Windows implementation treats buffer and file - // registration as an asynchronous operation that we submit to the ring, - // similar to a read or write. Those operations produce CQEs (completion - // queue entries) that we filter here using - // cqe.UserData == USERDATA_REGISTER - if (win_cqe.UserData == USERDATA_REGISTER) - continue; - - cqe->ResultCode = win_cqe.ResultCode; - cqe->Information = win_cqe.Information; - cqe->UserData = win_cqe.UserData; - - // Check for error and print warning - if (FAILED(win_cqe.ResultCode)) { - char error_msg[256]; - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, win_cqe.ResultCode, 0, error_msg, sizeof(error_msg), - NULL); - - // Try to get the file path from the buffer - IoBuffer *buf = (IoBuffer *)win_cqe.UserData; - const char *file_path = "unknown"; - if (buf && buf->file && buf->file->fe) { - file_path = buf->file->fe->path; + if (hr == S_FALSE) { + // No more CQEs available right now + break; } - fprintf(stderr, - "WARNING: I/O completion error for file '%s' - Error: %s (Code: " - "0x%lx)\n", - file_path, error_msg, win_cqe.ResultCode); + if (FAILED(hr)) { + fprintf(stderr, "WARNING: PopIoRingCompletion failed (0x%lx)\n", hr); + return; + } + + // Skip internal registration completions + if (win_cqe.UserData == USERDATA_REGISTER) { + continue; + } + + IoBuffer *buf = (IoBuffer *)win_cqe.UserData; + FileReadContext *file = buf->file; + + if (SUCCEEDED(win_cqe.ResultCode)) { + buf->result = 0; + buf->bytes_read = win_cqe.Information; + } else { + buf->result = win_cqe.ResultCode; + buf->bytes_read = 0; + + char error_msg[256]; + FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + win_cqe.ResultCode, 0, error_msg, sizeof(error_msg), NULL); + + fprintf(stderr, + "WARNING: I/O completion error for file '%s' - Error: %s " + "(Code: 0x%lx)\n", + buf->file->fe->path, error_msg, win_cqe.ResultCode); + } + + file->active_reads--; + file->reads_completed++; + thread_ctx->num_submissions--; + + // Count only "real" completions toward wait budget + waited++; } - return 1; + // ---- If we already waited enough, exit ---- + if (waited >= target) { + break; + } + + // ---- Otherwise wait for more completions ---- + WaitForSingleObject(thread_ctx->completion_event, SUBMIT_TIMEOUT_MS); } } @@ -1431,10 +1450,11 @@ static void ioring_register_buffers(ThreadIoContext *thread_ctx, struct rlimit limit; getrlimit(RLIMIT_MEMLOCK, &limit); - fprintf(stderr, - "WARNING: Buffer registration failed due to memlock limits " - "(ENOMEM).\n" - "See README for more informations.\n"); + fprintf( + stderr, + "WARNING: Buffer registration failed due to Memlock limit, Error: " + "Cannot allocate memory (code: -12, ENOMEM).\n" + "See README for more informations.\n"); } else { // For any other error (e.g., EFAULT, EBUSY, EINVAL) @@ -1530,10 +1550,11 @@ static int ioring_build_read(ThreadIoContext *thread_ctx, return 0; } -static int ioring_submit(ThreadIoContext *thread_ctx, uint32_t wait_count, - uint32_t *submitted) { +static int ioring_submit(ThreadIoContext *thread_ctx, uint32_t *submitted) { int ret; + uint32_t wait_count = MIN(thread_ctx->num_submissions, MAX_WAIT_COUNT); + if (wait_count > 0) { ret = io_uring_submit_and_wait(&((IoUring *)thread_ctx->ring)->ring, wait_count); @@ -1553,58 +1574,46 @@ static int ioring_submit(ThreadIoContext *thread_ctx, uint32_t wait_count, return 0; } -static int ioring_pop_completion(IoRingHandle ring, IoRingCQE *cqe) { +static void ioring_process_completions(ThreadIoContext *thread_ctx) { + IoUring *impl = (IoUring *)thread_ctx->ring; - struct io_uring_cqe *cqe_ptr = NULL; + struct io_uring_cqe *cqes[NUM_BUFFERS_PER_THREAD]; - int ret = io_uring_peek_cqe(&((IoUring *)ring)->ring, &cqe_ptr); + unsigned count = + io_uring_peek_batch_cqe(&impl->ring, cqes, NUM_BUFFERS_PER_THREAD); - if (ret == -EAGAIN) { - // No CQE available - return 0; + if (count == 0) { + return; } - if (ret < 0) { - // Error - fprintf(stderr, "WARNING: io_uring_peek_cqe error - Error: %s (Code: %d)\n", - strerror(-ret), ret); - return -1; - } + for (unsigned i = 0; i < count; i++) { + struct io_uring_cqe *cqe = cqes[i]; - if (!cqe_ptr) { - return 0; - } + int res = cqe->res; - int res = cqe_ptr->res; + IoBuffer *buf = (IoBuffer *)cqe->user_data; + FileReadContext *file = buf->file; - if (res >= 0) { - cqe->ResultCode = 0; - cqe->Information = (uint32_t)res; - } else { - cqe->ResultCode = res; - cqe->Information = 0; - } + if (res >= 0) { + buf->result = 0; + buf->bytes_read = (uint32_t)res; + } else { + buf->result = res; + buf->bytes_read = 0; - cqe->UserData = (uintptr_t)cqe_ptr->user_data; - - io_uring_cqe_seen(&((IoUring *)ring)->ring, cqe_ptr); - - // Check for error and print warning - if (res < 0) { - // Try to get the file path from the buffer - IoBuffer *buf = (IoBuffer *)cqe->UserData; - const char *file_path = "unknown"; - if (buf && buf->file && buf->file->fe) { - file_path = buf->file->fe->path; + fprintf(stderr, + "WARNING: I/O completion error for file '%s' - Error: %s (Code: " + "%d)\n", + buf->file->fe->path, strerror(-res), res); } - fprintf( - stderr, - "WARNING: I/O completion error for file '%s' - Error: %s (Code: %d)\n", - file_path, strerror(-res), res); + file->active_reads--; + file->reads_completed++; + thread_ctx->num_submissions--; } - return 1; + // Mark CQE as seen, equivalent to io_uring_cqe_seen() but marks multiple CQEs + io_uring_cq_advance(&impl->ring, count); } FileHandle ioring_open_file(FileEntry *fe) { @@ -1790,22 +1799,22 @@ static void return_buffer(ThreadIoContext *ctx, IoBuffer *buf) { } // -------------------------- Process completions --------------------------- -static void process_completions(ThreadIoContext *thread_ctx) { - IoRingCQE cqe; - - while (ioring_pop_completion(thread_ctx->ring, &cqe) == 1) { - - IoBuffer *buf = (IoBuffer *)cqe.UserData; - FileReadContext *file = buf->file; - - buf->result = cqe.ResultCode; - buf->bytes_read = cqe.Information; - - file->active_reads--; - file->reads_completed++; - thread_ctx->num_submissions--; - } -} +// static void process_completions(ThreadIoContext *thread_ctx) { +// IoRingCQE cqe; +// +// while (ioring_pop_completion(thread_ctx->ring, &cqe) == 1) { +// +// IoBuffer *buf = (IoBuffer *)cqe.UserData; +// FileReadContext *file = buf->file; +// +// buf->result = cqe.ResultCode; +// buf->bytes_read = cqe.Information; +// +// file->active_reads--; +// file->reads_completed++; +// thread_ctx->num_submissions--; +// } +// } // -------------------- File operations ----------------------- static int init_file(ThreadIoContext *thread_ctx, FileReadContext *file, @@ -2074,8 +2083,7 @@ static THREAD_RETURN hash_worker_ioring(void *arg) { FileQueue fq; memset(&fq, 0, sizeof(fq)); - uint32_t submitted = 0; - uint32_t wait_count; + uint32_t submitted; // Main pipeline loop for (;;) { @@ -2083,13 +2091,14 @@ static THREAD_RETURN hash_worker_ioring(void *arg) { // Submit new reads build_pending_reads(thread_ctx, &fq, worker_ctx); - wait_count = MIN(thread_ctx->num_submissions, NUM_BUFFERS_PER_THREAD - 6); - submitted = 0; - ioring_submit(thread_ctx, wait_count, &submitted); + ioring_submit(thread_ctx, &submitted); // Process completions - process_completions(thread_ctx); + ioring_process_completions(thread_ctx); + + // Hash files + hash_ready_files(thread_ctx, &fq, worker_ctx); #if IORING_DEBUG_STATS printf( @@ -2098,9 +2107,6 @@ static THREAD_RETURN hash_worker_ioring(void *arg) { thread_ctx->active_files, fq.count); #endif - // Hash files - hash_ready_files(thread_ctx, &fq, worker_ctx); - // Exit condition if (!thread_ctx->submitting && thread_ctx->active_files == 0 && thread_ctx->num_submissions == 0) {