diff --git a/runtime-light/components/kphp/kphp-component.cpp b/runtime-light/components/kphp/kphp-component.cpp index c10c005230..e1ade63f1b 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" @@ -11,7 +13,9 @@ #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" +#include "runtime-light/stdlib/diagnostics/metrics.h" #define VISIBILITY_DEFAULT __attribute__((visibility("default"))) @@ -56,7 +60,12 @@ 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{}; - k2::instance_state()->init_script_execution(); + + { + auto& ciis{CpuInfoInstanceState::get()}; + auto guard{ciis.write_cycles(ciis.processing_cycles)}; + k2::instance_state()->init_script_execution(); + } } VISIBILITY_DEFAULT k2::PollStatus k2_warmup() { @@ -68,7 +77,15 @@ VISIBILITY_DEFAULT k2::PollStatus k2_poll() { 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()}; + + auto& cpu_info_instance_state{CpuInfoInstanceState::get()}; + k2::PollStatus poll_status{}; + { + 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(); + 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/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..aa0aa2e847 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,21 @@ class await_broker { template 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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } @@ -175,15 +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& 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& 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& 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 ec8ddea722..6c04cd6e04 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,21 @@ struct promise_self_deleting : kphp::coro::async_stack_element { template 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::coro::detail::memory::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::coro::detail::memory::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::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..d1d1f871b5 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,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& 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& 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& 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 8a90b1961f..25ce005599 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,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& 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& 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& 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 e6148b6217..9e0f700590 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,21 @@ struct promise_base : kphp::coro::async_stack_element { template 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::coro::detail::memory::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::coro::detail::memory::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::coro::detail::memory::free(ptr); } diff --git a/runtime-light/coroutine/task.h b/runtime-light/coroutine/task.h index 8e874c480c..3f08c4d91c 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,21 @@ struct promise_base : kphp::coro::async_stack_element { template 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::coro::detail::memory::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::coro::detail::memory::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::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..776afdd133 --- /dev/null +++ b/runtime-light/stdlib/cpu-info/cpu-info-state.h @@ -0,0 +1,54 @@ +// 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 + +#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]] 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}; +}; 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