diff --git a/NEWS b/NEWS index 57bbefc880..1efe2729a6 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,7 @@ ver 0.25 (not yet released) - pipewire: add option "reconnect_stream" * player - support replay gain parameter in stream URI + - preallocate physical RAM for audio buffer when playback starts * switch to C++23 * require Meson 1.2 diff --git a/src/MusicBuffer.hxx b/src/MusicBuffer.hxx index b509e092a8..13c8203c91 100644 --- a/src/MusicBuffer.hxx +++ b/src/MusicBuffer.hxx @@ -1,8 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later // Copyright The Music Player Daemon Project -#ifndef MPD_MUSIC_BUFFER_HXX -#define MPD_MUSIC_BUFFER_HXX +#pragma once #include "MusicChunk.hxx" #include "MusicChunkPtr.hxx" @@ -29,9 +28,10 @@ public: #ifndef NDEBUG /** - * Check whether the buffer is empty. This call is not - * protected with the mutex, and may only be used while this - * object is inaccessible to other threads. + * Check whether the buffer is empty. + * + * This call is not protected with the #mutex, and may only be + * used while this object is inaccessible to other threads. */ bool IsEmptyUnsafe() const { return buffer.empty(); @@ -45,14 +45,27 @@ public: /** * Returns the total number of reserved chunks in this buffer. This - * is the same value which was passed to the constructor - * music_buffer_new(). + * is the same value which was passed to the constructor. */ [[gnu::pure]] unsigned GetSize() const noexcept { return buffer.GetCapacity(); } + void PopulateMemory() noexcept { + buffer.PopulateMemory(); + } + + /** + * Give all memory allocations back to the kernel. + * + * This call is not protected with the #mutex, and may only be + * used while this object is inaccessible to other threads. + */ + void DiscardMemory() noexcept { + buffer.DiscardMemory(); + } + /** * Allocates a chunk from the buffer. When it is not used anymore, * call Return(). @@ -68,5 +81,3 @@ public: */ void Return(MusicChunk *chunk) noexcept; }; - -#endif diff --git a/src/event/Backend.hxx b/src/event/Backend.hxx index e2194bbfdc..7aa54ed476 100644 --- a/src/event/Backend.hxx +++ b/src/event/Backend.hxx @@ -1,10 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-or-later // Copyright The Music Player Daemon Project -#ifndef EVENT_BACKEND_HXX -#define EVENT_BACKEND_HXX +#pragma once -#include "event/Features.h" +#include "event/config.h" #ifdef _WIN32 @@ -22,5 +21,3 @@ using EventPollBackend = EpollBackend; using EventPollBackend = PollBackend; #endif - -#endif diff --git a/src/event/BackendEvents.hxx b/src/event/BackendEvents.hxx index ed183f5e37..87975d5f42 100644 --- a/src/event/BackendEvents.hxx +++ b/src/event/BackendEvents.hxx @@ -2,7 +2,7 @@ #pragma once -#include "event/Features.h" +#include "event/config.h" // for USE_EPOLL #ifdef _WIN32 diff --git a/src/event/FineTimerEvent.hxx b/src/event/FineTimerEvent.hxx index 536540511a..0977e24ca7 100644 --- a/src/event/FineTimerEvent.hxx +++ b/src/event/FineTimerEvent.hxx @@ -5,7 +5,6 @@ #pragma once #include "Chrono.hxx" -#include "event/Features.h" #include "util/BindMethod.hxx" #include "util/IntrusiveTreeSet.hxx" diff --git a/src/event/Loop.hxx b/src/event/Loop.hxx index 6301899d74..ebe05c3e69 100644 --- a/src/event/Loop.hxx +++ b/src/event/Loop.hxx @@ -7,9 +7,9 @@ #include "Chrono.hxx" #include "TimerWheel.hxx" #include "Backend.hxx" -#include "event/Features.h" #include "time/ClockCache.hxx" #include "util/IntrusiveList.hxx" +#include "event/config.h" #ifndef NO_FINE_TIMER_EVENT #include "TimerList.hxx" diff --git a/src/event/MultiSocketMonitor.hxx b/src/event/MultiSocketMonitor.hxx index 6b9c1ffad1..5e776401b0 100644 --- a/src/event/MultiSocketMonitor.hxx +++ b/src/event/MultiSocketMonitor.hxx @@ -1,13 +1,12 @@ // SPDX-License-Identifier: GPL-2.0-or-later // Copyright The Music Player Daemon Project -#ifndef MPD_MULTI_SOCKET_MONITOR_HXX -#define MPD_MULTI_SOCKET_MONITOR_HXX +#pragma once #include "IdleEvent.hxx" #include "FineTimerEvent.hxx" #include "SocketEvent.hxx" -#include "event/Features.h" +#include "event/config.h" #include #include @@ -244,5 +243,3 @@ private: void OnIdle() noexcept; }; - -#endif diff --git a/src/event/SignalMonitor.cxx b/src/event/SignalMonitor.cxx index 7182050c0e..134ea932a3 100644 --- a/src/event/SignalMonitor.cxx +++ b/src/event/SignalMonitor.cxx @@ -2,7 +2,7 @@ // Copyright The Music Player Daemon Project #include "SignalMonitor.hxx" -#include "event/Features.h" +#include "event/config.h" #ifndef _WIN32 diff --git a/src/event/SocketEvent.cxx b/src/event/SocketEvent.cxx index 8b4eac7bb3..b2b333e7b0 100644 --- a/src/event/SocketEvent.cxx +++ b/src/event/SocketEvent.cxx @@ -3,7 +3,6 @@ #include "SocketEvent.hxx" #include "Loop.hxx" -#include "event/Features.h" #include #include diff --git a/src/event/SocketEvent.hxx b/src/event/SocketEvent.hxx index 63de91a42a..bc7e49c4aa 100644 --- a/src/event/SocketEvent.hxx +++ b/src/event/SocketEvent.hxx @@ -4,7 +4,7 @@ #pragma once #include "BackendEvents.hxx" -#include "event/Features.h" // for USE_EPOLL +#include "event/config.h" // for USE_EPOLL #include "net/SocketDescriptor.hxx" #include "util/BindMethod.hxx" #include "util/IntrusiveList.hxx" diff --git a/src/event/TimerList.cxx b/src/event/TimerList.cxx index 21cb9a4039..03cd433cd0 100644 --- a/src/event/TimerList.cxx +++ b/src/event/TimerList.cxx @@ -11,7 +11,7 @@ TimerList::GetDue::operator()(const FineTimerEvent &timer) const noexcept return timer.GetDue(); } -TimerList::TimerList() = default; +TimerList::TimerList() noexcept = default; TimerList::~TimerList() noexcept { diff --git a/src/event/TimerList.hxx b/src/event/TimerList.hxx index 90f048fc60..a4179438dc 100644 --- a/src/event/TimerList.hxx +++ b/src/event/TimerList.hxx @@ -5,7 +5,6 @@ #pragma once #include "Chrono.hxx" -#include "event/Features.h" #include "util/IntrusiveTreeSet.hxx" class FineTimerEvent; @@ -22,7 +21,7 @@ class TimerList final { IntrusiveTreeSetOperators> timers; public: - TimerList(); + TimerList() noexcept; ~TimerList() noexcept; TimerList(const TimerList &other) = delete; diff --git a/src/event/WakeFD.hxx b/src/event/WakeFD.hxx index 1f91eb27a3..3099ecd98f 100644 --- a/src/event/WakeFD.hxx +++ b/src/event/WakeFD.hxx @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later // Copyright The Music Player Daemon Project -#ifndef MPD_WAKE_FD_HXX -#define MPD_WAKE_FD_HXX +#pragma once #include "net/SocketDescriptor.hxx" -#include "event/Features.h" +#include "event/config.h" #ifdef USE_EVENTFD #include "system/EventFD.hxx" @@ -37,5 +36,3 @@ public: fd.Write(); } }; - -#endif diff --git a/src/event/meson.build b/src/event/meson.build index ff5818c9e2..5b5a4bcc36 100644 --- a/src/event/meson.build +++ b/src/event/meson.build @@ -3,7 +3,7 @@ event_features.set('USE_EVENTFD', is_linux and get_option('eventfd')) event_features.set('USE_SIGNALFD', is_linux and get_option('signalfd')) event_features.set('USE_EPOLL', is_linux and get_option('epoll')) event_features.set('HAVE_THREADED_EVENT_LOOP', true) -configure_file(output: 'Features.h', configuration: event_features) +configure_file(output: 'config.h', configuration: event_features) event_sources = [] diff --git a/src/memory/HugeAllocator.cxx b/src/memory/HugeAllocator.cxx index eda938bd76..a1c797d013 100644 --- a/src/memory/HugeAllocator.cxx +++ b/src/memory/HugeAllocator.cxx @@ -43,6 +43,13 @@ HugeForkCow(std::span p, bool enable) noexcept EnablePageFork(AlignToPageSize(p), enable); } +void +HugePopulate(std::span p) noexcept +{ + PagesPopulateWrite(p); + CollapseHugePages(p); +} + void HugeDiscard(std::span p) noexcept { diff --git a/src/memory/HugeAllocator.hxx b/src/memory/HugeAllocator.hxx index b9c22b0c12..2b796ffa75 100644 --- a/src/memory/HugeAllocator.hxx +++ b/src/memory/HugeAllocator.hxx @@ -43,6 +43,13 @@ HugeSetName(std::span p, const char *name) noexcept; void HugeForkCow(std::span p, bool enable) noexcept; +/** + * Populate (prefault) page tables writable, faulting in all pages in + * the range just as if manually writing to each each page. + */ +void +HugePopulate(std::span p) noexcept; + /** * Discard any data stored in the allocation and give the memory back * to the kernel. After returning, the allocation still exists and @@ -75,6 +82,12 @@ HugeForkCow(std::span, bool) noexcept { } +static inline void +HugePopulate(std::span p) noexcept +{ + VirtualAlloc(p.data(), p.size(), MEM_COMMIT|MEM_RESERVE, PAGE_NOACCESS); +} + static inline void HugeDiscard(std::span p) noexcept { @@ -109,6 +122,11 @@ HugeForkCow(std::span, bool) noexcept { } +static inline void +HugePopulate(std::span) noexcept +{ +} + static inline void HugeDiscard(std::span) noexcept { diff --git a/src/memory/HugeArray.hxx b/src/memory/HugeArray.hxx index ff59f7dd5d..16db9725e5 100644 --- a/src/memory/HugeArray.hxx +++ b/src/memory/HugeArray.hxx @@ -6,6 +6,10 @@ #include "HugeAllocator.hxx" #include "util/SpanCast.hxx" +#ifdef __linux__ +#include "system/PageAllocator.hxx" +#endif + #include /** @@ -51,6 +55,10 @@ public: HugeForkCow(std::as_writable_bytes(buffer), enable); } + void Populate() noexcept { + HugePopulate(std::as_writable_bytes(buffer)); + } + void Discard() noexcept { HugeDiscard(std::as_writable_bytes(buffer)); } diff --git a/src/memory/SliceBuffer.hxx b/src/memory/SliceBuffer.hxx index f93089e96d..b530fe72c2 100644 --- a/src/memory/SliceBuffer.hxx +++ b/src/memory/SliceBuffer.hxx @@ -72,6 +72,10 @@ public: buffer.SetName(name); } + void PopulateMemory() noexcept { + buffer.Populate(); + } + void DiscardMemory() noexcept { assert(empty()); @@ -120,11 +124,5 @@ public: slice->next = available; available = slice; --n_allocated; - - /* give memory back to the kernel when the last slice - was freed */ - if (n_allocated == 0) { - DiscardMemory(); - } } }; diff --git a/src/player/Thread.cxx b/src/player/Thread.cxx index 7c47763292..5a8ecc4d32 100644 --- a/src/player/Thread.cxx +++ b/src/player/Thread.cxx @@ -1271,6 +1271,12 @@ try { { const ScopeUnlock unlock(mutex); + + /* allocate physical RAM for the whole + buffer to reduce page faults + later */ + buffer.PopulateMemory(); + do_play(*this, dc, buffer); /* give the main thread a chance to @@ -1306,6 +1312,7 @@ try { CommandFinished(); assert(buffer.IsEmptyUnsafe()); + buffer.DiscardMemory(); break;