1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 00:47:45 +00:00

LibAudio: Avoid frequent read() calls in FLAC residual decode

Decoding the residual in FLAC subframes is by far the most I/O-heavy
operation in FLAC decoding, as the residual data makes up the majority
of subframe data in LPC subframes. As the residual consists of many
Rice-encoded numbers with different bit sizes for differently large
numbers, the residual decoder frequently reads only one or two bytes at
a time. As we use a normal FileInputStream, that directly translates to
many calls to the read() syscall. We can see that the I/O overhead while
FLAC decoding is quite large, and much time is spent in the read()
syscall's kernel code.

This is optimized by using a Buffered<FileInputStream> instead, leading
to 4K blocks being read at once and a large reduction in I/O overhead.

Benchmarking with the new abench utility gives a 15-20% speedup on
identical files, usually pushing FLAC decoding to 10-15x realtime speed
on common sample rates.
This commit is contained in:
kleines Filmröllchen 2021-10-03 11:39:17 +02:00 committed by Brian Gianforcaro
parent cbb2b4fe71
commit 14d330faba
2 changed files with 16 additions and 7 deletions

View file

@ -28,9 +28,14 @@ FlacLoaderPlugin::FlacLoaderPlugin(StringView path)
return; return;
} }
m_stream = make<FlacInputStream>(Core::InputFileStream(*m_file)); auto maybe_stream = Core::InputFileStream::open_buffered(path);
if (maybe_stream.is_error()) {
m_error_string = "Can't open file stream";
return;
}
m_stream = make<FlacInputStream>(maybe_stream.release_value());
if (!m_stream) { if (!m_stream) {
m_error_string = String::formatted("Can't open memory stream"); m_error_string = "Can't open file stream";
return; return;
} }
@ -69,7 +74,7 @@ bool FlacLoaderPlugin::parse_header()
InputBitStream bit_input = [&]() -> InputBitStream { InputBitStream bit_input = [&]() -> InputBitStream {
if (m_file) { if (m_file) {
return InputBitStream(m_stream->get<Core::InputFileStream>()); return InputBitStream(m_stream->get<Buffered<Core::InputFileStream>>());
} }
return InputBitStream(m_stream->get<InputMemoryStream>()); return InputBitStream(m_stream->get<InputMemoryStream>());
}(); }();

View file

@ -10,6 +10,7 @@
#include "FlacTypes.h" #include "FlacTypes.h"
#include "Loader.h" #include "Loader.h"
#include <AK/BitStream.h> #include <AK/BitStream.h>
#include <AK/Buffered.h>
#include <AK/Stream.h> #include <AK/Stream.h>
#include <AK/Types.h> #include <AK/Types.h>
#include <AK/Variant.h> #include <AK/Variant.h>
@ -17,16 +18,19 @@
namespace Audio { namespace Audio {
class FlacInputStream : public Variant<Core::InputFileStream, InputMemoryStream> { class FlacInputStream : public Variant<Buffered<Core::InputFileStream>, InputMemoryStream> {
public: public:
using Variant<Core::InputFileStream, InputMemoryStream>::Variant; using Variant<Buffered<Core::InputFileStream>, InputMemoryStream>::Variant;
bool seek(size_t pos) bool seek(size_t pos)
{ {
return this->visit( return this->visit(
[&](Core::InputFileStream& stream) { [&](Buffered<Core::InputFileStream>& buffered) {
return stream.seek(pos); // Discard the buffer, then seek normally.
if (!buffered.discard_or_error(buffered.buffered()))
return false;
return buffered.underlying_stream().seek(pos);
}, },
[&](InputMemoryStream& stream) { [&](InputMemoryStream& stream) {
if (pos >= stream.bytes().size()) { if (pos >= stream.bytes().size()) {