Skip to content

Commit

Permalink
[Impeller] Affinity adjustments for Vulkan backend. (#46063)
Browse files Browse the repository at this point in the history
Runs the waiter threads with efficiency affinity and the worker thread with "not performance" affinity.
  • Loading branch information
jonahwilliams authored and harryterkelsen committed Oct 23, 2023
1 parent 58b183f commit 601745d
Show file tree
Hide file tree
Showing 10 changed files with 83 additions and 19 deletions.
22 changes: 21 additions & 1 deletion fml/cpu_affinity.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,34 @@
// found in the LICENSE file.

#include "flutter/fml/cpu_affinity.h"
#include "flutter/fml/build_config.h"

#include <fstream>
#include <optional>
#include <string>

#ifdef FML_OS_ANDROID
#include "flutter/fml/platform/android/cpu_affinity.h"
#endif // FML_OS_ANDROID

namespace fml {

std::optional<size_t> EfficiencyCoreCount() {
#ifdef FML_OS_ANDROID
return AndroidEfficiencyCoreCount();
#else
return std::nullopt;
#endif
}

bool RequestAffinity(CpuAffinity affinity) {
#ifdef FML_OS_ANDROID
return AndroidRequestAffinity(affinity);
#else
return true;
#endif
}

CPUSpeedTracker::CPUSpeedTracker(std::vector<CpuIndexAndSpeed> data)
: cpu_speeds_(std::move(data)) {
std::optional<int64_t> max_speed = std::nullopt;
Expand Down Expand Up @@ -61,7 +82,6 @@ const std::vector<size_t>& CPUSpeedTracker::GetIndices(
// required because files under /proc do not always return a valid size
// when using fseek(0, SEEK_END) + ftell(). Nor can they be mmap()-ed.
std::optional<int64_t> ReadIntFromFile(const std::string& path) {
// size_t data_length = 0u;
std::ifstream file;
file.open(path.c_str());

Expand Down
18 changes: 18 additions & 0 deletions fml/cpu_affinity.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,24 @@ enum class CpuAffinity {
kNotPerformance,
};

/// @brief Request count of efficiency cores.
///
/// Efficiency cores are defined as those with the lowest reported
/// cpu_max_freq. If the CPU speed could not be determined, or if all
/// cores have the same reported speed then this returns std::nullopt.
/// That is, the result will never be 0.
std::optional<size_t> EfficiencyCoreCount();

/// @brief Request the given affinity for the current thread.
///
/// Returns true if successfull, or if it was a no-op. This function is
/// only supported on Android devices.
///
/// Affinity requests are based on documented CPU speed. This speed data
/// is parsed from cpuinfo_max_freq files, see also:
/// https://www.kernel.org/doc/Documentation/cpu-freq/user-guide.txt
bool RequestAffinity(CpuAffinity affinity);

struct CpuIndexAndSpeed {
// The index of the given CPU.
size_t index;
Expand Down
5 changes: 5 additions & 0 deletions fml/cpu_affinity_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
namespace fml {
namespace testing {

TEST(CpuAffinity, NonAndroidPlatformDefaults) {
ASSERT_FALSE(fml::EfficiencyCoreCount().has_value());
ASSERT_TRUE(fml::RequestAffinity(fml::CpuAffinity::kEfficiency));
}

TEST(CpuAffinity, NormalSlowMedFastCores) {
auto speeds = {CpuIndexAndSpeed{.index = 0, .speed = 1},
CpuIndexAndSpeed{.index = 1, .speed = 2},
Expand Down
19 changes: 16 additions & 3 deletions fml/platform/android/cpu_affinity.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <mutex>
#include <optional>
#include <thread>
#include "flutter/fml/logging.h"

namespace fml {

Expand All @@ -36,15 +37,27 @@ void InitCPUInfo(size_t cpu_count) {
gCPUTracker = new CPUSpeedTracker(cpu_speeds);
}

bool RequestAffinity(CpuAffinity affinity) {
bool SetUpCPUTracker() {
// Populate CPU Info if uninitialized.
auto count = std::thread::hardware_concurrency();
std::call_once(gCPUTrackerFlag, [count]() { InitCPUInfo(count); });
if (gCPUTracker == nullptr) {
if (gCPUTracker == nullptr || !gCPUTracker->IsValid()) {
return false;
}
return true;
}

std::optional<size_t> AndroidEfficiencyCoreCount() {
if (!SetUpCPUTracker()) {
return true;
}
auto result = gCPUTracker->GetIndices(CpuAffinity::kEfficiency).size();
FML_DCHECK(result > 0);
return result;
}

if (!gCPUTracker->IsValid()) {
bool AndroidRequestAffinity(CpuAffinity affinity) {
if (!SetUpCPUTracker()) {
return true;
}

Expand Down
14 changes: 5 additions & 9 deletions fml/platform/android/cpu_affinity.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,10 @@

namespace fml {

/// @brief Request the given affinity for the current thread.
///
/// Returns true if successfull, or if it was a no-op. This function is
/// only supported on Android devices.
///
/// Affinity requests are based on documented CPU speed. This speed data
/// is parsed from cpuinfo_max_freq files, see also:
/// https://www.kernel.org/doc/Documentation/cpu-freq/user-guide.txt
bool RequestAffinity(CpuAffinity affinity);
/// @brief Android specific implementation of EfficiencyCoreCount.
std::optional<size_t> AndroidEfficiencyCoreCount();

/// @brief Android specific implementation of RequestAffinity.
bool AndroidRequestAffinity(CpuAffinity affinity);

} // namespace fml
8 changes: 6 additions & 2 deletions impeller/renderer/backend/vulkan/context_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <string>
#include <vector>

#include "flutter/fml/cpu_affinity.h"
#include "flutter/fml/trace_event.h"
#include "impeller/base/validation.h"
#include "impeller/renderer/backend/vulkan/allocator_vk.h"
Expand Down Expand Up @@ -130,13 +131,16 @@ void ContextVK::Setup(Settings settings) {

raster_message_loop_ = fml::ConcurrentMessageLoop::Create(
std::min(4u, std::thread::hardware_concurrency()));
#ifdef FML_OS_ANDROID
raster_message_loop_->PostTaskToAllWorkers([]() {
// Currently we only use the worker task pool for small parts of a frame
// workload, if this changes this setting may need to be adjusted.
fml::RequestAffinity(fml::CpuAffinity::kNotPerformance);
#ifdef FML_OS_ANDROID
if (::setpriority(PRIO_PROCESS, gettid(), -5) != 0) {
FML_LOG(ERROR) << "Failed to set Workers task runner priority";
}
});
#endif // FML_OS_ANDROID
});

auto& dispatcher = VULKAN_HPP_DEFAULT_DISPATCHER;
dispatcher.init(settings.proc_address_callback);
Expand Down
5 changes: 3 additions & 2 deletions impeller/renderer/backend/vulkan/fence_waiter_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <chrono>
#include <utility>

#include "flutter/fml/cpu_affinity.h"
#include "flutter/fml/thread.h"
#include "flutter/fml/trace_event.h"
#include "impeller/base/validation.h"
Expand Down Expand Up @@ -86,8 +87,8 @@ static std::vector<vk::Fence> GetFencesForWaitSet(const WaitSet& set) {
void FenceWaiterVK::Main() {
fml::Thread::SetCurrentThreadName(
fml::Thread::ThreadConfig{"io.flutter.impeller.fence_waiter"});

using namespace std::literals::chrono_literals;
// Since this thread mostly waits on fences, it doesn't need to be fast.
fml::RequestAffinity(fml::CpuAffinity::kEfficiency);

while (true) {
// We'll read the terminate_ flag within the lock below.
Expand Down
4 changes: 4 additions & 0 deletions impeller/renderer/backend/vulkan/resource_manager_vk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "impeller/renderer/backend/vulkan/resource_manager_vk.h"

#include "flutter/fml/cpu_affinity.h"
#include "flutter/fml/thread.h"
#include "flutter/fml/trace_event.h"
#include "fml/logging.h"
Expand Down Expand Up @@ -39,6 +40,9 @@ void ResourceManagerVK::Start() {

fml::Thread::SetCurrentThreadName(
fml::Thread::ThreadConfig{"io.flutter.impeller.resource_manager"});
// While this code calls destructors it doesn't need to be particularly fast
// with them, as long as it doesn't interrupt raster thread.
fml::RequestAffinity(fml::CpuAffinity::kEfficiency);

bool should_exit = false;
while (!should_exit) {
Expand Down
5 changes: 4 additions & 1 deletion runtime/dart_vm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "flutter/common/settings.h"
#include "flutter/fml/compiler_specific.h"
#include "flutter/fml/cpu_affinity.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/mapping.h"
#include "flutter/fml/size.h"
Expand Down Expand Up @@ -285,7 +286,9 @@ size_t DartVM::GetVMLaunchCount() {
DartVM::DartVM(const std::shared_ptr<const DartVMData>& vm_data,
std::shared_ptr<IsolateNameServer> isolate_name_server)
: settings_(vm_data->GetSettings()),
concurrent_message_loop_(fml::ConcurrentMessageLoop::Create()),
concurrent_message_loop_(fml::ConcurrentMessageLoop::Create(
fml::EfficiencyCoreCount().value_or(
std::thread::hardware_concurrency()))),
skia_concurrent_executor_(
[runner = concurrent_message_loop_->GetTaskRunner()](
const fml::closure& work) { runner->PostTask(work); }),
Expand Down
2 changes: 1 addition & 1 deletion shell/platform/android/android_shell_holder.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
#include <string>
#include <utility>

#include "flutter/fml/cpu_affinity.h"
#include "flutter/fml/logging.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/message_loop.h"
#include "flutter/fml/native_library.h"
#include "flutter/fml/platform/android/cpu_affinity.h"
#include "flutter/fml/platform/android/jni_util.h"
#include "flutter/lib/ui/painting/image_generator_registry.h"
#include "flutter/shell/common/rasterizer.h"
Expand Down

0 comments on commit 601745d

Please sign in to comment.