From a41a141877539685b9b2dbf440ebd132e68a9351 Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Mon, 15 Jun 2026 18:18:19 +0300 Subject: [PATCH 1/6] [k2] add coroutine cpu cycles --- .../components/kphp/kphp-component.cpp | 22 ++++++++++ .../components/kphp/state/instance-state.h | 2 + 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 ++ .../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, 102 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/components/kphp/kphp-component.cpp b/runtime-light/components/kphp/kphp-component.cpp index c10c005230..7d25c89b44 100644 --- a/runtime-light/components/kphp/kphp-component.cpp +++ b/runtime-light/components/kphp/kphp-component.cpp @@ -11,6 +11,7 @@ #include "runtime-light/coroutine/io-scheduler.h" #include "runtime-light/k2-platform/k2-api.h" #include "runtime-light/k2-platform/k2-header.h" +#include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" #define VISIBILITY_DEFAULT __attribute__((visibility("default"))) @@ -56,6 +57,11 @@ VISIBILITY_DEFAULT void k2_init_instance() { k2::details::component_state_ptr = k2_component_state(); k2::details::instance_state_ptr = k2_instance_state(); 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(); } @@ -69,6 +75,22 @@ VISIBILITY_DEFAULT k2::PollStatus k2_poll() { k2::details::instance_state_ptr = k2_instance_state(); kphp::log::trace("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::trace("k2_poll finished: {}", std::to_underlying(poll_status)); return poll_status; } diff --git a/runtime-light/components/kphp/state/instance-state.h b/runtime-light/components/kphp/state/instance-state.h index 92beeab5bf..46c7cc0049 100644 --- a/runtime-light/components/kphp/state/instance-state.h +++ b/runtime-light/components/kphp/state/instance-state.h @@ -23,6 +23,7 @@ #include "runtime-light/server/job-worker/job-worker-server-state.h" #include "runtime-light/server/rpc/rpc-server-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" @@ -100,6 +101,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/coroutine/detail/await-set.h b/runtime-light/coroutine/detail/await-set.h index b45f177559..67575a0bc5 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/detail/allocator/coroutine-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" #include "runtime-light/stdlib/diagnostics/logs.h" namespace kphp::coro { @@ -53,15 +54,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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } diff --git a/runtime-light/coroutine/detail/task-self-deleting.h b/runtime-light/coroutine/detail/task-self-deleting.h index ec8ddea722..5b516dec60 100644 --- a/runtime-light/coroutine/detail/task-self-deleting.h +++ b/runtime-light/coroutine/detail/task-self-deleting.h @@ -13,6 +13,7 @@ #include "runtime-light/coroutine/concepts.h" #include "runtime-light/coroutine/coroutine-state.h" #include "runtime-light/coroutine/detail/allocator/coroutine-malloc-interface.h" +#include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" namespace kphp::coro::detail { @@ -35,15 +36,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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } diff --git a/runtime-light/coroutine/detail/when-all.h b/runtime-light/coroutine/detail/when-all.h index 186ba2793c..666ab5effe 100644 --- a/runtime-light/coroutine/detail/when-all.h +++ b/runtime-light/coroutine/detail/when-all.h @@ -19,6 +19,7 @@ #include "runtime-light/coroutine/detail/allocator/coroutine-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" #include "runtime-light/stdlib/diagnostics/logs.h" namespace kphp::coro::detail::when_all { @@ -153,15 +154,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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } diff --git a/runtime-light/coroutine/detail/when-any.h b/runtime-light/coroutine/detail/when-any.h index 8a90b1961f..e335a51775 100644 --- a/runtime-light/coroutine/detail/when-any.h +++ b/runtime-light/coroutine/detail/when-any.h @@ -18,6 +18,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 { @@ -163,15 +164,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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } diff --git a/runtime-light/coroutine/shared-task.h b/runtime-light/coroutine/shared-task.h index e6148b6217..42b9091560 100644 --- a/runtime-light/coroutine/shared-task.h +++ b/runtime-light/coroutine/shared-task.h @@ -19,6 +19,7 @@ #include "runtime-light/coroutine/async-stack.h" #include "runtime-light/coroutine/detail/allocator/coroutine-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" namespace kphp::coro { @@ -125,15 +126,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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } diff --git a/runtime-light/coroutine/task.h b/runtime-light/coroutine/task.h index 8e874c480c..0ce4cc0409 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-light/coroutine/async-stack.h" #include "runtime-light/coroutine/detail/allocator/coroutine-malloc-interface.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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } 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..186f5d354a --- /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/components/kphp/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 8776da4dc4..ab04b73bd9 100644 --- a/runtime-light/stdlib/stdlib.cmake +++ b/runtime-light/stdlib/stdlib.cmake @@ -2,6 +2,7 @@ prepend( RUNTIME_LIGHT_STDLIB_SRC stdlib/ confdata/confdata-functions.cpp + cpu-info/cpu-info-state.cpp crypto/crypto-functions.cpp diagnostics/backtrace.cpp diagnostics/php-assert.cpp From 3c728d0363fb29236c61e5fa50491f1bf888be45 Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Mon, 15 Jun 2026 18:22:21 +0300 Subject: [PATCH 2/6] 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 01f35734a4f0a09628a04567303ca4a4aa2c5742 Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Mon, 15 Jun 2026 18:25:24 +0300 Subject: [PATCH 3/6] minor fix --- runtime-light/components/kphp/kphp-component.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runtime-light/components/kphp/kphp-component.cpp b/runtime-light/components/kphp/kphp-component.cpp index 7d25c89b44..6d77b05fe9 100644 --- a/runtime-light/components/kphp/kphp-component.cpp +++ b/runtime-light/components/kphp/kphp-component.cpp @@ -85,9 +85,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 5e9260380c62e6e325620d6715caaa23c2ad9fe5 Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Tue, 7 Jul 2026 13:14:13 +0300 Subject: [PATCH 4/6] added sending of metrics --- .../components/kphp/kphp-component.cpp | 52 ++++++++++++------- runtime-light/coroutine/detail/await-set.h | 3 ++ .../stdlib/cpu-info/cpu-info-state.h | 40 ++++++-------- 3 files changed, 53 insertions(+), 42 deletions(-) diff --git a/runtime-light/components/kphp/kphp-component.cpp b/runtime-light/components/kphp/kphp-component.cpp index 6d77b05fe9..b5d36c3fc8 100644 --- a/runtime-light/components/kphp/kphp-component.cpp +++ b/runtime-light/components/kphp/kphp-component.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/components/kphp/state/component-state.h" @@ -13,6 +15,7 @@ #include "runtime-light/k2-platform/k2-header.h" #include "runtime-light/stdlib/cpu-info/cpu-info-state.h" #include "runtime-light/stdlib/diagnostics/logs.h" +#include "runtime-light/stdlib/diagnostics/metrics.h" #define VISIBILITY_DEFAULT __attribute__((visibility("default"))) @@ -58,11 +61,10 @@ VISIBILITY_DEFAULT void k2_init_instance() { k2::details::instance_state_ptr = k2_instance_state(); 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(); + { + auto guard = CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().processing_cycles); + k2::instance_state()->init_script_execution(); + } } VISIBILITY_DEFAULT k2::PollStatus k2_warmup() { @@ -70,26 +72,38 @@ VISIBILITY_DEFAULT k2::PollStatus k2_warmup() { } 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::trace("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::trace("k2_poll finished: {}", std::to_underlying(poll_status)); return poll_status; diff --git a/runtime-light/coroutine/detail/await-set.h b/runtime-light/coroutine/detail/await-set.h index 67575a0bc5..032f050a96 100644 --- a/runtime-light/coroutine/detail/await-set.h +++ b/runtime-light/coroutine/detail/await-set.h @@ -179,15 +179,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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } 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 70d43036a57742d65b8cecf74e98ebf4f4eda52a Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Thu, 9 Jul 2026 11:41:03 +0300 Subject: [PATCH 5/6] fix --- runtime-light/components/kphp/kphp-component.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/runtime-light/components/kphp/kphp-component.cpp b/runtime-light/components/kphp/kphp-component.cpp index b5d36c3fc8..c330cd0c66 100644 --- a/runtime-light/components/kphp/kphp-component.cpp +++ b/runtime-light/components/kphp/kphp-component.cpp @@ -72,16 +72,15 @@ VISIBILITY_DEFAULT k2::PollStatus k2_warmup() { } 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::trace("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 a1c7c97f2cecee0facadb0c13f086a2fa6a76367 Mon Sep 17 00:00:00 2001 From: Denis Zubarev Date: Tue, 14 Jul 2026 18:41:04 +0300 Subject: [PATCH 6/6] fix --- .../components/kphp/kphp-component.cpp | 28 ++++--------------- .../components/kphp/state/instance-state.cpp | 22 +++++++++++++++ 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 ++++-- .../stdlib/cpu-info/cpu-info-state.h | 20 +++++++++++-- 9 files changed, 87 insertions(+), 46 deletions(-) diff --git a/runtime-light/components/kphp/kphp-component.cpp b/runtime-light/components/kphp/kphp-component.cpp index c330cd0c66..e1ade63f1b 100644 --- a/runtime-light/components/kphp/kphp-component.cpp +++ b/runtime-light/components/kphp/kphp-component.cpp @@ -62,7 +62,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(); } } @@ -80,29 +81,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::trace("k2_poll finished: {}", std::to_underlying(poll_status)); return poll_status; diff --git a/runtime-light/components/kphp/state/instance-state.cpp b/runtime-light/components/kphp/state/instance-state.cpp index 7b332b13ce..36f3e8f4ae 100644 --- a/runtime-light/components/kphp/state/instance-state.cpp +++ b/runtime-light/components/kphp/state/instance-state.cpp @@ -26,6 +26,7 @@ #include "runtime-light/server/rpc/init-functions.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/time/time-functions.h" @@ -224,4 +225,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/coroutine/detail/await-set.h b/runtime-light/coroutine/detail/await-set.h index 032f050a96..aa0aa2e847 100644 --- a/runtime-light/coroutine/detail/await-set.h +++ b/runtime-light/coroutine/detail/await-set.h @@ -54,18 +54,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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } @@ -179,18 +182,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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } diff --git a/runtime-light/coroutine/detail/task-self-deleting.h b/runtime-light/coroutine/detail/task-self-deleting.h index 5b516dec60..6c04cd6e04 100644 --- a/runtime-light/coroutine/detail/task-self-deleting.h +++ b/runtime-light/coroutine/detail/task-self-deleting.h @@ -36,18 +36,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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } diff --git a/runtime-light/coroutine/detail/when-all.h b/runtime-light/coroutine/detail/when-all.h index 666ab5effe..d1d1f871b5 100644 --- a/runtime-light/coroutine/detail/when-all.h +++ b/runtime-light/coroutine/detail/when-all.h @@ -154,18 +154,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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } diff --git a/runtime-light/coroutine/detail/when-any.h b/runtime-light/coroutine/detail/when-any.h index e335a51775..25ce005599 100644 --- a/runtime-light/coroutine/detail/when-any.h +++ b/runtime-light/coroutine/detail/when-any.h @@ -164,18 +164,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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } diff --git a/runtime-light/coroutine/shared-task.h b/runtime-light/coroutine/shared-task.h index 42b9091560..9e0f700590 100644 --- a/runtime-light/coroutine/shared-task.h +++ b/runtime-light/coroutine/shared-task.h @@ -126,18 +126,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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } diff --git a/runtime-light/coroutine/task.h b/runtime-light/coroutine/task.h index 0ce4cc0409..3f08c4d91c 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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } 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}; };