Skip to content

[4/N] Add backend options map #11462

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

Open
wants to merge 1 commit into
base: gh/cccclai/24/base
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions runtime/backend/backend_options_map.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

#include <executorch/runtime/backend/backend_options.h>
#include <executorch/runtime/core/error.h>
#include <cstring>

#pragma once
namespace executorch {
namespace runtime {

struct Entry {
const char* backend_name;
ArrayRef<BackendOption> options;
};

template <size_t MaxBackends>
class BackendOptionsMap {
public:
// Default constructor
BackendOptionsMap() : size_(0) {}

// Add a new backend configuration
Error add(
const char* backend_name,
::executorch::runtime::ArrayRef<BackendOption> options) {
if (size_ < MaxBackends) {
entries_[size_] = {backend_name, options};
++size_;
return Error::Ok;
} else {
ET_LOG(Error, "Maximum number of backends %lu reached", MaxBackends);
}
return Error::InvalidArgument;
}

// Get options for a specific backend
::executorch::runtime::ArrayRef<BackendOption> get(
const char* backend_name) const {
for (size_t i = 0; i < size_; ++i) {
if (std::strcmp(entries_[i].backend_name, backend_name) == 0) {
return entries_[i].options;
}
}
return {}; // Return empty ArrayRef if not found
}

// Get a view of the entries (const version)
::executorch::runtime::ArrayRef<const Entry> entries() const {
return ::executorch::runtime::ArrayRef<const Entry>(entries_, size_);
}

// Get a view of the entries (non-const version)
::executorch::runtime::ArrayRef<Entry> entries() {
return ::executorch::runtime::ArrayRef<Entry>(entries_, size_);
}

// Get number of entries
size_t size() const {
return size_;
}

private:
Entry entries_[MaxBackends]; // Storage for backend entries
size_t size_ = 0; // Current number of entries
};

} // namespace runtime
} // namespace executorch
37 changes: 36 additions & 1 deletion runtime/backend/targets.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,25 @@ def define_common_targets():

for aten_mode in get_aten_mode_options():
aten_suffix = ("_aten" if aten_mode else "")
runtime.cxx_library(
name = "backend_options" + aten_suffix,
exported_headers = [
"backend_options.h",
],
preprocessor_flags = ["-DUSE_ATEN_LIB"] if aten_mode else [],
visibility = [
"//executorch/...",
"@EXECUTORCH_CLIENTS",
],
exported_deps = [
"//executorch/runtime/core:core",
"//executorch/runtime/core:evalue" + aten_suffix,
"//executorch/runtime/core:event_tracer" + aten_suffix,
"//executorch/runtime/core:memory_allocator",
"//executorch/runtime/core:named_data_map",
],
)

runtime.cxx_library(
name = "interface" + aten_suffix,
srcs = [
Expand All @@ -18,7 +37,6 @@ def define_common_targets():
"backend_execution_context.h",
"backend_init_context.h",
"backend_update_context.h",
"backend_options.h",
"interface.h",
],
preprocessor_flags = ["-DUSE_ATEN_LIB"] if aten_mode else [],
Expand All @@ -32,5 +50,22 @@ def define_common_targets():
"//executorch/runtime/core:event_tracer" + aten_suffix,
"//executorch/runtime/core:memory_allocator",
"//executorch/runtime/core:named_data_map",
"//executorch/runtime/backend:backend_options" + aten_suffix,
],
)

runtime.cxx_library(
name = "backend_options_map" + aten_suffix,
exported_headers = [
"backend_options_map.h",
],
preprocessor_flags = ["-DUSE_ATEN_LIB"] if aten_mode else [],
visibility = [
"//executorch/...",
"@EXECUTORCH_CLIENTS",
],
exported_deps = [
"//executorch/runtime/core:core",
":backend_options" + aten_suffix,
],
)
156 changes: 156 additions & 0 deletions runtime/backend/test/backend_options_map_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
/*
* Copyright (c) Meta Platforms, Inc. and affiliates.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/

#include <executorch/runtime/backend/backend_options.h>
#include <executorch/runtime/backend/backend_options_map.h>
#include <executorch/runtime/platform/runtime.h>

#include <gtest/gtest.h>

using namespace ::testing;
using executorch::runtime::BackendOption;
using executorch::runtime::BackendOptions;
using executorch::runtime::BackendOptionsMap;
using executorch::runtime::BoolKey;
using executorch::runtime::Error;
using executorch::runtime::IntKey;
using executorch::runtime::OptionKey;
using executorch::runtime::StrKey;

namespace executorch {
namespace runtime {

class BackendOptionsMapTest : public ::testing::Test {
protected:
void SetUp() override {
// Initialize any necessary runtime components
executorch::runtime::runtime_init();
}
// Assume 3 backends, each with max 5 options
BackendOptionsMap<3> map;
};

TEST_F(BackendOptionsMapTest, BasicAddAndRetrieve) {
BackendOptions<5> cpu_options;

cpu_options.set_option(BoolKey("use_fp16"), true);
cpu_options.set_option(BoolKey("thead"), 4);
map.add("CPU", cpu_options.view());

auto retrieved = map.get("CPU");
EXPECT_GE(retrieved.size(), 1);

// bool value;
bool found = false;
for (auto retrieved_option : retrieved) {
if (strcmp(retrieved_option.key, "use_fp16") == 0) {
EXPECT_EQ(retrieved_option.value.bool_value, true);
found = true;
}
}
EXPECT_TRUE(found);
}

// TEST_F(BackendOptionsMapTest, UnknownBackendHandling) {
// EXPECT_EQ(map.get("NPU"), nullptr)
// << "Should return nullptr for unknown backend";
// }

TEST_F(BackendOptionsMapTest, CapacityLimits) {
BackendOptionsMap<2> small_map; // Only 2 backends capacity

BackendOptions<5> options;
ASSERT_EQ(small_map.add("CPU", options.view()), Error::Ok);
ASSERT_EQ(small_map.add("GPU", options.view()), Error::Ok);
// Return error if it exceeds capacity
ASSERT_EQ(small_map.add("NPU", options.view()), Error::InvalidArgument);
}

TEST_F(BackendOptionsMapTest, EntryIteration) {
BackendOptions<2> cpu_options;
BackendOptions<3> gpu_options;

// Add to map using ArrayRef
ASSERT_EQ(map.add("CPU", cpu_options.view()), Error::Ok);
ASSERT_EQ(map.add("GPU", gpu_options.view()), Error::Ok);

auto entries = map.entries();
// Should have 2 backends (entries)
ASSERT_EQ(entries.size(), 2);

bool found_cpu = false;
bool found_gpu = false;
for (const auto& entry : entries) {
if (strcmp(entry.backend_name, "CPU") == 0)
found_cpu = true;
if (strcmp(entry.backend_name, "GPU") == 0)
found_gpu = true;
}
// Should find CPU and GPU in the entries
EXPECT_TRUE(found_cpu);
EXPECT_TRUE(found_gpu);
}

TEST_F(BackendOptionsMapTest, ConstCorrectness) {
auto cpu_options = BackendOptions<5>();
ASSERT_EQ(map.add("CPU", cpu_options.view()), Error::Ok);

const auto& const_map = map;
auto options_retrived = const_map.get("CPU");
EXPECT_EQ(options_retrived.size(), 0);

auto entries = const_map.entries();
EXPECT_FALSE(entries.empty());
}

TEST_F(BackendOptionsMapTest, EmptyMapBehavior) {
EXPECT_EQ(map.get("CPU").size(), 0);
EXPECT_TRUE(map.entries().empty());
EXPECT_EQ(map.entries().size(), 0);
}

TEST_F(BackendOptionsMapTest, OptionIsolation) {
BackendOptions<2> cpu_options;
cpu_options.set_option(BoolKey("Debug"), true);
cpu_options.set_option(IntKey("NumThreads"), 3);

BackendOptions<3> gpu_options;
gpu_options.set_option(BoolKey("Profile"), true);
gpu_options.set_option(IntKey("Mem"), 1024);
gpu_options.set_option(StrKey("Hardware"), "H100");

// Add to map using ArrayRef
map.add("CPU", cpu_options.view());
map.add("GPU", gpu_options.view());

// Test CPU options
auto cpu_opts = map.get("CPU");
ASSERT_FALSE(cpu_opts.empty());

// Verify CPU has its own option
EXPECT_EQ(cpu_opts.size(), 2);
EXPECT_EQ(cpu_opts[0].key, "Debug");
EXPECT_EQ(cpu_opts[0].value.bool_value, true);
EXPECT_EQ(cpu_opts[1].key, "NumThreads");
EXPECT_EQ(cpu_opts[1].value.int_value, 3);

// Test GPU options
auto gpu_opts = map.get("GPU");
ASSERT_FALSE(gpu_opts.empty());

// Verify GPU has its own option
EXPECT_EQ(gpu_opts.size(), 3);
EXPECT_EQ(gpu_opts[0].key, "Profile");
EXPECT_EQ(gpu_opts[0].value.bool_value, true);
EXPECT_EQ(gpu_opts[1].key, "Mem");
EXPECT_EQ(gpu_opts[1].value.int_value, 1024);
EXPECT_EQ(gpu_opts[2].key, "Hardware");
EXPECT_EQ(gpu_opts[2].value.string_value, "H100");
}
} // namespace runtime
} // namespace executorch
9 changes: 9 additions & 0 deletions runtime/backend/test/targets.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@ def define_common_targets():
],
)

runtime.cxx_test(
name = "backend_options_map_test",
srcs = ["backend_options_map_test.cpp"],
deps = [
"//executorch/runtime/core:core",
"//executorch/runtime/backend:backend_options_map",
],
)

runtime.cxx_test(
name = "backend_interface_update_test",
srcs = ["backend_interface_update_test.cpp"],
Expand Down
Loading