From 566512af5caac1727f6f28f843baadab6f45890a Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Mon, 15 Jun 2026 18:18:19 +0300 Subject: [PATCH 1/8] [k2] add coroutine cpu cycles --- runtime-light/coroutine/detail/await-set.h | 4 ++ .../coroutine/detail/task-self-deleting.h | 4 ++ runtime-light/coroutine/detail/when-all.h | 4 ++ runtime-light/coroutine/detail/when-any.h | 4 ++ runtime-light/coroutine/shared-task.h | 4 ++ runtime-light/coroutine/task.h | 4 ++ runtime-light/runtime-light.cpp | 21 ++++++++++ runtime-light/state/instance-state.h | 2 + .../stdlib/cpu-info/cpu-info-state.cpp | 11 +++++ .../stdlib/cpu-info/cpu-info-state.h | 42 +++++++++++++++++++ runtime-light/stdlib/stdlib.cmake | 1 + 11 files changed, 101 insertions(+) create mode 100644 runtime-light/stdlib/cpu-info/cpu-info-state.cpp create mode 100644 runtime-light/stdlib/cpu-info/cpu-info-state.h diff --git a/runtime-light/coroutine/detail/await-set.h b/runtime-light/coroutine/detail/await-set.h index 2c05be0c6e..f307f26850 100644 --- a/runtime-light/coroutine/detail/await-set.h +++ b/runtime-light/coroutine/detail/await-set.h @@ -15,6 +15,7 @@ #include "runtime-light/coroutine/async-stack.h" #include "runtime-light/coroutine/type-traits.h" #include "runtime-light/coroutine/void-value.h" +#include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" namespace kphp::coro { @@ -58,15 +59,18 @@ class await_broker { template void* operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } void operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/detail/task-self-deleting.h b/runtime-light/coroutine/detail/task-self-deleting.h index 3ff19f1831..795123c585 100644 --- a/runtime-light/coroutine/detail/task-self-deleting.h +++ b/runtime-light/coroutine/detail/task-self-deleting.h @@ -12,6 +12,7 @@ #include "runtime-light/coroutine/async-stack.h" #include "runtime-light/coroutine/concepts.h" #include "runtime-light/coroutine/coroutine-state.h" +#include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" namespace kphp::coro::detail { @@ -32,15 +33,18 @@ struct promise_self_deleting : kphp::coro::async_stack_element { template auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/detail/when-all.h b/runtime-light/coroutine/detail/when-all.h index 2386e4fbb6..133df6363e 100644 --- a/runtime-light/coroutine/detail/when-all.h +++ b/runtime-light/coroutine/detail/when-all.h @@ -18,6 +18,7 @@ #include "runtime-light/coroutine/concepts.h" #include "runtime-light/coroutine/type-traits.h" #include "runtime-light/coroutine/void-value.h" +#include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" namespace kphp::coro::detail::when_all { @@ -152,15 +153,18 @@ class when_all_task_promise_base : public kphp::coro::async_stack_element { template auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/detail/when-any.h b/runtime-light/coroutine/detail/when-any.h index f5ee01549e..fb4bd96a1c 100644 --- a/runtime-light/coroutine/detail/when-any.h +++ b/runtime-light/coroutine/detail/when-any.h @@ -17,6 +17,7 @@ #include "runtime-light/coroutine/type-traits.h" #include "runtime-light/coroutine/void-value.h" #include "runtime-light/metaprogramming/type-functions.h" +#include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" namespace kphp::coro::detail::when_any { @@ -162,15 +163,18 @@ class when_any_task_promise_base : public kphp::coro::async_stack_element { template auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/shared-task.h b/runtime-light/coroutine/shared-task.h index ddec603c58..eacb345061 100644 --- a/runtime-light/coroutine/shared-task.h +++ b/runtime-light/coroutine/shared-task.h @@ -17,6 +17,7 @@ #include "runtime-common/core/allocator/script-malloc-interface.h" #include "runtime-light/coroutine/async-stack.h" #include "runtime-light/coroutine/void-value.h" +#include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" namespace kphp::coro { @@ -145,15 +146,18 @@ struct promise_base : kphp::coro::async_stack_element { template auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/task.h b/runtime-light/coroutine/task.h index d5c064720b..aad7f3a4b6 100644 --- a/runtime-light/coroutine/task.h +++ b/runtime-light/coroutine/task.h @@ -13,6 +13,7 @@ #include "common/containers/final_action.h" #include "runtime-common/core/allocator/script-malloc-interface.h" #include "runtime-light/coroutine/async-stack.h" +#include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" namespace kphp::coro { @@ -66,15 +67,18 @@ struct promise_base : kphp::coro::async_stack_element { template auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/runtime-light.cpp b/runtime-light/runtime-light.cpp index e65f3a0966..fb8279906d 100644 --- a/runtime-light/runtime-light.cpp +++ b/runtime-light/runtime-light.cpp @@ -65,6 +65,11 @@ VISIBILITY_DEFAULT void k2_init_instance() { k2::details::instance_state_ptr = k2_instance_state(); kphp::log::debug("start instance state init"); new (k2::instance_state()) InstanceState{}; + + auto& cpu_info_instance_state{CpuInfoInstanceState::get()}; + cpu_info_instance_state.init(); + cpu_info_instance_state.total_cycles = -CpuInfoInstanceState::rdtsc(); + k2::instance_state()->init_script_execution(); kphp::log::debug("finish instance state init"); } @@ -75,6 +80,22 @@ VISIBILITY_DEFAULT k2::PollStatus k2_poll() { k2::details::instance_state_ptr = k2_instance_state(); kphp::log::debug("k2_poll started"); const auto poll_status{kphp::coro::io_scheduler::get().process_events()}; + + if (poll_status == k2::PollStatus::PollFinishedOk) { + auto& cpu_info_instance_state{CpuInfoInstanceState::get()}; + cpu_info_instance_state.total_cycles += CpuInfoInstanceState::rdtsc(); + + uint64_t coro_alloc_cycles{cpu_info_instance_state.coro_alloc_cycles}; + uint64_t coro_free_cycles{cpu_info_instance_state.coro_free_cycles}; + uint64_t coro_alloc_free_cycles{coro_alloc_cycles + coro_free_cycles}; + + kphp::log::info("\ntotal cpu cycles -> {}\n" + "coro_alloc_cycles -> {} ({}%)\n" + "coro_free_cycles -> {} ({}%)\n" + "coro_alloc+free_cycles -> {} ({}%)", + cpu_info_instance_state.total_cycles, coro_alloc_cycles, cpu_info_instance_state.get_percent(coro_alloc_cycles), coro_free_cycles, + cpu_info_instance_state.get_percent(coro_free_cycles), coro_alloc_free_cycles, cpu_info_instance_state.get_percent(coro_alloc_free_cycles)); + } kphp::log::debug("k2_poll finished: {}", std::to_underlying(poll_status)); return poll_status; } diff --git a/runtime-light/state/instance-state.h b/runtime-light/state/instance-state.h index f48a39f9c8..ceef7fd5d4 100644 --- a/runtime-light/state/instance-state.h +++ b/runtime-light/state/instance-state.h @@ -22,6 +22,7 @@ #include "runtime-light/server/rpc/rpc-server-state.h" #include "runtime-light/state/component-state.h" #include "runtime-light/stdlib/confdata/confdata-state.h" +#include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/curl/curl-state.h" #include "runtime-light/stdlib/diagnostics/contextual-tags.h" #include "runtime-light/stdlib/diagnostics/error-handling-state.h" @@ -98,6 +99,7 @@ struct InstanceState final : vk::not_copyable { WaitQueueInstanceState wait_queue_instance_state; RpcQueueInstanceState rpc_queue_instance_state; PhpScriptMutableGlobals php_script_mutable_globals_singleton; + CpuInfoInstanceState cpu_info_instance_state; RuntimeContext runtime_context; CLIInstanceInstance cli_instance_instate; diff --git a/runtime-light/stdlib/cpu-info/cpu-info-state.cpp b/runtime-light/stdlib/cpu-info/cpu-info-state.cpp new file mode 100644 index 0000000000..cb38369e68 --- /dev/null +++ b/runtime-light/stdlib/cpu-info/cpu-info-state.cpp @@ -0,0 +1,11 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2026 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#include "runtime-light/stdlib/cpu-info/cpu-info-state.h" + +#include "runtime-light/state/instance-state.h" + +CpuInfoInstanceState& CpuInfoInstanceState::get() noexcept { + return InstanceState::get().cpu_info_instance_state; +} diff --git a/runtime-light/stdlib/cpu-info/cpu-info-state.h b/runtime-light/stdlib/cpu-info/cpu-info-state.h new file mode 100644 index 0000000000..d688b3fd50 --- /dev/null +++ b/runtime-light/stdlib/cpu-info/cpu-info-state.h @@ -0,0 +1,42 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2026 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#pragma once + +#include +#include + +#include "common/containers/final_action.h" +#include "runtime-light/stdlib/diagnostics/logs.h" + +class CpuInfoInstanceState { +public: + CpuInfoInstanceState() noexcept = default; + + static CpuInfoInstanceState& get() noexcept; + + [[gnu::always_inline]] static uint64_t rdtsc() noexcept { + return __rdtsc(); + } + + static auto write_cycles(uint64_t& dist) noexcept { + uint64_t start{CpuInfoInstanceState::rdtsc()}; + return vk::final_action([start, &dist]() noexcept { dist += CpuInfoInstanceState::rdtsc() - start; }); + } + + double get_percent(uint64_t cycles) const noexcept { + kphp::log::assertion(cycles <= total_cycles); + return 100.0 * cycles / total_cycles; + } + + void init() noexcept { + total_cycles = 0; + coro_alloc_cycles = 0; + coro_free_cycles = 0; + } + + uint64_t total_cycles; + uint64_t coro_alloc_cycles; + uint64_t coro_free_cycles; +}; diff --git a/runtime-light/stdlib/stdlib.cmake b/runtime-light/stdlib/stdlib.cmake index 92f67cd6b4..b82d1c1e6d 100644 --- a/runtime-light/stdlib/stdlib.cmake +++ b/runtime-light/stdlib/stdlib.cmake @@ -3,6 +3,7 @@ prepend( stdlib/ confdata/confdata-functions.cpp confdata/confdata-state.cpp + cpu-info/cpu-info-state.cpp crypto/crypto-functions.cpp curl/curl-state.cpp web-transfer-lib/web-state.cpp From c22ea27ec193ba3a85e97398b50df6495ceb773d Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Mon, 15 Jun 2026 18:22:21 +0300 Subject: [PATCH 2/8] minor fix --- runtime-light/stdlib/cpu-info/cpu-info-state.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runtime-light/stdlib/cpu-info/cpu-info-state.h b/runtime-light/stdlib/cpu-info/cpu-info-state.h index d688b3fd50..b312c814bb 100644 --- a/runtime-light/stdlib/cpu-info/cpu-info-state.h +++ b/runtime-light/stdlib/cpu-info/cpu-info-state.h @@ -26,7 +26,9 @@ class CpuInfoInstanceState { } double get_percent(uint64_t cycles) const noexcept { - kphp::log::assertion(cycles <= total_cycles); + if (cycles > total_cycles) { + kphp::log::warning("CpuInfo. Something wrong: cycles {} > total_cycles {}", cycles, total_cycles); + } return 100.0 * cycles / total_cycles; } From 6232a4ed376e79a50ab219ca9ab5ba643c3358ed Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Mon, 15 Jun 2026 18:25:24 +0300 Subject: [PATCH 3/8] minor fix --- runtime-light/runtime-light.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime-light/runtime-light.cpp b/runtime-light/runtime-light.cpp index fb8279906d..10a2484efb 100644 --- a/runtime-light/runtime-light.cpp +++ b/runtime-light/runtime-light.cpp @@ -90,9 +90,9 @@ VISIBILITY_DEFAULT k2::PollStatus k2_poll() { uint64_t coro_alloc_free_cycles{coro_alloc_cycles + coro_free_cycles}; kphp::log::info("\ntotal cpu cycles -> {}\n" - "coro_alloc_cycles -> {} ({}%)\n" - "coro_free_cycles -> {} ({}%)\n" - "coro_alloc+free_cycles -> {} ({}%)", + "coro alloc cycles -> {} ({}%)\n" + "coro free cycles -> {} ({}%)\n" + "coro alloc+free cycles -> {} ({}%)\n", cpu_info_instance_state.total_cycles, coro_alloc_cycles, cpu_info_instance_state.get_percent(coro_alloc_cycles), coro_free_cycles, cpu_info_instance_state.get_percent(coro_free_cycles), coro_alloc_free_cycles, cpu_info_instance_state.get_percent(coro_alloc_free_cycles)); } From 79e59f8443ad39522142538fd27cc1207ddc3d78 Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Tue, 7 Jul 2026 13:14:13 +0300 Subject: [PATCH 4/8] added sending of metrics --- runtime-light/coroutine/detail/await-set.h | 3 ++ runtime-light/runtime-light.cpp | 52 ++++++++++++------- .../stdlib/cpu-info/cpu-info-state.h | 40 ++++++-------- 3 files changed, 54 insertions(+), 41 deletions(-) diff --git a/runtime-light/coroutine/detail/await-set.h b/runtime-light/coroutine/detail/await-set.h index f307f26850..3faff1d72a 100644 --- a/runtime-light/coroutine/detail/await-set.h +++ b/runtime-light/coroutine/detail/await-set.h @@ -174,15 +174,18 @@ class await_set_task_promise_base : public kphp::coro::async_stack_element { template void* operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } void operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept { + auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/runtime-light.cpp b/runtime-light/runtime-light.cpp index 10a2484efb..6f7538b778 100644 --- a/runtime-light/runtime-light.cpp +++ b/runtime-light/runtime-light.cpp @@ -2,6 +2,8 @@ // Copyright (c) 2024 LLC «V Kontakte» // Distributed under the GPL v3 License, see LICENSE.notice.txt +#include +#include #include #include "runtime-light/core/globals/php-init-scripts.h" @@ -12,6 +14,7 @@ #include "runtime-light/state/image-state.h" #include "runtime-light/state/instance-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" +#include "runtime-light/stdlib/diagnostics/metrics.h" #define VISIBILITY_DEFAULT __attribute__((visibility("default"))) @@ -66,36 +69,49 @@ VISIBILITY_DEFAULT void k2_init_instance() { kphp::log::debug("start instance state init"); new (k2::instance_state()) InstanceState{}; - auto& cpu_info_instance_state{CpuInfoInstanceState::get()}; - cpu_info_instance_state.init(); - cpu_info_instance_state.total_cycles = -CpuInfoInstanceState::rdtsc(); + { + auto guard = CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().processing_cycles); + k2::instance_state()->init_script_execution(); + } - k2::instance_state()->init_script_execution(); kphp::log::debug("finish instance state init"); } VISIBILITY_DEFAULT k2::PollStatus k2_poll() { + auto& cpu_info_instance_state{CpuInfoInstanceState::get()}; + k2::details::image_state_ptr = k2_image_state(); k2::details::component_state_ptr = k2_component_state(); k2::details::instance_state_ptr = k2_instance_state(); kphp::log::debug("k2_poll started"); - const auto poll_status{kphp::coro::io_scheduler::get().process_events()}; + + k2::PollStatus poll_status{}; + { + auto guard = CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().processing_cycles); + poll_status = kphp::coro::io_scheduler::get().process_events(); + } if (poll_status == k2::PollStatus::PollFinishedOk) { - auto& cpu_info_instance_state{CpuInfoInstanceState::get()}; - cpu_info_instance_state.total_cycles += CpuInfoInstanceState::rdtsc(); - - uint64_t coro_alloc_cycles{cpu_info_instance_state.coro_alloc_cycles}; - uint64_t coro_free_cycles{cpu_info_instance_state.coro_free_cycles}; - uint64_t coro_alloc_free_cycles{coro_alloc_cycles + coro_free_cycles}; - - kphp::log::info("\ntotal cpu cycles -> {}\n" - "coro alloc cycles -> {} ({}%)\n" - "coro free cycles -> {} ({}%)\n" - "coro alloc+free cycles -> {} ({}%)\n", - cpu_info_instance_state.total_cycles, coro_alloc_cycles, cpu_info_instance_state.get_percent(coro_alloc_cycles), coro_free_cycles, - cpu_info_instance_state.get_percent(coro_free_cycles), coro_alloc_free_cycles, cpu_info_instance_state.get_percent(coro_alloc_free_cycles)); + constexpr std::string_view metric_name{"instance_cpu_cycles"}; + constexpr std::string_view tag_name{"kind"}; + + auto result{ + kphp::diagnostics::metric::empty().send_value(metric_name, std::array{std::pair{tag_name, "total"}}, cpu_info_instance_state.processing_cycles)}; + if (!result.second.has_value()) { + kphp::log::warning("failed to send metric {} (kind=total): error {}", metric_name, result.second.error()); + } + + auto send = [&result, metric_name, tag_name](std::string_view tag_value, uint64_t value) noexcept { + result = kphp::diagnostics::metric::with_buffer(std::move(result.first)).send_value(metric_name, std::array{std::pair{tag_name, tag_value}}, value); + if (!result.second.has_value()) { + kphp::log::warning("failed to send metric {} (kind={}): error {}", metric_name, tag_value, result.second.error()); + } + }; + + send("coro_alloc", cpu_info_instance_state.coro_alloc_cycles); + send("coro_free", cpu_info_instance_state.coro_free_cycles); } + kphp::log::debug("k2_poll finished: {}", std::to_underlying(poll_status)); return poll_status; } diff --git a/runtime-light/stdlib/cpu-info/cpu-info-state.h b/runtime-light/stdlib/cpu-info/cpu-info-state.h index b312c814bb..99630c6026 100644 --- a/runtime-light/stdlib/cpu-info/cpu-info-state.h +++ b/runtime-light/stdlib/cpu-info/cpu-info-state.h @@ -5,40 +5,34 @@ #pragma once #include + +#if defined(__x86_64__) || defined(__i386__) #include +#endif #include "common/containers/final_action.h" -#include "runtime-light/stdlib/diagnostics/logs.h" class CpuInfoInstanceState { -public: - CpuInfoInstanceState() noexcept = default; - - static CpuInfoInstanceState& get() noexcept; - +private: [[gnu::always_inline]] static uint64_t rdtsc() noexcept { +#if defined(__x86_64__) || defined(__i386__) return __rdtsc(); +#else + return 0; +#endif } - static auto write_cycles(uint64_t& dist) noexcept { - uint64_t start{CpuInfoInstanceState::rdtsc()}; - return vk::final_action([start, &dist]() noexcept { dist += CpuInfoInstanceState::rdtsc() - start; }); - } +public: + CpuInfoInstanceState() noexcept = default; - double get_percent(uint64_t cycles) const noexcept { - if (cycles > total_cycles) { - kphp::log::warning("CpuInfo. Something wrong: cycles {} > total_cycles {}", cycles, total_cycles); - } - return 100.0 * cycles / total_cycles; - } + static CpuInfoInstanceState& get() noexcept; - void init() noexcept { - total_cycles = 0; - coro_alloc_cycles = 0; - coro_free_cycles = 0; + [[nodiscard]] static auto write_cycles(uint64_t& cycles_accumulator) noexcept { + uint64_t start{CpuInfoInstanceState::rdtsc()}; + return vk::final_action([start, &cycles_accumulator]() noexcept { cycles_accumulator += CpuInfoInstanceState::rdtsc() - start; }); } - uint64_t total_cycles; - uint64_t coro_alloc_cycles; - uint64_t coro_free_cycles; + uint64_t processing_cycles{0}; + uint64_t coro_alloc_cycles{0}; + uint64_t coro_free_cycles{0}; }; From 1974d1c9782939445a61c44ed1715bc4e311c1ce Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Thu, 9 Jul 2026 11:41:03 +0300 Subject: [PATCH 5/8] fix --- runtime-light/runtime-light.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/runtime-light/runtime-light.cpp b/runtime-light/runtime-light.cpp index 6f7538b778..a34a5a75d3 100644 --- a/runtime-light/runtime-light.cpp +++ b/runtime-light/runtime-light.cpp @@ -78,16 +78,15 @@ VISIBILITY_DEFAULT void k2_init_instance() { } VISIBILITY_DEFAULT k2::PollStatus k2_poll() { - auto& cpu_info_instance_state{CpuInfoInstanceState::get()}; - k2::details::image_state_ptr = k2_image_state(); k2::details::component_state_ptr = k2_component_state(); k2::details::instance_state_ptr = k2_instance_state(); kphp::log::debug("k2_poll started"); + auto& cpu_info_instance_state{CpuInfoInstanceState::get()}; k2::PollStatus poll_status{}; { - auto guard = CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().processing_cycles); + auto guard = CpuInfoInstanceState::write_cycles(cpu_info_instance_state.processing_cycles); poll_status = kphp::coro::io_scheduler::get().process_events(); } From 1bf3c1aed935adb5e4537e9628709c0f9a90aae2 Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Tue, 14 Jul 2026 18:41:04 +0300 Subject: [PATCH 6/8] fix --- runtime-light/coroutine/detail/await-set.h | 18 ++++++++---- .../coroutine/detail/task-self-deleting.h | 9 ++++-- runtime-light/coroutine/detail/when-all.h | 9 ++++-- runtime-light/coroutine/detail/when-any.h | 9 ++++-- runtime-light/coroutine/shared-task.h | 9 ++++-- runtime-light/coroutine/task.h | 9 ++++-- runtime-light/runtime-light.cpp | 28 ++++--------------- runtime-light/state/instance-state.cpp | 22 +++++++++++++++ .../stdlib/cpu-info/cpu-info-state.h | 20 +++++++++++-- 9 files changed, 87 insertions(+), 46 deletions(-) diff --git a/runtime-light/coroutine/detail/await-set.h b/runtime-light/coroutine/detail/await-set.h index 3faff1d72a..56361fedb5 100644 --- a/runtime-light/coroutine/detail/await-set.h +++ b/runtime-light/coroutine/detail/await-set.h @@ -59,18 +59,21 @@ class await_broker { template void* operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } void operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; kphp::memory::script::free(ptr); } @@ -174,18 +177,21 @@ class await_set_task_promise_base : public kphp::coro::async_stack_element { template void* operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } void operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/detail/task-self-deleting.h b/runtime-light/coroutine/detail/task-self-deleting.h index 795123c585..48dfaa8998 100644 --- a/runtime-light/coroutine/detail/task-self-deleting.h +++ b/runtime-light/coroutine/detail/task-self-deleting.h @@ -33,18 +33,21 @@ struct promise_self_deleting : kphp::coro::async_stack_element { template auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/detail/when-all.h b/runtime-light/coroutine/detail/when-all.h index 133df6363e..598ddec6e8 100644 --- a/runtime-light/coroutine/detail/when-all.h +++ b/runtime-light/coroutine/detail/when-all.h @@ -153,18 +153,21 @@ class when_all_task_promise_base : public kphp::coro::async_stack_element { template auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/detail/when-any.h b/runtime-light/coroutine/detail/when-any.h index fb4bd96a1c..fa9d4e85c6 100644 --- a/runtime-light/coroutine/detail/when-any.h +++ b/runtime-light/coroutine/detail/when-any.h @@ -163,18 +163,21 @@ class when_any_task_promise_base : public kphp::coro::async_stack_element { template auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/shared-task.h b/runtime-light/coroutine/shared-task.h index eacb345061..73a5dcc843 100644 --- a/runtime-light/coroutine/shared-task.h +++ b/runtime-light/coroutine/shared-task.h @@ -146,18 +146,21 @@ struct promise_base : kphp::coro::async_stack_element { template auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/coroutine/task.h b/runtime-light/coroutine/task.h index aad7f3a4b6..64724e1032 100644 --- a/runtime-light/coroutine/task.h +++ b/runtime-light/coroutine/task.h @@ -67,18 +67,21 @@ struct promise_base : kphp::coro::async_stack_element { template auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_alloc_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; return kphp::memory::script::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { - auto writer{CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().coro_free_cycles)}; + auto& ciis{CpuInfoInstanceState::get()}; + auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; kphp::memory::script::free(ptr); } diff --git a/runtime-light/runtime-light.cpp b/runtime-light/runtime-light.cpp index a34a5a75d3..a05d59189a 100644 --- a/runtime-light/runtime-light.cpp +++ b/runtime-light/runtime-light.cpp @@ -70,7 +70,8 @@ VISIBILITY_DEFAULT void k2_init_instance() { new (k2::instance_state()) InstanceState{}; { - auto guard = CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().processing_cycles); + auto& ciis{CpuInfoInstanceState::get()}; + auto guard{ciis.write_cycles(ciis.processing_cycles)}; k2::instance_state()->init_script_execution(); } @@ -86,29 +87,10 @@ VISIBILITY_DEFAULT k2::PollStatus k2_poll() { auto& cpu_info_instance_state{CpuInfoInstanceState::get()}; k2::PollStatus poll_status{}; { - auto guard = CpuInfoInstanceState::write_cycles(cpu_info_instance_state.processing_cycles); + cpu_info_instance_state.write_k2_poll_start(); + auto guard{cpu_info_instance_state.write_cycles(cpu_info_instance_state.processing_cycles, cpu_info_instance_state.k2_poll_start.value())}; poll_status = kphp::coro::io_scheduler::get().process_events(); - } - - if (poll_status == k2::PollStatus::PollFinishedOk) { - constexpr std::string_view metric_name{"instance_cpu_cycles"}; - constexpr std::string_view tag_name{"kind"}; - - auto result{ - kphp::diagnostics::metric::empty().send_value(metric_name, std::array{std::pair{tag_name, "total"}}, cpu_info_instance_state.processing_cycles)}; - if (!result.second.has_value()) { - kphp::log::warning("failed to send metric {} (kind=total): error {}", metric_name, result.second.error()); - } - - auto send = [&result, metric_name, tag_name](std::string_view tag_value, uint64_t value) noexcept { - result = kphp::diagnostics::metric::with_buffer(std::move(result.first)).send_value(metric_name, std::array{std::pair{tag_name, tag_value}}, value); - if (!result.second.has_value()) { - kphp::log::warning("failed to send metric {} (kind={}): error {}", metric_name, tag_value, result.second.error()); - } - }; - - send("coro_alloc", cpu_info_instance_state.coro_alloc_cycles); - send("coro_free", cpu_info_instance_state.coro_free_cycles); + cpu_info_instance_state.write_k2_poll_finish(); } kphp::log::debug("k2_poll finished: {}", std::to_underlying(poll_status)); diff --git a/runtime-light/state/instance-state.cpp b/runtime-light/state/instance-state.cpp index 4bb90f6e55..6472abbffe 100644 --- a/runtime-light/state/instance-state.cpp +++ b/runtime-light/state/instance-state.cpp @@ -27,6 +27,7 @@ #include "runtime-light/state/component-state.h" #include "runtime-light/stdlib/component/component-api.h" #include "runtime-light/stdlib/diagnostics/logs.h" +#include "runtime-light/stdlib/diagnostics/metrics.h" #include "runtime-light/stdlib/fork/fork-functions.h" #include "runtime-light/stdlib/fork/fork-state.h" #include "runtime-light/stdlib/rpc/rpc-client-state.h" @@ -246,4 +247,25 @@ kphp::coro::task<> InstanceState::run_instance_epilogue() noexcept { web_state.session_is_finished = true; web_state.session.reset(); } + + auto& cpu_info_instance_state{CpuInfoInstanceState::get()}; + cpu_info_instance_state.write_exit(); + + constexpr std::string_view metric_name{"instance_cpu_cycles"}; + constexpr std::string_view tag_name{"kind"}; + + auto result{kphp::diagnostics::metric::empty().send_value(metric_name, std::array{std::pair{tag_name, "total"}}, cpu_info_instance_state.processing_cycles)}; + if (!result.second.has_value()) { + kphp::log::warning("failed to send metric {} (kind=total): error {}", metric_name, result.second.error()); + } + + auto send{[&result, metric_name, tag_name](std::string_view tag_value, uint64_t value) noexcept { + result = kphp::diagnostics::metric::with_buffer(std::move(result.first)).send_value(metric_name, std::array{std::pair{tag_name, tag_value}}, value); + if (!result.second.has_value()) { + kphp::log::warning("failed to send metric {} (kind={}): error {}", metric_name, tag_value, result.second.error()); + } + }}; + + send("coro_alloc", cpu_info_instance_state.coro_alloc_cycles); + send("coro_free", cpu_info_instance_state.coro_free_cycles); } diff --git a/runtime-light/stdlib/cpu-info/cpu-info-state.h b/runtime-light/stdlib/cpu-info/cpu-info-state.h index 99630c6026..776afdd133 100644 --- a/runtime-light/stdlib/cpu-info/cpu-info-state.h +++ b/runtime-light/stdlib/cpu-info/cpu-info-state.h @@ -5,6 +5,7 @@ #pragma once #include +#include #if defined(__x86_64__) || defined(__i386__) #include @@ -27,12 +28,27 @@ class CpuInfoInstanceState { static CpuInfoInstanceState& get() noexcept; - [[nodiscard]] static auto write_cycles(uint64_t& cycles_accumulator) noexcept { - uint64_t start{CpuInfoInstanceState::rdtsc()}; + [[nodiscard]] auto write_cycles(uint64_t& cycles_accumulator, uint64_t start = CpuInfoInstanceState::rdtsc()) const noexcept { return vk::final_action([start, &cycles_accumulator]() noexcept { cycles_accumulator += CpuInfoInstanceState::rdtsc() - start; }); } + void write_k2_poll_start() noexcept { + this->k2_poll_start = CpuInfoInstanceState::rdtsc(); + } + + void write_k2_poll_finish() noexcept { + this->k2_poll_start = std::nullopt; + } + + void write_exit() noexcept { + if (this->k2_poll_start.has_value()) { + this->processing_cycles += CpuInfoInstanceState::rdtsc() - this->k2_poll_start.value(); + } + } + uint64_t processing_cycles{0}; uint64_t coro_alloc_cycles{0}; uint64_t coro_free_cycles{0}; + + std::optional k2_poll_start{std::nullopt}; }; From 393c8257b10dea1c5ba037d47c5997d64fc8d489 Mon Sep 17 00:00:00 2001 From: Alexander Polyakov Date: Tue, 16 Jun 2026 12:19:46 +0300 Subject: [PATCH 7/8] cherry-pick --- runtime-light/coroutine/coroutine-state.h | 6 +- .../coroutine/detail/coro-malloc-interface.h | 171 ++++++++++++++++++ runtime-light/coroutine/shared-task.h | 8 +- runtime-light/coroutine/task.h | 8 +- 4 files changed, 184 insertions(+), 9 deletions(-) create mode 100644 runtime-light/coroutine/detail/coro-malloc-interface.h diff --git a/runtime-light/coroutine/coroutine-state.h b/runtime-light/coroutine/coroutine-state.h index b6d260c278..a0ce5fff21 100644 --- a/runtime-light/coroutine/coroutine-state.h +++ b/runtime-light/coroutine/coroutine-state.h @@ -4,8 +4,10 @@ #pragma once -#include "common/mixin/not_copyable.h" +#include +#include "common/mixin/not_copyable.h" +#include "runtime-common/core/allocator/runtime-allocator.h" #include "runtime-light/coroutine/async-stack.h" struct CoroutineInstanceState final : private vk::not_copyable { @@ -15,4 +17,6 @@ struct CoroutineInstanceState final : private vk::not_copyable { static CoroutineInstanceState& get() noexcept; kphp::coro::async_stack_root coroutine_stack_root; + + RuntimeAllocator coro_allocator{static_cast(1024 * 1024 * 8), static_cast(1024 * 1024 * 8), 0}; }; diff --git a/runtime-light/coroutine/detail/coro-malloc-interface.h b/runtime-light/coroutine/detail/coro-malloc-interface.h new file mode 100644 index 0000000000..81af83f052 --- /dev/null +++ b/runtime-light/coroutine/detail/coro-malloc-interface.h @@ -0,0 +1,171 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2025 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#pragma once + +#include +#include +#include +#include +#include + +#include "common/wrappers/likely.h" +#include "runtime-common/core/allocator/runtime-allocator.h" +#include "runtime-common/core/utils/kphp-assert-core.h" +#include "runtime-light/coroutine/coroutine-state.h" + +namespace kphp { + +namespace coro { + +namespace details { + +constexpr uint64_t MALLOC_REPLACER_MAX_ALLOC = 0xFFFFFF00; // 4GiB + +struct control_block { +private: + static constexpr auto SIZE_FIELD_BITSIZE{48}; + static constexpr auto BASE_OFFSET_FIELD_BITSIZE{16}; + static constexpr uint64_t BLOCK_SIZE_MASK{(1UL << SIZE_FIELD_BITSIZE) - 1}; + static constexpr uint64_t BASE_OFFSET_MASK{(1UL << BASE_OFFSET_FIELD_BITSIZE) - 1}; + + static_assert(SIZE_FIELD_BITSIZE + BASE_OFFSET_FIELD_BITSIZE == std::numeric_limits::digits); + +public: + static constexpr uint64_t max_size() noexcept { + return 1UL << SIZE_FIELD_BITSIZE; + } + + static constexpr uint64_t max_alignment() noexcept { + return 1UL << BASE_OFFSET_FIELD_BITSIZE; + } + + uint64_t raw() const noexcept { + return (static_cast(base_offset) << SIZE_FIELD_BITSIZE) | (static_cast(size) & BLOCK_SIZE_MASK); + } + + static control_block from_raw(uint64_t raw) noexcept { + return control_block{.size = raw & BLOCK_SIZE_MASK, .base_offset = static_cast((raw >> SIZE_FIELD_BITSIZE) & BASE_OFFSET_MASK)}; + } + + uint64_t size : SIZE_FIELD_BITSIZE; + uint16_t base_offset : BASE_OFFSET_FIELD_BITSIZE; +}; + +inline bool is_power_of_2(uint64_t v) noexcept { + return v && !(v & (v - 1)); +} + +static_assert(sizeof(control_block) == sizeof(uint64_t), "Control block's size must be equal to uint64"); + +} // namespace details + +inline void* alloc(size_t size) noexcept { + constexpr size_t cb_size{sizeof(details::control_block)}; + if (unlikely(size > std::min(details::control_block::max_size(), kphp::coro::details::MALLOC_REPLACER_MAX_ALLOC) - cb_size)) { + php_warning("attempt to allocate too much memory by malloc replacer, requested : %lu", size); + return nullptr; + } + const size_t total_size{size + cb_size}; + void* base{CoroutineInstanceState::get().coro_allocator.alloc_script_memory(total_size)}; + if (unlikely(base == nullptr)) { + php_warning("not enough script memory to allocate, requested : %lu, actual requested: %lu", size, total_size); + return base; + } + *(static_cast(base)) = details::control_block{.size = total_size, .base_offset = cb_size}.raw(); + return static_cast(static_cast(base) + cb_size); +} + +inline void* alloc_aligned(size_t size, std::align_val_t alignment) noexcept { + // Check that provided alignment is power of two + const size_t align{static_cast(alignment)}; + if (unlikely(align == 0 || !details::is_power_of_2(align) || align >= details::control_block::max_alignment())) { + php_warning("allocation alignment have to be non-zero power of two and not greater than %" PRIu64 ", got : %lu", details::control_block::max_alignment(), + align); + return nullptr; + } + + // Check that memory is enough + constexpr size_t cb_size{sizeof(details::control_block)}; + if (unlikely(size > std::min(details::control_block::max_size(), kphp::coro::details::MALLOC_REPLACER_MAX_ALLOC) - (align - 1) - cb_size)) { + php_warning("attempt to allocate too much memory by malloc replacer, requested : %lu", size); + return nullptr; + } + + // Request mem from underlying memory manager + const size_t total_size{size + (align - 1) + cb_size}; + void* base{CoroutineInstanceState::get().coro_allocator.alloc_script_memory(total_size)}; + if (unlikely(base == nullptr)) { + php_warning("not enough script memory to allocate, requested : %lu, actual requested: %lu", size, total_size); + return base; + } + + const uint64_t base_u{reinterpret_cast(base)}; + // The smallest multiple of `align` greater than or equal to requested memory + const uint64_t aligned_u{((base_u + cb_size) + (align - 1)) & ~(align - 1)}; + const uint64_t base_offset_u{aligned_u - base_u}; + + // Save control block + *(reinterpret_cast(aligned_u - cb_size)) = // NOLINT + details::control_block{.size = total_size, .base_offset = static_cast(base_offset_u)}.raw(); + + return reinterpret_cast(aligned_u); // NOLINT +} + +inline void* calloc(size_t num, size_t size) noexcept { + void* ptr{kphp::coro::alloc(num * size)}; + if (unlikely(ptr == nullptr)) { + return nullptr; + } + return std::memset(ptr, 0, num * size); +} + +inline void free(void* ptr) noexcept { + if (unlikely(ptr == nullptr)) { + return; + } + + constexpr size_t cb_size{sizeof(details::control_block)}; + const auto mem{reinterpret_cast(ptr)}; + + const auto cb{details::control_block::from_raw(*reinterpret_cast(mem - cb_size))}; // NOLINT + void* base{reinterpret_cast(mem - cb.base_offset)}; // NOLINT + + CoroutineInstanceState::get().coro_allocator.free_script_memory(base, cb.size); +} + +inline void* realloc(void* ptr, size_t new_size) noexcept { + if (unlikely(ptr == nullptr)) { + return kphp::coro::alloc(new_size); + } + + if (unlikely(new_size == 0)) { + kphp::coro::free(ptr); + return nullptr; + } + + constexpr size_t cb_size{sizeof(details::control_block)}; + const auto mem{reinterpret_cast(ptr)}; + + const auto cb{details::control_block::from_raw(*reinterpret_cast(mem - cb_size))}; // NOLINT + + void* old_base{reinterpret_cast(mem - cb.base_offset)}; // NOLINT + const size_t old_size{cb.size}; + + void* new_ptr{kphp::coro::alloc(new_size)}; + if (likely(new_ptr != nullptr)) { + std::memcpy(new_ptr, ptr, std::min(new_size, old_size)); + CoroutineInstanceState::get().coro_allocator.free_script_memory(old_base, old_size); + } + return new_ptr; +} + +inline char* strdup(const char* str1) noexcept { + auto* str2{static_cast(kphp::coro::alloc(std::strlen(str1) + 1))}; + return std::strcpy(str2, str1); +} + +} // namespace coro + +} // namespace kphp diff --git a/runtime-light/coroutine/shared-task.h b/runtime-light/coroutine/shared-task.h index 73a5dcc843..daa266f2cd 100644 --- a/runtime-light/coroutine/shared-task.h +++ b/runtime-light/coroutine/shared-task.h @@ -14,8 +14,8 @@ #include #include -#include "runtime-common/core/allocator/script-malloc-interface.h" #include "runtime-light/coroutine/async-stack.h" +#include "runtime-light/coroutine/detail/coro-malloc-interface.h" #include "runtime-light/coroutine/void-value.h" #include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" @@ -148,20 +148,20 @@ struct promise_base : kphp::coro::async_stack_element { auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc(n); + return kphp::coro::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc_aligned(n, al); + return kphp::coro::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; - kphp::memory::script::free(ptr); + kphp::coro::free(ptr); } private: diff --git a/runtime-light/coroutine/task.h b/runtime-light/coroutine/task.h index 64724e1032..1107262ff7 100644 --- a/runtime-light/coroutine/task.h +++ b/runtime-light/coroutine/task.h @@ -11,8 +11,8 @@ #include #include "common/containers/final_action.h" -#include "runtime-common/core/allocator/script-malloc-interface.h" #include "runtime-light/coroutine/async-stack.h" +#include "runtime-light/coroutine/detail/coro-malloc-interface.h" #include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" @@ -69,20 +69,20 @@ struct promise_base : kphp::coro::async_stack_element { auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc(n); + return kphp::coro::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc_aligned(n, al); + return kphp::coro::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; - kphp::memory::script::free(ptr); + kphp::coro::free(ptr); } void* m_next{}; From 147568050856feba410e9a06b519be8d445e8da8 Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Wed, 15 Jul 2026 18:55:54 +0300 Subject: [PATCH 8/8] added new allocator to other coro structs --- runtime-light/coroutine/detail/await-set.h | 14 +++++++------- .../coroutine/detail/task-self-deleting.h | 8 ++++---- runtime-light/coroutine/detail/when-all.h | 7 ++++--- runtime-light/coroutine/detail/when-any.h | 7 ++++--- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/runtime-light/coroutine/detail/await-set.h b/runtime-light/coroutine/detail/await-set.h index 56361fedb5..bfdf99723a 100644 --- a/runtime-light/coroutine/detail/await-set.h +++ b/runtime-light/coroutine/detail/await-set.h @@ -10,9 +10,9 @@ #include #include -#include "runtime-common/core/allocator/script-malloc-interface.h" #include "runtime-common/core/std/containers.h" #include "runtime-light/coroutine/async-stack.h" +#include "runtime-light/coroutine/detail/coro-malloc-interface.h" #include "runtime-light/coroutine/type-traits.h" #include "runtime-light/coroutine/void-value.h" #include "runtime-light/stdlib/cpu-info/cpu-info-state.h" @@ -61,20 +61,20 @@ class await_broker { void* operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc(n); + return kphp::coro::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc_aligned(n, al); + return kphp::coro::alloc_aligned(n, al); } void operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; - kphp::memory::script::free(ptr); + kphp::coro::free(ptr); } void start_task(await_set_task&& task, kphp::coro::async_stack_root& coroutine_stack_root, void* return_address) noexcept { @@ -179,20 +179,20 @@ class await_set_task_promise_base : public kphp::coro::async_stack_element { void* operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc(n); + return kphp::coro::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc_aligned(n, al); + return kphp::coro::alloc_aligned(n, al); } void operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; - kphp::memory::script::free(ptr); + kphp::coro::free(ptr); } std::suspend_always initial_suspend() const noexcept { diff --git a/runtime-light/coroutine/detail/task-self-deleting.h b/runtime-light/coroutine/detail/task-self-deleting.h index 48dfaa8998..32c79688af 100644 --- a/runtime-light/coroutine/detail/task-self-deleting.h +++ b/runtime-light/coroutine/detail/task-self-deleting.h @@ -8,10 +8,10 @@ #include #include -#include "runtime-common/core/allocator/script-malloc-interface.h" #include "runtime-light/coroutine/async-stack.h" #include "runtime-light/coroutine/concepts.h" #include "runtime-light/coroutine/coroutine-state.h" +#include "runtime-light/coroutine/detail/coro-malloc-interface.h" #include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" @@ -35,20 +35,20 @@ struct promise_self_deleting : kphp::coro::async_stack_element { auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc(n); + return kphp::coro::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc_aligned(n, al); + return kphp::coro::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; - kphp::memory::script::free(ptr); + kphp::coro::free(ptr); } auto get_return_object() noexcept -> task_self_deleting; diff --git a/runtime-light/coroutine/detail/when-all.h b/runtime-light/coroutine/detail/when-all.h index 598ddec6e8..a155bd3211 100644 --- a/runtime-light/coroutine/detail/when-all.h +++ b/runtime-light/coroutine/detail/when-all.h @@ -16,6 +16,7 @@ #include "runtime-light/coroutine/async-stack.h" #include "runtime-light/coroutine/concepts.h" +#include "runtime-light/coroutine/detail/coro-malloc-interface.h" #include "runtime-light/coroutine/type-traits.h" #include "runtime-light/coroutine/void-value.h" #include "runtime-light/stdlib/cpu-info/cpu-info-state.h" @@ -155,20 +156,20 @@ class when_all_task_promise_base : public kphp::coro::async_stack_element { auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc(n); + return kphp::coro::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc_aligned(n, al); + return kphp::coro::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; - kphp::memory::script::free(ptr); + kphp::coro::free(ptr); } auto initial_suspend() const noexcept -> std::suspend_always { diff --git a/runtime-light/coroutine/detail/when-any.h b/runtime-light/coroutine/detail/when-any.h index fa9d4e85c6..59c06e57d5 100644 --- a/runtime-light/coroutine/detail/when-any.h +++ b/runtime-light/coroutine/detail/when-any.h @@ -14,6 +14,7 @@ #include #include "runtime-light/coroutine/concepts.h" +#include "runtime-light/coroutine/detail/coro-malloc-interface.h" #include "runtime-light/coroutine/type-traits.h" #include "runtime-light/coroutine/void-value.h" #include "runtime-light/metaprogramming/type-functions.h" @@ -165,20 +166,20 @@ class when_any_task_promise_base : public kphp::coro::async_stack_element { auto operator new(size_t n, [[maybe_unused]] Args&&... args) noexcept -> void* { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc(n); + return kphp::coro::alloc(n); } template auto operator new(size_t n, std::align_val_t al, [[maybe_unused]] Args&&... args) noexcept -> void* { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_alloc_cycles)}; - return kphp::memory::script::alloc_aligned(n, al); + return kphp::coro::alloc_aligned(n, al); } auto operator delete(void* ptr, [[maybe_unused]] size_t n) noexcept -> void { auto& ciis{CpuInfoInstanceState::get()}; auto writer{ciis.write_cycles(ciis.coro_free_cycles)}; - kphp::memory::script::free(ptr); + kphp::coro::free(ptr); } auto initial_suspend() const noexcept -> std::suspend_always {