Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev: Add RecentMessages benchmark #5071

Merged
merged 13 commits into from
Jan 7, 2024
4 changes: 4 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,9 @@ CheckOptions:
- key: readability-identifier-naming.LocalPointerIgnoredRegexp
value: ^L$

# Benchmarks
- key: readability-identifier-naming.FunctionIgnoredRegexp
value: ^BM_[^_]+$

- key: misc-const-correctness.AnalyzeValues
value: false
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
# JSON resources should not be prettified...
resources/*.json
benchmarks/resources/*.json
tests/resources/*.json
# ...themes should be prettified for readability.
!resources/themes/*.json

# Ignore submodule files
lib/*/
conan-pkgs/*/
cmake/sanitizers-cmake/
tools/crash-handler

# Build folders
*build-*/
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
- Dev: Autogenerate docs/plugin-meta.lua. (#5055)
- Dev: Removed duplicate scale in settings dialog. (#5069)
- Dev: Fix `NotebookTab` emitting updates for every message. (#5068)
- Dev: Added benchmark for parsing and building recent messages. (#5071)

## 2.4.6

Expand Down
20 changes: 13 additions & 7 deletions benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
project(chatterino-benchmark)

set(benchmark_SOURCES
${CMAKE_CURRENT_LIST_DIR}/src/main.cpp
${CMAKE_CURRENT_LIST_DIR}/src/Emojis.cpp
${CMAKE_CURRENT_LIST_DIR}/src/Highlights.cpp
${CMAKE_CURRENT_LIST_DIR}/src/FormatTime.cpp
${CMAKE_CURRENT_LIST_DIR}/src/Helpers.cpp
${CMAKE_CURRENT_LIST_DIR}/src/LimitedQueue.cpp
${CMAKE_CURRENT_LIST_DIR}/src/LinkParser.cpp
src/main.cpp
resources/bench.qrc

src/Emojis.cpp
src/Highlights.cpp
src/FormatTime.cpp
src/Helpers.cpp
src/LimitedQueue.cpp
src/LinkParser.cpp
src/RecentMessages.cpp
# Add your new file above this line!
)

list(TRANSFORM benchmark_SOURCES PREPEND "${CMAKE_CURRENT_LIST_DIR}/")
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved

add_executable(${PROJECT_NAME} ${benchmark_SOURCES})
add_sanitizers(${PROJECT_NAME})

Expand All @@ -27,4 +32,5 @@ set_target_properties(${PROJECT_NAME}
RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin"
RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin"
RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin"
AUTORCC ON
)
6 changes: 6 additions & 0 deletions benchmarks/resources/bench.qrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/bench">
<file>recentmessages-nymn.json</file>
<file>seventvemotes-nymn.json</file>
</qresource>
</RCC>
1 change: 1 addition & 0 deletions benchmarks/resources/recentmessages-nymn.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions benchmarks/resources/seventvemotes-nymn.json

Large diffs are not rendered by default.

173 changes: 173 additions & 0 deletions benchmarks/src/RecentMessages.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#include "common/Literals.hpp"
#include "controllers/accounts/AccountController.hpp"
#include "controllers/highlights/HighlightController.hpp"
#include "messages/Emote.hpp"
#include "mocks/EmptyApplication.hpp"
#include "mocks/TwitchIrcServer.hpp"
#include "mocks/UserData.hpp"
#include "providers/bttv/BttvEmotes.hpp"
#include "providers/chatterino/ChatterinoBadges.hpp"
#include "providers/ffz/FfzBadges.hpp"
#include "providers/ffz/FfzEmotes.hpp"
#include "providers/recentmessages/Impl.hpp"
#include "providers/seventv/SeventvBadges.hpp"
#include "providers/seventv/SeventvEmotes.hpp"
#include "providers/twitch/TwitchChannel.hpp"
#include "singletons/Emotes.hpp"
#include "singletons/Resources.hpp"

#include <benchmark/benchmark.h>
#include <QFile>
#include <QJsonArray>
#include <QJsonDocument>
#include <QString>

#include <optional>

using namespace chatterino;
using namespace literals;

namespace {

class MockApplication : mock::EmptyApplication
{
public:
IEmotes *getEmotes() override
{
return &this->emotes;
}

IUserDataController *getUserData() override
{
return &this->userData;
}

AccountController *getAccounts() override
{
return &this->accounts;
}

ITwitchIrcServer *getTwitch() override
{
return &this->twitch;
}

ChatterinoBadges *getChatterinoBadges() override
{
return &this->chatterinoBadges;
}

FfzBadges *getFfzBadges() override
{
return &this->ffzBadges;
}

SeventvBadges *getSeventvBadges() override
{
return &this->seventvBadges;
}

HighlightController *getHighlights() override
{
return &this->highlights;
}

AccountController accounts;
Emotes emotes;
mock::UserDataController userData;
mock::MockTwitchIrcServer twitch;
ChatterinoBadges chatterinoBadges;
FfzBadges ffzBadges;
SeventvBadges seventvBadges;
HighlightController highlights;
};

// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
std::unique_ptr<MockApplication> APP;

void doSetup(const benchmark::State & /*state*/)
{
APP = std::make_unique<MockApplication>();
}

void doTeardown(const benchmark::State & /*state*/)
{
APP = {};
}

std::optional<QJsonDocument> tryReadJsonFile(const QString &path)
{
QFile file(path);
if (!file.open(QFile::ReadOnly))
{
return std::nullopt;
}

QJsonParseError e;
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
auto doc = QJsonDocument::fromJson(file.readAll(), &e);
if (e.error != QJsonParseError::NoError)
{
return std::nullopt;
}

return doc;
}

QJsonDocument readJsonFile(const QString &path)
{
auto opt = tryReadJsonFile(path);
if (!opt)
{
_exit(1);
}
return *opt;
}

void BM_RecentMessages(benchmark::State &state, const QString &name)
{
initResources();
TwitchChannel chan("nymn");

const auto seventvEmotes =
tryReadJsonFile(u":/bench/seventvemotes-%1.json"_s.arg(name));
const auto bttvEmotes =
tryReadJsonFile(u":/bench/bttvemotes-%1.json"_s.arg(name));
const auto ffzEmotes =
tryReadJsonFile(u":/bench/ffzemotes-%1.json"_s.arg(name));

if (seventvEmotes)
{
chan.setSeventvEmotes(
std::make_shared<const EmoteMap>(seventv::detail::parseEmotes(
seventvEmotes->object()["emote_set"_L1]["emotes"_L1].toArray(),
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
false)));
}

if (bttvEmotes)
{
chan.setBttvEmotes(std::make_shared<const EmoteMap>(
bttv::detail::parseChannelEmotes(bttvEmotes->object(), name)));
}

if (ffzEmotes)
{
chan.setFfzEmotes(std::make_shared<const EmoteMap>(
ffz::detail::parseChannelEmotes(ffzEmotes->object())));
}

auto messages = readJsonFile(u":/bench/recentmessages-%1.json"_s.arg(name));

for (auto _ : state)
{
auto parsed =
recentmessages::detail::parseRecentMessages(messages.object());
auto built = recentmessages::detail::buildRecentMessages(parsed, &chan);
benchmark::DoNotOptimize(built);
}
}

} // namespace

BENCHMARK_CAPTURE(BM_RecentMessages, nymn, u"nymn"_s)
->Setup(doSetup)
->Teardown(doTeardown);
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
8 changes: 5 additions & 3 deletions src/controllers/ignores/IgnoreController.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ bool isIgnoredMessage(IgnoredMessageParameters &&params)
{
auto sourceUserID = params.twitchUserID;

bool isBlocked =
getApp()->accounts->twitch.getCurrent()->blockedUserIds().contains(
sourceUserID);
bool isBlocked = getIApp()
->getAccounts()
->twitch.getCurrent()
->blockedUserIds()
.contains(sourceUserID);
if (isBlocked)
{
switch (static_cast<ShowIgnoredUsersMessages>(
Expand Down
53 changes: 27 additions & 26 deletions src/providers/bttv/BttvEmotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,30 +142,32 @@ namespace {
return anyModifications;
}

std::pair<Outcome, EmoteMap> parseChannelEmotes(
const QJsonObject &jsonRoot, const QString &channelDisplayName)
{
auto emotes = EmoteMap();
} // namespace

auto innerParse = [&jsonRoot, &emotes,
&channelDisplayName](const char *key) {
auto jsonEmotes = jsonRoot.value(key).toArray();
for (auto jsonEmote_ : jsonEmotes)
{
auto emote = createChannelEmote(channelDisplayName,
jsonEmote_.toObject());
using namespace bttv::detail;

emotes[emote.name] =
cachedOrMake(std::move(emote.emote), emote.id);
}
};
EmoteMap bttv::detail::parseChannelEmotes(const QJsonObject &jsonRoot,
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
const QString &channelDisplayName)
{
auto emotes = EmoteMap();

innerParse("channelEmotes");
innerParse("sharedEmotes");
auto innerParse = [&jsonRoot, &emotes,
&channelDisplayName](const char *key) {
auto jsonEmotes = jsonRoot.value(key).toArray();
for (auto jsonEmote_ : jsonEmotes)
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
{
auto emote =
createChannelEmote(channelDisplayName, jsonEmote_.toObject());

return {Success, std::move(emotes)};
}
} // namespace
emotes[emote.name] = cachedOrMake(std::move(emote.emote), emote.id);
}
};

innerParse("channelEmotes");
innerParse("sharedEmotes");

return emotes;
}

//
// BttvEmotes
Expand Down Expand Up @@ -230,14 +232,13 @@ void BttvEmotes::loadChannel(std::weak_ptr<Channel> channel,
.timeout(20000)
.onSuccess([callback = std::move(callback), channel, channelDisplayName,
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
manualRefresh](auto result) {
auto pair =
auto emotes =
parseChannelEmotes(result.parseJson(), channelDisplayName);
bool hasEmotes = false;
if (pair.first)
{
hasEmotes = !pair.second.empty();
callback(std::move(pair.second));
}

hasEmotes = !emotes.empty();
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
callback(std::move(emotes));

if (auto shared = channel.lock(); manualRefresh)
{
if (hasEmotes)
Expand Down
9 changes: 9 additions & 0 deletions src/providers/bttv/BttvEmotes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include "common/Aliases.hpp"
#include "common/Atomic.hpp"

#include <QJsonObject>

#include <memory>
#include <optional>

Expand All @@ -15,6 +17,13 @@ class Channel;
struct BttvLiveUpdateEmoteUpdateAddMessage;
struct BttvLiveUpdateEmoteRemoveMessage;

namespace bttv::detail {

EmoteMap parseChannelEmotes(const QJsonObject &jsonRoot,
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
const QString &channelDisplayName);

} // namespace bttv::detail

class BttvEmotes final
{
static constexpr const char *globalEmoteApiUrl =
Expand Down
21 changes: 12 additions & 9 deletions src/providers/ffz/FfzEmotes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,21 @@ namespace {
return authorityBadge;
}

EmoteMap parseChannelEmotes(const QJsonObject &jsonRoot)
{
auto emotes = EmoteMap();
} // namespace

for (const auto emoteSetRef : jsonRoot["sets"].toObject())
{
parseEmoteSetInto(emoteSetRef.toObject(), "Channel", emotes);
}
using namespace ffz::detail;

return emotes;
EmoteMap ffz::detail::parseChannelEmotes(const QJsonObject &jsonRoot)
Nerixyz marked this conversation as resolved.
Show resolved Hide resolved
{
auto emotes = EmoteMap();

for (const auto emoteSetRef : jsonRoot["sets"].toObject())
{
parseEmoteSetInto(emoteSetRef.toObject(), "Channel", emotes);
}
} // namespace

return emotes;
}

FfzEmotes::FfzEmotes()
: global_(std::make_shared<EmoteMap>())
Expand Down
Loading
Loading