diff --git a/runtime-light/coroutine/detail/await-set.h b/runtime-light/coroutine/detail/await-set.h index 2c05be0c6e..3faff1d72a 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); } @@ -170,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/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..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"))) @@ -65,16 +68,50 @@ 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{}; - k2::instance_state()->init_script_execution(); + + { + auto guard = CpuInfoInstanceState::write_cycles(CpuInfoInstanceState::get().processing_cycles); + 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) { + 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/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..99630c6026 --- /dev/null +++ b/runtime-light/stdlib/cpu-info/cpu-info-state.h @@ -0,0 +1,38 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2026 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#pragma once + +#include + +#if defined(__x86_64__) || defined(__i386__) +#include +#endif + +#include "common/containers/final_action.h" + +class CpuInfoInstanceState { +private: + [[gnu::always_inline]] static uint64_t rdtsc() noexcept { +#if defined(__x86_64__) || defined(__i386__) + return __rdtsc(); +#else + return 0; +#endif + } + +public: + CpuInfoInstanceState() noexcept = default; + + static CpuInfoInstanceState& get() noexcept; + + [[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 processing_cycles{0}; + uint64_t coro_alloc_cycles{0}; + uint64_t coro_free_cycles{0}; +}; 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