Revamp the rabit implementation. (#10112)

This PR replaces the original RABIT implementation with a new one, which has already been partially merged into XGBoost. The new one features:
- Federated learning for both CPU and GPU.
- NCCL.
- More data types.
- A unified interface for all the underlying implementations.
- Improved timeout handling for both tracker and workers.
- Exhausted tests with metrics (fixed a couple of bugs along the way).
- A reusable tracker for Python and JVM packages.
This commit is contained in:
Jiaming Yuan
2024-05-20 11:56:23 +08:00
committed by GitHub
parent ba9b4cb1ee
commit a5a58102e5
195 changed files with 2768 additions and 9234 deletions

View File

@@ -1,41 +0,0 @@
/**
* Copyright 2022-2023, XGBoost Contributors
*/
#pragma once
#include <gtest/gtest.h>
#include <xgboost/collective/socket.h>
#include <fstream> // ifstream
#include "../helpers.h" // for FileExists
namespace xgboost::collective {
class SocketTest : public ::testing::Test {
protected:
std::string skip_msg_{"Skipping IPv6 test"};
bool SkipTest() {
std::string path{"/sys/module/ipv6/parameters/disable"};
if (FileExists(path)) {
std::ifstream fin(path);
if (!fin) {
return true;
}
std::string s_value;
fin >> s_value;
auto value = std::stoi(s_value);
if (value != 0) {
return true;
}
} else {
return true;
}
return false;
}
protected:
void SetUp() override { system::SocketStartup(); }
void TearDown() override { system::SocketFinalize(); }
};
} // namespace xgboost::collective

View File

@@ -175,4 +175,35 @@ TEST_F(AllgatherTest, VAlgo) {
worker.TestVAlgo();
});
}
TEST(VectorAllgatherV, Basic) {
std::int32_t n_workers{3};
TestDistributedGlobal(n_workers, []() {
auto n_workers = collective::GetWorldSize();
ASSERT_EQ(n_workers, 3);
auto rank = collective::GetRank();
// Construct input that has different length for each worker.
std::vector<std::vector<char>> inputs;
for (std::int32_t i = 0; i < rank + 1; ++i) {
std::vector<char> in;
for (std::int32_t j = 0; j < rank + 1; ++j) {
in.push_back(static_cast<char>(j));
}
inputs.emplace_back(std::move(in));
}
Context ctx;
auto outputs = VectorAllgatherV(&ctx, inputs);
ASSERT_EQ(outputs.size(), (1 + n_workers) * n_workers / 2);
auto const& res = outputs;
for (std::int32_t i = 0; i < n_workers; ++i) {
std::int32_t k = 0;
for (auto v : res[i]) {
ASSERT_EQ(v, k++);
}
}
});
}
} // namespace xgboost::collective

View File

@@ -39,6 +39,22 @@ class AllreduceWorker : public WorkerForTest {
}
}
void Restricted() {
this->LimitSockBuf(4096);
std::size_t n = 4096 * 4;
std::vector<std::int32_t> data(comm_.World() * n, 1);
auto rc = Allreduce(comm_, common::Span{data.data(), data.size()}, [](auto lhs, auto rhs) {
for (std::size_t i = 0; i < rhs.size(); ++i) {
rhs[i] += lhs[i];
}
});
ASSERT_TRUE(rc.OK());
for (auto v : data) {
ASSERT_EQ(v, comm_.World());
}
}
void Acc() {
std::vector<double> data(314, 1.5);
auto rc = Allreduce(comm_, common::Span{data.data(), data.size()}, [](auto lhs, auto rhs) {
@@ -95,4 +111,45 @@ TEST_F(AllreduceTest, BitOr) {
worker.BitOr();
});
}
TEST_F(AllreduceTest, Restricted) {
std::int32_t n_workers = std::min(3u, std::thread::hardware_concurrency());
TestDistributed(n_workers, [=](std::string host, std::int32_t port, std::chrono::seconds timeout,
std::int32_t r) {
AllreduceWorker worker{host, port, timeout, n_workers, r};
worker.Restricted();
});
}
TEST(AllreduceGlobal, Basic) {
auto n_workers = 3;
TestDistributedGlobal(n_workers, [&]() {
std::vector<float> values(n_workers * 2, 0);
auto rank = GetRank();
auto s_values = common::Span{values.data(), values.size()};
auto self = s_values.subspan(rank * 2, 2);
for (auto& v : self) {
v = 1.0f;
}
Context ctx;
auto rc =
Allreduce(&ctx, linalg::MakeVec(s_values.data(), s_values.size()), collective::Op::kSum);
SafeColl(rc);
for (auto v : s_values) {
ASSERT_EQ(v, 1);
}
});
}
TEST(AllreduceGlobal, Small) {
// Test when the data is not large enougth to be divided by the number of workers
auto n_workers = 8;
TestDistributedGlobal(n_workers, [&]() {
std::uint64_t value{1};
Context ctx;
auto rc = Allreduce(&ctx, linalg::MakeVec(&value, 1), collective::Op::kSum);
SafeColl(rc);
ASSERT_EQ(value, n_workers);
});
}
} // namespace xgboost::collective

View File

@@ -1,63 +0,0 @@
/*!
* Copyright 2022 XGBoost contributors
*/
#include <dmlc/parameter.h>
#include <gtest/gtest.h>
#include "../../../src/collective/communicator.h"
namespace xgboost {
namespace collective {
TEST(CommunicatorFactory, TypeFromEnv) {
EXPECT_EQ(CommunicatorType::kUnknown, Communicator::GetTypeFromEnv());
dmlc::SetEnv<std::string>("XGBOOST_COMMUNICATOR", "foo");
EXPECT_THROW(Communicator::GetTypeFromEnv(), dmlc::Error);
dmlc::SetEnv<std::string>("XGBOOST_COMMUNICATOR", "rabit");
EXPECT_EQ(CommunicatorType::kRabit, Communicator::GetTypeFromEnv());
dmlc::SetEnv<std::string>("XGBOOST_COMMUNICATOR", "Federated");
EXPECT_EQ(CommunicatorType::kFederated, Communicator::GetTypeFromEnv());
dmlc::SetEnv<std::string>("XGBOOST_COMMUNICATOR", "In-Memory");
EXPECT_EQ(CommunicatorType::kInMemory, Communicator::GetTypeFromEnv());
}
TEST(CommunicatorFactory, TypeFromArgs) {
Json config{JsonObject()};
EXPECT_EQ(CommunicatorType::kUnknown, Communicator::GetTypeFromConfig(config));
config["xgboost_communicator"] = String("rabit");
EXPECT_EQ(CommunicatorType::kRabit, Communicator::GetTypeFromConfig(config));
config["xgboost_communicator"] = String("federated");
EXPECT_EQ(CommunicatorType::kFederated, Communicator::GetTypeFromConfig(config));
config["xgboost_communicator"] = String("in-memory");
EXPECT_EQ(CommunicatorType::kInMemory, Communicator::GetTypeFromConfig(config));
config["xgboost_communicator"] = String("foo");
EXPECT_THROW(Communicator::GetTypeFromConfig(config), dmlc::Error);
}
TEST(CommunicatorFactory, TypeFromArgsUpperCase) {
Json config{JsonObject()};
EXPECT_EQ(CommunicatorType::kUnknown, Communicator::GetTypeFromConfig(config));
config["XGBOOST_COMMUNICATOR"] = String("rabit");
EXPECT_EQ(CommunicatorType::kRabit, Communicator::GetTypeFromConfig(config));
config["XGBOOST_COMMUNICATOR"] = String("federated");
EXPECT_EQ(CommunicatorType::kFederated, Communicator::GetTypeFromConfig(config));
config["XGBOOST_COMMUNICATOR"] = String("in-memory");
EXPECT_EQ(CommunicatorType::kInMemory, Communicator::GetTypeFromConfig(config));
config["XGBOOST_COMMUNICATOR"] = String("foo");
EXPECT_THROW(Communicator::GetTypeFromConfig(config), dmlc::Error);
}
} // namespace collective
} // namespace xgboost

View File

@@ -1,237 +0,0 @@
/*!
* Copyright 2022 XGBoost contributors
*/
#include <dmlc/parameter.h>
#include <gtest/gtest.h>
#include <bitset>
#include <thread>
#include "../../../src/collective/in_memory_communicator.h"
namespace xgboost {
namespace collective {
class InMemoryCommunicatorTest : public ::testing::Test {
public:
static void Verify(void (*function)(int)) {
std::vector<std::thread> threads;
for (auto rank = 0; rank < kWorldSize; rank++) {
threads.emplace_back(function, rank);
}
for (auto &thread : threads) {
thread.join();
}
}
static void Allgather(int rank) {
InMemoryCommunicator comm{kWorldSize, rank};
VerifyAllgather(comm, rank);
}
static void AllgatherV(int rank) {
InMemoryCommunicator comm{kWorldSize, rank};
VerifyAllgatherV(comm, rank);
}
static void AllreduceMax(int rank) {
InMemoryCommunicator comm{kWorldSize, rank};
VerifyAllreduceMax(comm, rank);
}
static void AllreduceMin(int rank) {
InMemoryCommunicator comm{kWorldSize, rank};
VerifyAllreduceMin(comm, rank);
}
static void AllreduceSum(int rank) {
InMemoryCommunicator comm{kWorldSize, rank};
VerifyAllreduceSum(comm);
}
static void AllreduceBitwiseAND(int rank) {
InMemoryCommunicator comm{kWorldSize, rank};
VerifyAllreduceBitwiseAND(comm, rank);
}
static void AllreduceBitwiseOR(int rank) {
InMemoryCommunicator comm{kWorldSize, rank};
VerifyAllreduceBitwiseOR(comm, rank);
}
static void AllreduceBitwiseXOR(int rank) {
InMemoryCommunicator comm{kWorldSize, rank};
VerifyAllreduceBitwiseXOR(comm, rank);
}
static void Broadcast(int rank) {
InMemoryCommunicator comm{kWorldSize, rank};
VerifyBroadcast(comm, rank);
}
static void Mixture(int rank) {
InMemoryCommunicator comm{kWorldSize, rank};
for (auto i = 0; i < 5; i++) {
VerifyAllgather(comm, rank);
VerifyAllreduceMax(comm, rank);
VerifyAllreduceMin(comm, rank);
VerifyAllreduceSum(comm);
VerifyAllreduceBitwiseAND(comm, rank);
VerifyAllreduceBitwiseOR(comm, rank);
VerifyAllreduceBitwiseXOR(comm, rank);
VerifyBroadcast(comm, rank);
}
}
protected:
static void VerifyAllgather(InMemoryCommunicator &comm, int rank) {
std::string input{static_cast<char>('0' + rank)};
auto output = comm.AllGather(input);
for (auto i = 0; i < kWorldSize; i++) {
EXPECT_EQ(output[i], static_cast<char>('0' + i));
}
}
static void VerifyAllgatherV(InMemoryCommunicator &comm, int rank) {
std::vector<std::string_view> inputs{"a", "bb", "ccc"};
auto output = comm.AllGatherV(inputs[rank]);
EXPECT_EQ(output, "abbccc");
}
static void VerifyAllreduceMax(InMemoryCommunicator &comm, int rank) {
int buffer[] = {1 + rank, 2 + rank, 3 + rank, 4 + rank, 5 + rank};
comm.AllReduce(buffer, sizeof(buffer) / sizeof(buffer[0]), DataType::kInt32, Operation::kMax);
int expected[] = {3, 4, 5, 6, 7};
for (auto i = 0; i < 5; i++) {
EXPECT_EQ(buffer[i], expected[i]);
}
}
static void VerifyAllreduceMin(InMemoryCommunicator &comm, int rank) {
int buffer[] = {1 + rank, 2 + rank, 3 + rank, 4 + rank, 5 + rank};
comm.AllReduce(buffer, sizeof(buffer) / sizeof(buffer[0]), DataType::kInt32, Operation::kMin);
int expected[] = {1, 2, 3, 4, 5};
for (auto i = 0; i < 5; i++) {
EXPECT_EQ(buffer[i], expected[i]);
}
}
static void VerifyAllreduceSum(InMemoryCommunicator &comm) {
int buffer[] = {1, 2, 3, 4, 5};
comm.AllReduce(buffer, sizeof(buffer) / sizeof(buffer[0]), DataType::kInt32, Operation::kSum);
int expected[] = {3, 6, 9, 12, 15};
for (auto i = 0; i < 5; i++) {
EXPECT_EQ(buffer[i], expected[i]);
}
}
static void VerifyAllreduceBitwiseAND(InMemoryCommunicator &comm, int rank) {
std::bitset<2> original(rank);
auto buffer = original.to_ulong();
comm.AllReduce(&buffer, 1, DataType::kUInt32, Operation::kBitwiseAND);
EXPECT_EQ(buffer, 0UL);
}
static void VerifyAllreduceBitwiseOR(InMemoryCommunicator &comm, int rank) {
std::bitset<2> original(rank);
auto buffer = original.to_ulong();
comm.AllReduce(&buffer, 1, DataType::kUInt32, Operation::kBitwiseOR);
std::bitset<2> actual(buffer);
std::bitset<2> expected{0b11};
EXPECT_EQ(actual, expected);
}
static void VerifyAllreduceBitwiseXOR(InMemoryCommunicator &comm, int rank) {
std::bitset<3> original(rank * 2);
auto buffer = original.to_ulong();
comm.AllReduce(&buffer, 1, DataType::kUInt32, Operation::kBitwiseXOR);
std::bitset<3> actual(buffer);
std::bitset<3> expected{0b110};
EXPECT_EQ(actual, expected);
}
static void VerifyBroadcast(InMemoryCommunicator &comm, int rank) {
if (rank == 0) {
std::string buffer{"hello"};
comm.Broadcast(&buffer[0], buffer.size(), 0);
EXPECT_EQ(buffer, "hello");
} else {
std::string buffer{" "};
comm.Broadcast(&buffer[0], buffer.size(), 0);
EXPECT_EQ(buffer, "hello");
}
}
static int const kWorldSize{3};
};
TEST(InMemoryCommunicatorSimpleTest, ThrowOnWorldSizeTooSmall) {
auto construct = []() { InMemoryCommunicator comm{0, 0}; };
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(InMemoryCommunicatorSimpleTest, ThrowOnRankTooSmall) {
auto construct = []() { InMemoryCommunicator comm{1, -1}; };
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(InMemoryCommunicatorSimpleTest, ThrowOnRankTooBig) {
auto construct = []() { InMemoryCommunicator comm{1, 1}; };
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(InMemoryCommunicatorSimpleTest, ThrowOnWorldSizeNotInteger) {
auto construct = []() {
Json config{JsonObject()};
config["in_memory_world_size"] = std::string("1");
config["in_memory_rank"] = Integer(0);
auto *comm = InMemoryCommunicator::Create(config);
delete comm;
};
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(InMemoryCommunicatorSimpleTest, ThrowOnRankNotInteger) {
auto construct = []() {
Json config{JsonObject()};
config["in_memory_world_size"] = 1;
config["in_memory_rank"] = std::string("0");
auto *comm = InMemoryCommunicator::Create(config);
delete comm;
};
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(InMemoryCommunicatorSimpleTest, GetWorldSizeAndRank) {
InMemoryCommunicator comm{1, 0};
EXPECT_EQ(comm.GetWorldSize(), 1);
EXPECT_EQ(comm.GetRank(), 0);
}
TEST(InMemoryCommunicatorSimpleTest, IsDistributed) {
InMemoryCommunicator comm{1, 0};
EXPECT_TRUE(comm.IsDistributed());
}
TEST_F(InMemoryCommunicatorTest, Allgather) { Verify(&Allgather); }
TEST_F(InMemoryCommunicatorTest, AllgatherV) { Verify(&AllgatherV); }
TEST_F(InMemoryCommunicatorTest, AllreduceMax) { Verify(&AllreduceMax); }
TEST_F(InMemoryCommunicatorTest, AllreduceMin) { Verify(&AllreduceMin); }
TEST_F(InMemoryCommunicatorTest, AllreduceSum) { Verify(&AllreduceSum); }
TEST_F(InMemoryCommunicatorTest, AllreduceBitwiseAND) { Verify(&AllreduceBitwiseAND); }
TEST_F(InMemoryCommunicatorTest, AllreduceBitwiseOR) { Verify(&AllreduceBitwiseOR); }
TEST_F(InMemoryCommunicatorTest, AllreduceBitwiseXOR) { Verify(&AllreduceBitwiseXOR); }
TEST_F(InMemoryCommunicatorTest, Broadcast) { Verify(&Broadcast); }
TEST_F(InMemoryCommunicatorTest, Mixture) { Verify(&Mixture); }
} // namespace collective
} // namespace xgboost

View File

@@ -59,7 +59,7 @@ class LoopTest : public ::testing::Test {
TEST_F(LoopTest, Timeout) {
std::vector<std::int8_t> data(1);
Loop::Op op{Loop::Op::kRead, 0, data.data(), data.size(), &pair_.second, 0};
loop_->Submit(op);
loop_->Submit(std::move(op));
auto rc = loop_->Block();
ASSERT_FALSE(rc.OK());
ASSERT_EQ(rc.Code(), std::make_error_code(std::errc::timed_out)) << rc.Report();
@@ -75,8 +75,8 @@ TEST_F(LoopTest, Op) {
Loop::Op wop{Loop::Op::kWrite, 0, wbuf.data(), wbuf.size(), &send, 0};
Loop::Op rop{Loop::Op::kRead, 0, rbuf.data(), rbuf.size(), &recv, 0};
loop_->Submit(wop);
loop_->Submit(rop);
loop_->Submit(std::move(wop));
loop_->Submit(std::move(rop));
auto rc = loop_->Block();
SafeColl(rc);
@@ -90,7 +90,7 @@ TEST_F(LoopTest, Block) {
common::Timer t;
t.Start();
loop_->Submit(op);
loop_->Submit(std::move(op));
t.Stop();
// submit is non-blocking
ASSERT_LT(t.ElapsedSeconds(), 1);

View File

@@ -1,99 +0,0 @@
/**
* Copyright 2022-2023, XGBoost contributors
*/
#ifdef XGBOOST_USE_NCCL
#include <gtest/gtest.h>
#include <bitset>
#include <string> // for string
#include "../../../src/collective/comm.cuh"
#include "../../../src/collective/communicator-inl.cuh"
#include "../../../src/collective/nccl_device_communicator.cuh"
#include "../helpers.h"
namespace xgboost {
namespace collective {
TEST(NcclDeviceCommunicatorSimpleTest, ThrowOnInvalidDeviceOrdinal) {
auto construct = []() { NcclDeviceCommunicator comm{-1, false, DefaultNcclName()}; };
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(NcclDeviceCommunicatorSimpleTest, SystemError) {
auto stub = std::make_shared<NcclStub>(DefaultNcclName());
auto rc = stub->GetNcclResult(ncclSystemError);
auto msg = rc.Report();
ASSERT_TRUE(msg.find("environment variables") != std::string::npos);
}
namespace {
void VerifyAllReduceBitwiseAND() {
auto const rank = collective::GetRank();
std::bitset<64> original{};
original[rank] = true;
HostDeviceVector<uint64_t> buffer({original.to_ullong()}, DeviceOrd::CUDA(rank));
collective::AllReduce<collective::Operation::kBitwiseAND>(rank, buffer.DevicePointer(), 1);
collective::Synchronize(rank);
EXPECT_EQ(buffer.HostVector()[0], 0ULL);
}
} // anonymous namespace
TEST(NcclDeviceCommunicator, MGPUAllReduceBitwiseAND) {
auto const n_gpus = common::AllVisibleGPUs();
if (n_gpus <= 1) {
GTEST_SKIP() << "Skipping MGPUAllReduceBitwiseAND test with # GPUs = " << n_gpus;
}
auto constexpr kUseNccl = true;
RunWithInMemoryCommunicator<kUseNccl>(n_gpus, VerifyAllReduceBitwiseAND);
}
namespace {
void VerifyAllReduceBitwiseOR() {
auto const world_size = collective::GetWorldSize();
auto const rank = collective::GetRank();
std::bitset<64> original{};
original[rank] = true;
HostDeviceVector<uint64_t> buffer({original.to_ullong()}, DeviceOrd::CUDA(rank));
collective::AllReduce<collective::Operation::kBitwiseOR>(rank, buffer.DevicePointer(), 1);
collective::Synchronize(rank);
EXPECT_EQ(buffer.HostVector()[0], (1ULL << world_size) - 1);
}
} // anonymous namespace
TEST(NcclDeviceCommunicator, MGPUAllReduceBitwiseOR) {
auto const n_gpus = common::AllVisibleGPUs();
if (n_gpus <= 1) {
GTEST_SKIP() << "Skipping MGPUAllReduceBitwiseOR test with # GPUs = " << n_gpus;
}
auto constexpr kUseNccl = true;
RunWithInMemoryCommunicator<kUseNccl>(n_gpus, VerifyAllReduceBitwiseOR);
}
namespace {
void VerifyAllReduceBitwiseXOR() {
auto const world_size = collective::GetWorldSize();
auto const rank = collective::GetRank();
std::bitset<64> original{~0ULL};
original[rank] = false;
HostDeviceVector<uint64_t> buffer({original.to_ullong()}, DeviceOrd::CUDA(rank));
collective::AllReduce<collective::Operation::kBitwiseXOR>(rank, buffer.DevicePointer(), 1);
collective::Synchronize(rank);
EXPECT_EQ(buffer.HostVector()[0], (1ULL << world_size) - 1);
}
} // anonymous namespace
TEST(NcclDeviceCommunicator, MGPUAllReduceBitwiseXOR) {
auto const n_gpus = common::AllVisibleGPUs();
if (n_gpus <= 1) {
GTEST_SKIP() << "Skipping MGPUAllReduceBitwiseXOR test with # GPUs = " << n_gpus;
}
auto constexpr kUseNccl = true;
RunWithInMemoryCommunicator<kUseNccl>(n_gpus, VerifyAllReduceBitwiseXOR);
}
} // namespace collective
} // namespace xgboost
#endif // XGBOOST_USE_NCCL

View File

@@ -1,70 +0,0 @@
/**
* Copyright 2022-2024, XGBoost contributors
*/
#include <gtest/gtest.h>
#include "../../../src/collective/rabit_communicator.h"
#include "../helpers.h"
namespace xgboost::collective {
TEST(RabitCommunicatorSimpleTest, ThrowOnWorldSizeTooSmall) {
auto construct = []() { RabitCommunicator comm{0, 0}; };
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(RabitCommunicatorSimpleTest, ThrowOnRankTooSmall) {
auto construct = []() { RabitCommunicator comm{1, -1}; };
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(RabitCommunicatorSimpleTest, ThrowOnRankTooBig) {
auto construct = []() { RabitCommunicator comm{1, 1}; };
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(RabitCommunicatorSimpleTest, GetWorldSizeAndRank) {
RabitCommunicator comm{6, 3};
EXPECT_EQ(comm.GetWorldSize(), 6);
EXPECT_EQ(comm.GetRank(), 3);
}
TEST(RabitCommunicatorSimpleTest, IsNotDistributed) {
RabitCommunicator comm{2, 1};
// Rabit is only distributed with a tracker.
EXPECT_FALSE(comm.IsDistributed());
}
namespace {
void VerifyVectorAllgatherV() {
auto n_workers = collective::GetWorldSize();
ASSERT_EQ(n_workers, 3);
auto rank = collective::GetRank();
// Construct input that has different length for each worker.
std::vector<std::vector<char>> inputs;
for (std::int32_t i = 0; i < rank + 1; ++i) {
std::vector<char> in;
for (std::int32_t j = 0; j < rank + 1; ++j) {
in.push_back(static_cast<char>(j));
}
inputs.emplace_back(std::move(in));
}
auto outputs = VectorAllgatherV(inputs);
ASSERT_EQ(outputs.size(), (1 + n_workers) * n_workers / 2);
auto const& res = outputs;
for (std::int32_t i = 0; i < n_workers; ++i) {
std::int32_t k = 0;
for (auto v : res[i]) {
ASSERT_EQ(v, k++);
}
}
}
} // namespace
TEST(VectorAllgatherV, Basic) {
std::int32_t n_workers{3};
RunWithInMemoryCommunicator(n_workers, VerifyVectorAllgatherV);
}
} // namespace xgboost::collective

View File

@@ -29,6 +29,7 @@ class PrintWorker : public WorkerForTest {
TEST_F(TrackerTest, Bootstrap) {
RabitTracker tracker{MakeTrackerConfig(host, n_workers, timeout)};
ASSERT_TRUE(HasTimeout(tracker.Timeout()));
ASSERT_FALSE(tracker.Ready());
auto fut = tracker.Run();
@@ -47,6 +48,9 @@ TEST_F(TrackerTest, Bootstrap) {
w.join();
}
SafeColl(fut.get());
ASSERT_FALSE(HasTimeout(std::chrono::seconds{-1}));
ASSERT_FALSE(HasTimeout(std::chrono::seconds{0}));
}
TEST_F(TrackerTest, Print) {

View File

@@ -16,6 +16,10 @@
#include "../../../src/collective/tracker.h" // for GetHostAddress
#include "../helpers.h" // for FileExists
#if defined(XGBOOST_USE_FEDERATED)
#include "../plugin/federated/test_worker.h"
#endif // defined(XGBOOST_USE_FEDERATED)
namespace xgboost::collective {
class WorkerForTest {
std::string tracker_host_;
@@ -45,6 +49,7 @@ class WorkerForTest {
if (i != comm_.Rank()) {
ASSERT_TRUE(comm_.Chan(i)->Socket()->NonBlocking());
ASSERT_TRUE(comm_.Chan(i)->Socket()->SetBufSize(n_bytes).OK());
ASSERT_TRUE(comm_.Chan(i)->Socket()->SetNoDelay().OK());
}
}
}
@@ -126,15 +131,80 @@ void TestDistributed(std::int32_t n_workers, WorkerFn worker_fn) {
ASSERT_TRUE(fut.get().OK());
}
inline auto MakeDistributedTestConfig(std::string host, std::int32_t port,
std::chrono::seconds timeout, std::int32_t r) {
Json config{Object{}};
config["dmlc_communicator"] = std::string{"rabit"};
config["dmlc_tracker_uri"] = host;
config["dmlc_tracker_port"] = port;
config["dmlc_timeout_sec"] = static_cast<std::int64_t>(timeout.count());
config["dmlc_timeout"] = static_cast<std::int64_t>(timeout.count());
config["dmlc_task_id"] = std::to_string(r);
config["dmlc_retry"] = 2;
return config;
}
template <typename WorkerFn>
void TestDistributedGlobal(std::int32_t n_workers, WorkerFn worker_fn, bool need_finalize = true) {
system::SocketStartup();
std::chrono::seconds timeout{1};
std::string host;
auto rc = GetHostAddress(&host);
SafeColl(rc);
RabitTracker tracker{MakeTrackerConfig(host, n_workers, timeout)};
auto fut = tracker.Run();
std::vector<std::thread> workers;
std::int32_t port = tracker.Port();
for (std::int32_t i = 0; i < n_workers; ++i) {
workers.emplace_back([=] {
auto config = MakeDistributedTestConfig(host, port, timeout, i);
Init(config);
worker_fn();
if (need_finalize) {
Finalize();
}
});
}
for (auto& t : workers) {
t.join();
}
ASSERT_TRUE(fut.get().OK());
system::SocketFinalize();
}
class BaseMGPUTest : public ::testing::Test {
public:
/**
* @param emulate_if_single Emulate multi-GPU for federated test if there's only one GPU
* available.
*/
template <typename Fn>
auto DoTest(Fn&& fn, bool is_federated, bool emulate_if_single = false) const {
auto n_gpus = common::AllVisibleGPUs();
if (is_federated) {
#if defined(XGBOOST_USE_FEDERATED)
if (n_gpus == 1 && emulate_if_single) {
// Emulate multiple GPUs.
// We don't use nccl and can have multiple communicators running on the same device.
n_gpus = 3;
}
TestFederatedGlobal(n_gpus, fn);
#else
GTEST_SKIP_("Not compiled with federated learning.");
#endif // defined(XGBOOST_USE_FEDERATED)
} else {
#if defined(XGBOOST_USE_NCCL)
TestDistributedGlobal(n_gpus, fn);
#else
GTEST_SKIP_("Not compiled with NCCL.");
#endif // defined(XGBOOST_USE_NCCL)
}
}
};
} // namespace xgboost::collective

View File

@@ -15,8 +15,8 @@ namespace xgboost::common {
TEST(MemoryFixSizeBuffer, Seek) {
size_t constexpr kSize { 64 };
std::vector<int32_t> memory( kSize );
rabit::utils::MemoryFixSizeBuffer buf(memory.data(), memory.size());
buf.Seek(rabit::utils::MemoryFixSizeBuffer::kSeekEnd);
MemoryFixSizeBuffer buf(memory.data(), memory.size());
buf.Seek(MemoryFixSizeBuffer::kSeekEnd);
size_t end = buf.Tell();
ASSERT_EQ(end, kSize);
}

View File

@@ -1,12 +1,16 @@
/**
* Copyright 2020-2023 by XGBoost Contributors
* Copyright 2020-2024, XGBoost Contributors
*/
#include "test_quantile.h"
#include <gtest/gtest.h>
#include <cstdint> // for int64_t
#include "../../../src/collective/allreduce.h"
#include "../../../src/common/hist_util.h"
#include "../../../src/data/adapter.h"
#include "../collective/test_worker.h" // for TestDistributedGlobal
#include "xgboost/context.h"
namespace xgboost::common {
@@ -90,6 +94,7 @@ void DoTestDistributedQuantile(size_t rows, size_t cols) {
// Generate cuts for single node environment
collective::Finalize();
CHECK_EQ(collective::GetWorldSize(), 1);
std::for_each(column_size.begin(), column_size.end(), [=](auto& size) { size *= world; });
m->Info().num_row_ = world * rows;
@@ -145,7 +150,8 @@ void DoTestDistributedQuantile(size_t rows, size_t cols) {
template <bool use_column>
void TestDistributedQuantile(size_t const rows, size_t const cols) {
auto constexpr kWorkers = 4;
RunWithInMemoryCommunicator(kWorkers, DoTestDistributedQuantile<use_column>, rows, cols);
collective::TestDistributedGlobal(
kWorkers, [=] { DoTestDistributedQuantile<use_column>(rows, cols); }, false);
}
} // anonymous namespace
@@ -272,7 +278,8 @@ void DoTestColSplitQuantile(size_t rows, size_t cols) {
template <bool use_column>
void TestColSplitQuantile(size_t rows, size_t cols) {
auto constexpr kWorkers = 4;
RunWithInMemoryCommunicator(kWorkers, DoTestColSplitQuantile<use_column>, rows, cols);
collective::TestDistributedGlobal(kWorkers,
[=] { DoTestColSplitQuantile<use_column>(rows, cols); });
}
} // anonymous namespace
@@ -324,43 +331,56 @@ void TestSameOnAllWorkers() {
cut_ptrs(cuts.Ptrs().size() * world, 0);
std::vector<float> cut_min_values(cuts.MinValues().size() * world, 0);
size_t value_size = cuts.Values().size();
collective::Allreduce<collective::Operation::kMax>(&value_size, 1);
size_t ptr_size = cuts.Ptrs().size();
collective::Allreduce<collective::Operation::kMax>(&ptr_size, 1);
CHECK_EQ(ptr_size, kCols + 1);
size_t min_value_size = cuts.MinValues().size();
collective::Allreduce<collective::Operation::kMax>(&min_value_size, 1);
CHECK_EQ(min_value_size, kCols);
std::int64_t value_size = cuts.Values().size();
std::int64_t ptr_size = cuts.Ptrs().size();
std::int64_t min_value_size = cuts.MinValues().size();
size_t value_offset = value_size * rank;
std::copy(cuts.Values().begin(), cuts.Values().end(),
cut_values.begin() + value_offset);
size_t ptr_offset = ptr_size * rank;
std::copy(cuts.Ptrs().cbegin(), cuts.Ptrs().cend(),
cut_ptrs.begin() + ptr_offset);
size_t min_values_offset = min_value_size * rank;
auto rc = collective::Success() << [&] {
return collective::Allreduce(&ctx, &value_size, collective::Op::kMax);
} << [&] {
return collective::Allreduce(&ctx, &ptr_size, collective::Op::kMax);
} << [&] {
return collective::Allreduce(&ctx, &min_value_size, collective::Op::kMax);
};
collective::SafeColl(rc);
ASSERT_EQ(ptr_size, kCols + 1);
ASSERT_EQ(min_value_size, kCols);
std::size_t value_offset = value_size * rank;
std::copy(cuts.Values().begin(), cuts.Values().end(), cut_values.begin() + value_offset);
std::size_t ptr_offset = ptr_size * rank;
std::copy(cuts.Ptrs().cbegin(), cuts.Ptrs().cend(), cut_ptrs.begin() + ptr_offset);
std::size_t min_values_offset = min_value_size * rank;
std::copy(cuts.MinValues().cbegin(), cuts.MinValues().cend(),
cut_min_values.begin() + min_values_offset);
collective::Allreduce<collective::Operation::kSum>(cut_values.data(), cut_values.size());
collective::Allreduce<collective::Operation::kSum>(cut_ptrs.data(), cut_ptrs.size());
collective::Allreduce<collective::Operation::kSum>(cut_min_values.data(), cut_min_values.size());
rc = std::move(rc) << [&] {
return collective::Allreduce(&ctx, linalg::MakeVec(cut_values.data(), cut_values.size()),
collective::Op::kSum);
} << [&] {
return collective::Allreduce(&ctx, linalg::MakeVec(cut_ptrs.data(), cut_ptrs.size()),
collective::Op::kSum);
} << [&] {
return collective::Allreduce(
&ctx, linalg::MakeVec(cut_min_values.data(), cut_min_values.size()),
collective::Op::kSum);
};
collective::SafeColl(rc);
for (int32_t i = 0; i < world; i++) {
for (size_t j = 0; j < value_size; ++j) {
for (std::int32_t i = 0; i < world; i++) {
for (std::int64_t j = 0; j < value_size; ++j) {
size_t idx = i * value_size + j;
EXPECT_NEAR(cuts.Values().at(j), cut_values.at(idx), kRtEps);
ASSERT_NEAR(cuts.Values().at(j), cut_values.at(idx), kRtEps);
}
for (size_t j = 0; j < ptr_size; ++j) {
for (std::int64_t j = 0; j < ptr_size; ++j) {
size_t idx = i * ptr_size + j;
EXPECT_EQ(cuts.Ptrs().at(j), cut_ptrs.at(idx));
}
for (size_t j = 0; j < min_value_size; ++j) {
for (std::int64_t j = 0; j < min_value_size; ++j) {
size_t idx = i * min_value_size + j;
EXPECT_EQ(cuts.MinValues().at(j), cut_min_values.at(idx));
ASSERT_EQ(cuts.MinValues().at(j), cut_min_values.at(idx));
}
}
});
@@ -369,6 +389,6 @@ void TestSameOnAllWorkers() {
TEST(Quantile, SameOnAllWorkers) {
auto constexpr kWorkers = 4;
RunWithInMemoryCommunicator(kWorkers, TestSameOnAllWorkers);
collective::TestDistributedGlobal(kWorkers, [] { TestSameOnAllWorkers(); });
}
} // namespace xgboost::common

View File

@@ -1,12 +1,13 @@
/**
* Copyright 2020-2023, XGBoost contributors
* Copyright 2020-2024, XGBoost contributors
*/
#include <gtest/gtest.h>
#include "../../../src/collective/communicator-inl.cuh"
#include "../../../src/collective/allreduce.h"
#include "../../../src/common/hist_util.cuh"
#include "../../../src/common/quantile.cuh"
#include "../../../src/data/device_adapter.cuh" // CupyAdapter
#include "../collective/test_worker.h" // for BaseMGPUTest
#include "../helpers.h"
#include "test_quantile.h"
@@ -18,9 +19,9 @@ struct IsSorted {
}
};
}
namespace common {
class MGPUQuantileTest : public BaseMGPUTest {};
namespace common {
class MGPUQuantileTest : public collective::BaseMGPUTest {};
TEST(GPUQuantile, Basic) {
constexpr size_t kRows = 1000, kCols = 100, kBins = 256;
@@ -36,7 +37,8 @@ TEST(GPUQuantile, Basic) {
void TestSketchUnique(float sparsity) {
constexpr size_t kRows = 1000, kCols = 100;
RunWithSeedsAndBins(kRows, [kRows, kCols, sparsity](int32_t seed, size_t n_bins, MetaInfo const& info) {
RunWithSeedsAndBins(kRows, [kRows, kCols, sparsity](std::int32_t seed, bst_bin_t n_bins,
MetaInfo const& info) {
HostDeviceVector<FeatureType> ft;
SketchContainer sketch(ft, n_bins, kCols, kRows, FstCU());
@@ -121,7 +123,7 @@ void TestQuantileElemRank(DeviceOrd device, Span<SketchEntry const> in,
TEST(GPUQuantile, Prune) {
constexpr size_t kRows = 1000, kCols = 100;
RunWithSeedsAndBins(kRows, [=](int32_t seed, size_t n_bins, MetaInfo const& info) {
RunWithSeedsAndBins(kRows, [=](std::int32_t seed, bst_bin_t n_bins, MetaInfo const& info) {
HostDeviceVector<FeatureType> ft;
SketchContainer sketch(ft, n_bins, kCols, kRows, FstCU());
@@ -190,7 +192,7 @@ TEST(GPUQuantile, MergeEmpty) {
TEST(GPUQuantile, MergeBasic) {
constexpr size_t kRows = 1000, kCols = 100;
RunWithSeedsAndBins(kRows, [=](int32_t seed, size_t n_bins, MetaInfo const &info) {
RunWithSeedsAndBins(kRows, [=](std::int32_t seed, bst_bin_t n_bins, MetaInfo const& info) {
HostDeviceVector<FeatureType> ft;
SketchContainer sketch_0(ft, n_bins, kCols, kRows, FstCU());
HostDeviceVector<float> storage_0;
@@ -260,9 +262,9 @@ void TestMergeDuplicated(int32_t n_bins, size_t cols, size_t rows, float frac) {
using Tuple = thrust::tuple<size_t, float>;
auto it = thrust::make_zip_iterator(tuple_it);
thrust::transform(thrust::device, it, it + data_1.size(), data_1.data(),
[=] __device__(Tuple const &tuple) {
[=] XGBOOST_DEVICE(Tuple const& tuple) {
auto i = thrust::get<0>(tuple);
if (thrust::get<0>(tuple) % 2 == 0) {
if (i % 2 == 0) {
return 0.0f;
} else {
return thrust::get<1>(tuple);
@@ -306,7 +308,7 @@ TEST(GPUQuantile, MergeDuplicated) {
TEST(GPUQuantile, MultiMerge) {
constexpr size_t kRows = 20, kCols = 1;
int32_t world = 2;
RunWithSeedsAndBins(kRows, [=](int32_t seed, size_t n_bins, MetaInfo const& info) {
RunWithSeedsAndBins(kRows, [=](std::int32_t seed, bst_bin_t n_bins, MetaInfo const& info) {
// Set up single node version
HostDeviceVector<FeatureType> ft;
SketchContainer sketch_on_single_node(ft, n_bins, kCols, kRows, FstCU());
@@ -368,16 +370,18 @@ namespace {
void TestAllReduceBasic() {
auto const world = collective::GetWorldSize();
constexpr size_t kRows = 1000, kCols = 100;
RunWithSeedsAndBins(kRows, [=](int32_t seed, size_t n_bins, MetaInfo const& info) {
RunWithSeedsAndBins(kRows, [=](std::int32_t seed, bst_bin_t n_bins, MetaInfo const& info) {
auto const device = DeviceOrd::CUDA(GPUIDX);
auto ctx = MakeCUDACtx(device.ordinal);
// Set up single node version;
/**
* Set up single node version.
*/
HostDeviceVector<FeatureType> ft({}, device);
SketchContainer sketch_on_single_node(ft, n_bins, kCols, kRows, device);
size_t intermediate_num_cuts = std::min(
kRows * world, static_cast<size_t>(n_bins * WQSketch::kFactor));
size_t intermediate_num_cuts =
std::min(kRows * world, static_cast<size_t>(n_bins * WQSketch::kFactor));
std::vector<SketchContainer> containers;
for (auto rank = 0; rank < world; ++rank) {
HostDeviceVector<float> storage({}, device);
@@ -388,21 +392,22 @@ void TestAllReduceBasic() {
data::CupyAdapter adapter(interface_str);
HostDeviceVector<FeatureType> ft({}, device);
containers.emplace_back(ft, n_bins, kCols, kRows, device);
AdapterDeviceSketch(adapter.Value(), n_bins, info,
std::numeric_limits<float>::quiet_NaN(),
AdapterDeviceSketch(adapter.Value(), n_bins, info, std::numeric_limits<float>::quiet_NaN(),
&containers.back());
}
for (auto &sketch : containers) {
for (auto& sketch : containers) {
sketch.Prune(intermediate_num_cuts);
sketch_on_single_node.Merge(sketch.ColumnsPtr(), sketch.Data());
sketch_on_single_node.FixError();
}
sketch_on_single_node.Unique();
TestQuantileElemRank(device, sketch_on_single_node.Data(),
sketch_on_single_node.ColumnsPtr(), true);
TestQuantileElemRank(device, sketch_on_single_node.Data(), sketch_on_single_node.ColumnsPtr(),
true);
// Set up distributed version. We rely on using rank as seed to generate
// the exact same copy of data.
/**
* Set up distributed version. We rely on using rank as seed to generate
* the exact same copy of data.
*/
auto rank = collective::GetRank();
SketchContainer sketch_distributed(ft, n_bins, kCols, kRows, device);
HostDeviceVector<float> storage({}, device);
@@ -411,22 +416,23 @@ void TestAllReduceBasic() {
.Seed(rank + seed)
.GenerateArrayInterface(&storage);
data::CupyAdapter adapter(interface_str);
AdapterDeviceSketch(adapter.Value(), n_bins, info,
std::numeric_limits<float>::quiet_NaN(),
AdapterDeviceSketch(adapter.Value(), n_bins, info, std::numeric_limits<float>::quiet_NaN(),
&sketch_distributed);
if (world == 1) {
auto n_samples_global = kRows * world;
intermediate_num_cuts =
std::min(n_samples_global, static_cast<size_t>(n_bins * SketchContainer::kFactor));
sketch_distributed.Prune(intermediate_num_cuts);
}
sketch_distributed.AllReduce(&ctx, false);
sketch_distributed.Unique();
ASSERT_EQ(sketch_distributed.ColumnsPtr().size(),
sketch_on_single_node.ColumnsPtr().size());
ASSERT_EQ(sketch_distributed.Data().size(),
sketch_on_single_node.Data().size());
ASSERT_EQ(sketch_distributed.ColumnsPtr().size(), sketch_on_single_node.ColumnsPtr().size());
ASSERT_EQ(sketch_distributed.Data().size(), sketch_on_single_node.Data().size());
TestQuantileElemRank(device, sketch_distributed.Data(),
sketch_distributed.ColumnsPtr(), true);
TestQuantileElemRank(device, sketch_distributed.Data(), sketch_distributed.ColumnsPtr(), true);
std::vector<SketchEntry> single_node_data(
sketch_on_single_node.Data().size());
std::vector<SketchEntry> single_node_data(sketch_on_single_node.Data().size());
dh::CopyDeviceSpanToVector(&single_node_data, sketch_on_single_node.Data());
std::vector<SketchEntry> distributed_data(sketch_distributed.Data().size());
@@ -444,7 +450,8 @@ void TestAllReduceBasic() {
} // anonymous namespace
TEST_F(MGPUQuantileTest, AllReduceBasic) {
DoTest(TestAllReduceBasic);
this->DoTest([] { TestAllReduceBasic(); }, true);
this->DoTest([] { TestAllReduceBasic(); }, false);
}
namespace {
@@ -490,7 +497,8 @@ void TestColumnSplit(DMatrix* dmat) {
TEST_F(MGPUQuantileTest, ColumnSplitBasic) {
std::size_t constexpr kRows = 1000, kCols = 100;
auto dmat = RandomDataGenerator{kRows, kCols, 0}.GenerateDMatrix();
DoTest(TestColumnSplit, dmat.get());
this->DoTest([&] { TestColumnSplit(dmat.get()); }, true);
this->DoTest([&] { TestColumnSplit(dmat.get()); }, false);
}
TEST_F(MGPUQuantileTest, ColumnSplitCategorical) {
@@ -507,15 +515,15 @@ TEST_F(MGPUQuantileTest, ColumnSplitCategorical) {
.Type(ft)
.MaxCategory(13)
.GenerateDMatrix();
DoTest(TestColumnSplit, dmat.get());
this->DoTest([&] { TestColumnSplit(dmat.get()); }, true);
this->DoTest([&] { TestColumnSplit(dmat.get()); }, false);
}
namespace {
void TestSameOnAllWorkers() {
auto world = collective::GetWorldSize();
constexpr size_t kRows = 1000, kCols = 100;
RunWithSeedsAndBins(kRows, [=](int32_t seed, size_t n_bins,
MetaInfo const &info) {
RunWithSeedsAndBins(kRows, [=](std::int32_t seed, bst_bin_t n_bins, MetaInfo const& info) {
auto const rank = collective::GetRank();
auto const device = DeviceOrd::CUDA(GPUIDX);
Context ctx = MakeCUDACtx(device.ordinal);
@@ -536,7 +544,8 @@ void TestSameOnAllWorkers() {
// Test for all workers having the same sketch.
size_t n_data = sketch_distributed.Data().size();
collective::Allreduce<collective::Operation::kMax>(&n_data, 1);
auto rc = collective::Allreduce(&ctx, linalg::MakeVec(&n_data, 1), collective::Op::kMax);
SafeColl(rc);
ASSERT_EQ(n_data, sketch_distributed.Data().size());
size_t size_as_float =
sketch_distributed.Data().size_bytes() / sizeof(float);
@@ -549,9 +558,10 @@ void TestSameOnAllWorkers() {
thrust::copy(thrust::device, local_data.data(),
local_data.data() + local_data.size(),
all_workers.begin() + local_data.size() * rank);
collective::AllReduce<collective::Operation::kSum>(device.ordinal, all_workers.data().get(),
all_workers.size());
collective::Synchronize(device.ordinal);
rc = collective::Allreduce(
&ctx, linalg::MakeVec(all_workers.data().get(), all_workers.size(), ctx.Device()),
collective::Op::kSum);
SafeColl(rc);
auto base_line = dh::ToSpan(all_workers).subspan(0, size_as_float);
std::vector<float> h_base_line(base_line.size());
@@ -573,7 +583,8 @@ void TestSameOnAllWorkers() {
} // anonymous namespace
TEST_F(MGPUQuantileTest, SameOnAllWorkers) {
DoTest(TestSameOnAllWorkers);
this->DoTest([] { TestSameOnAllWorkers(); }, true);
this->DoTest([] { TestSameOnAllWorkers(); }, false);
}
TEST(GPUQuantile, Push) {

View File

@@ -1,21 +1,22 @@
/**
* Copyright 2020-2024, XGBoost Contributors
*/
#ifndef XGBOOST_TESTS_CPP_COMMON_TEST_QUANTILE_H_
#define XGBOOST_TESTS_CPP_COMMON_TEST_QUANTILE_H_
#include <algorithm>
#include <string>
#include <vector>
#include "../helpers.h"
namespace xgboost {
namespace common {
namespace xgboost::common {
template <typename Fn> void RunWithSeedsAndBins(size_t rows, Fn fn) {
std::vector<int32_t> seeds(2);
SimpleLCG lcg;
SimpleRealUniformDistribution<float> dist(3, 1000);
std::generate(seeds.begin(), seeds.end(), [&](){ return dist(&lcg); });
std::vector<size_t> bins(2);
std::vector<bst_bin_t> bins(2);
for (size_t i = 0; i < bins.size() - 1; ++i) {
bins[i] = i * 35 + 2;
}
@@ -36,7 +37,6 @@ template <typename Fn> void RunWithSeedsAndBins(size_t rows, Fn fn) {
}
}
}
} // namespace common
} // namespace xgboost
} // namespace xgboost::common
#endif // XGBOOST_TESTS_CPP_COMMON_TEST_QUANTILE_H_

View File

@@ -3,15 +3,16 @@
*/
#include "test_metainfo.h"
#include <gmock/gmock.h>
#include <dmlc/io.h>
#include <gmock/gmock.h>
#include <xgboost/data.h>
#include <memory>
#include <string>
#include "../filesystem.h" // dmlc::TemporaryDirectory
#include "../helpers.h" // for GMockTHrow
#include "../collective/test_worker.h" // for TestDistributedGlobal
#include "../filesystem.h" // dmlc::TemporaryDirectory
#include "../helpers.h" // for GMockTHrow
#include "xgboost/base.h"
namespace xgboost {
@@ -118,8 +119,8 @@ void VerifyGetSetFeatureColumnSplit() {
} // anonymous namespace
TEST(MetaInfo, GetSetFeatureColumnSplit) {
auto constexpr kWorldSize{3};
RunWithInMemoryCommunicator(kWorldSize, VerifyGetSetFeatureColumnSplit);
auto constexpr kWorkers{3};
collective::TestDistributedGlobal(kWorkers, VerifyGetSetFeatureColumnSplit);
}
TEST(MetaInfo, SaveLoadBinary) {

View File

@@ -9,6 +9,7 @@
#include "../../../src/data/adapter.h" // ArrayAdapter
#include "../../../src/data/simple_dmatrix.h" // SimpleDMatrix
#include "../collective/test_worker.h" // for TestDistributedGlobal
#include "../filesystem.h" // dmlc::TemporaryDirectory
#include "../helpers.h" // RandomDataGenerator,CreateSimpleTestData
#include "xgboost/base.h"
@@ -444,5 +445,5 @@ void VerifyColumnSplit() {
TEST(SimpleDMatrix, ColumnSplit) {
auto constexpr kWorldSize{3};
RunWithInMemoryCommunicator(kWorldSize, VerifyColumnSplit);
collective::TestDistributedGlobal(kWorldSize, VerifyColumnSplit);
}

View File

@@ -520,67 +520,6 @@ inline LearnerModelParam MakeMP(bst_feature_t n_features, float base_score, uint
inline std::int32_t AllThreadsForTest() { return Context{}.Threads(); }
template <bool use_nccl = false, typename Function, typename... Args>
void RunWithInMemoryCommunicator(int32_t world_size, Function&& function, Args&&... args) {
auto run = [&](auto rank) {
Json config{JsonObject()};
if constexpr (use_nccl) {
config["xgboost_communicator"] = String("in-memory-nccl");
} else {
config["xgboost_communicator"] = String("in-memory");
}
config["in_memory_world_size"] = world_size;
config["in_memory_rank"] = rank;
xgboost::collective::Init(config);
std::forward<Function>(function)(std::forward<Args>(args)...);
xgboost::collective::Finalize();
};
#if defined(_OPENMP)
common::ParallelFor(world_size, world_size, run);
#else
std::vector<std::thread> threads;
for (auto rank = 0; rank < world_size; rank++) {
threads.emplace_back(run, rank);
}
for (auto& thread : threads) {
thread.join();
}
#endif
}
class BaseMGPUTest : public ::testing::Test {
protected:
int world_size_;
bool use_nccl_{false};
void SetUp() override {
auto const n_gpus = common::AllVisibleGPUs();
if (n_gpus <= 1) {
// Use a single GPU to simulate distributed environment.
world_size_ = 3;
// NCCL doesn't like sharing a single GPU, so we use the adapter instead.
use_nccl_ = false;
} else {
// Use multiple GPUs for real.
world_size_ = n_gpus;
use_nccl_ = true;
}
}
template <typename Function, typename... Args>
void DoTest(Function&& function, Args&&... args) {
if (use_nccl_) {
RunWithInMemoryCommunicator<true>(world_size_, function, args...);
} else {
RunWithInMemoryCommunicator<false>(world_size_, function, args...);
}
}
};
class DeclareUnifiedDistributedTest(MetricTest) : public BaseMGPUTest{};
inline DeviceOrd FstCU() { return DeviceOrd::CUDA(0); }
inline auto GMockThrow(StringView msg) {

View File

@@ -1,68 +0,0 @@
#include "test_auc.h"
#include <xgboost/metric.h>
namespace xgboost {
namespace metric {
TEST(Metric, DeclareUnifiedTest(BinaryAUC)) { VerifyBinaryAUC(); }
TEST(Metric, DeclareUnifiedTest(MultiClassAUC)) { VerifyMultiClassAUC(); }
TEST(Metric, DeclareUnifiedTest(RankingAUC)) { VerifyRankingAUC(); }
TEST(Metric, DeclareUnifiedTest(PRAUC)) { VerifyPRAUC(); }
TEST(Metric, DeclareUnifiedTest(MultiClassPRAUC)) { VerifyMultiClassPRAUC(); }
TEST(Metric, DeclareUnifiedTest(RankingPRAUC)) { VerifyRankingPRAUC(); }
TEST_F(DeclareUnifiedDistributedTest(MetricTest), BinaryAUCRowSplit) {
DoTest(VerifyBinaryAUC, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), BinaryAUCColumnSplit) {
DoTest(VerifyBinaryAUC, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassAUCRowSplit) {
DoTest(VerifyMultiClassAUC, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassAUCColumnSplit) {
DoTest(VerifyMultiClassAUC, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RankingAUCRowSplit) {
DoTest(VerifyRankingAUC, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RankingAUCColumnSplit) {
DoTest(VerifyRankingAUC, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), PRAUCRowSplit) {
DoTest(VerifyPRAUC, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), PRAUCColumnSplit) {
DoTest(VerifyPRAUC, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassPRAUCRowSplit) {
DoTest(VerifyMultiClassPRAUC, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassPRAUCColumnSplit) {
DoTest(VerifyMultiClassPRAUC, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RankingPRAUCRowSplit) {
DoTest(VerifyRankingPRAUC, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RankingPRAUCColumnSplit) {
DoTest(VerifyRankingPRAUC, DataSplitMode::kCol);
}
} // namespace metric
} // namespace xgboost

View File

@@ -1,5 +0,0 @@
/*!
* Copyright 2021 XGBoost contributors
*/
// Dummy file to keep the CUDA conditional compile trick.
#include "test_auc.cc"

View File

@@ -7,11 +7,9 @@
#include "../helpers.h"
namespace xgboost {
namespace metric {
inline void VerifyBinaryAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
namespace xgboost::metric {
inline void VerifyBinaryAUC(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
std::unique_ptr<Metric> uni_ptr{Metric::Create("auc", &ctx)};
Metric* metric = uni_ptr.get();
ASSERT_STREQ(metric->Name(), "auc");
@@ -53,8 +51,8 @@ inline void VerifyBinaryAUC(DataSplitMode data_split_mode = DataSplitMode::kRow)
0.5, 1e-10);
}
inline void VerifyMultiClassAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyMultiClassAUC(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
std::unique_ptr<Metric> uni_ptr{Metric::Create("auc", &ctx)};
auto metric = uni_ptr.get();
@@ -114,8 +112,8 @@ inline void VerifyMultiClassAUC(DataSplitMode data_split_mode = DataSplitMode::k
ASSERT_GT(auc, 0.714);
}
inline void VerifyRankingAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyRankingAUC(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
std::unique_ptr<Metric> metric{Metric::Create("auc", &ctx)};
// single group
@@ -148,8 +146,8 @@ inline void VerifyRankingAUC(DataSplitMode data_split_mode = DataSplitMode::kRow
0.769841f, 1e-6);
}
inline void VerifyPRAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyPRAUC(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
xgboost::Metric* metric = xgboost::Metric::Create("aucpr", &ctx);
ASSERT_STREQ(metric->Name(), "aucpr");
@@ -185,8 +183,8 @@ inline void VerifyPRAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
delete metric;
}
inline void VerifyMultiClassPRAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyMultiClassPRAUC(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
std::unique_ptr<Metric> metric{Metric::Create("aucpr", &ctx)};
@@ -209,8 +207,8 @@ inline void VerifyMultiClassPRAUC(DataSplitMode data_split_mode = DataSplitMode:
ASSERT_GT(auc, 0.699);
}
inline void VerifyRankingPRAUC(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyRankingPRAUC(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
std::unique_ptr<Metric> metric{Metric::Create("aucpr", &ctx)};
@@ -245,5 +243,4 @@ inline void VerifyRankingPRAUC(DataSplitMode data_split_mode = DataSplitMode::kR
data_split_mode),
0.556021f, 0.001f);
}
} // namespace metric
} // namespace xgboost
} // namespace xgboost::metric

View File

@@ -0,0 +1,192 @@
/**
* Copyright 2023, XGBoost contributors
*/
#include <gtest/gtest.h>
#include <xgboost/context.h> // for DeviceOrd
#include <xgboost/data.h> // for DataSplitMode
#include <algorithm> // for min
#include <cstdint> // for int32_t
#include <functional> // for function
#include <string> // for string
#include <thread> // for thread
#include "../collective/test_worker.h" // for TestDistributedGlobal
#include "test_auc.h"
#include "test_elementwise_metric.h"
#include "test_multiclass_metric.h"
#include "test_rank_metric.h"
#include "test_survival_metric.h"
#if defined(XGBOOST_USE_FEDERATED)
#include "../plugin/federated/test_worker.h" // for TestFederatedGlobal
#endif // defined(XGBOOST_USE_FEDERATED)
namespace xgboost::metric {
namespace {
using Verifier = std::function<void(DataSplitMode, DeviceOrd)>;
struct Param {
bool is_dist; // is distributed
bool is_fed; // is federated learning
DataSplitMode split; // how to split data
Verifier v; // test function
std::string name; // metric name
DeviceOrd device; // device to run
};
class TestDistributedMetric : public ::testing::TestWithParam<Param> {
protected:
template <typename Fn>
void Run(bool is_dist, bool is_fed, DataSplitMode split_mode, Fn fn, DeviceOrd device) {
if (!is_dist) {
fn(split_mode, device);
return;
}
std::int32_t n_workers{0};
if (device.IsCUDA()) {
n_workers = common::AllVisibleGPUs();
} else {
n_workers = std::min(static_cast<std::int32_t>(std::thread::hardware_concurrency()), 3);
}
auto fn1 = [&]() {
auto r = collective::GetRank();
if (device.IsCPU()) {
fn(split_mode, DeviceOrd::CPU());
} else {
fn(split_mode, DeviceOrd::CUDA(r));
}
};
if (is_fed) {
#if defined(XGBOOST_USE_FEDERATED)
collective::TestFederatedGlobal(n_workers, fn1);
#endif // defined(XGBOOST_USE_FEDERATED)
} else {
collective::TestDistributedGlobal(n_workers, fn1);
}
}
};
} // anonymous namespace
TEST_P(TestDistributedMetric, BinaryAUCRowSplit) {
auto p = GetParam();
this->Run(p.is_dist, p.is_fed, p.split, p.v, p.device);
}
constexpr bool UseNCCL() {
#if defined(XGBOOST_USE_NCCL)
return true;
#else
return false;
#endif // defined(XGBOOST_USE_NCCL)
}
constexpr bool UseCUDA() {
#if defined(XGBOOST_USE_CUDA)
return true;
#else
return false;
#endif // defined(XGBOOST_USE_CUDA)
}
constexpr bool UseFederated() {
#if defined(XGBOOST_USE_FEDERATED)
return true;
#else
return false;
#endif
}
auto MakeParamsForTest() {
std::vector<Param> cases;
auto push = [&](std::string name, auto fn) {
for (bool is_federated : {false, true}) {
for (DataSplitMode m : {DataSplitMode::kCol, DataSplitMode::kRow}) {
for (auto d : {DeviceOrd::CPU(), DeviceOrd::CUDA(0)}) {
if (!is_federated && !UseNCCL() && d.IsCUDA()) {
// Federated doesn't use nccl.
continue;
}
if (!UseCUDA() && d.IsCUDA()) {
// skip CUDA tests
continue;
}
if (!UseFederated() && is_federated) {
// skip GRPC tests
continue;
}
auto p = Param{true, is_federated, m, fn, name, d};
cases.push_back(p);
if (!is_federated) {
// Add a local test.
p.is_dist = false;
cases.push_back(p);
}
}
}
}
};
#define REFLECT_NAME(name) push(#name, Verify##name)
// AUC
REFLECT_NAME(BinaryAUC);
REFLECT_NAME(MultiClassAUC);
REFLECT_NAME(RankingAUC);
REFLECT_NAME(PRAUC);
REFLECT_NAME(MultiClassPRAUC);
REFLECT_NAME(RankingPRAUC);
// Elementwise
REFLECT_NAME(RMSE);
REFLECT_NAME(RMSLE);
REFLECT_NAME(MAE);
REFLECT_NAME(MAPE);
REFLECT_NAME(MPHE);
REFLECT_NAME(LogLoss);
REFLECT_NAME(Error);
REFLECT_NAME(PoissonNegLogLik);
REFLECT_NAME(MultiRMSE);
REFLECT_NAME(Quantile);
// Multi-Class
REFLECT_NAME(MultiClassError);
REFLECT_NAME(MultiClassLogLoss);
// Ranking
REFLECT_NAME(Precision);
REFLECT_NAME(NDCG);
REFLECT_NAME(MAP);
REFLECT_NAME(NDCGExpGain);
// AFT
using namespace xgboost::common; // NOLINT
REFLECT_NAME(AFTNegLogLik);
REFLECT_NAME(IntervalRegressionAccuracy);
#undef REFLECT_NAME
return cases;
}
INSTANTIATE_TEST_SUITE_P(
DistributedMetric, TestDistributedMetric, ::testing::ValuesIn(MakeParamsForTest()),
[](const ::testing::TestParamInfo<TestDistributedMetric::ParamType>& info) {
std::string result;
if (info.param.is_dist) {
result += "Dist_";
}
if (info.param.is_fed) {
result += "Federated_";
}
if (info.param.split == DataSplitMode::kRow) {
result += "RowSplit";
} else {
result += "ColSplit";
}
result += "_";
result += info.param.device.IsCPU() ? "CPU" : "CUDA";
result += "_";
result += info.param.name;
return result;
});
} // namespace xgboost::metric

View File

@@ -1,106 +0,0 @@
/**
* Copyright 2018-2023 by XGBoost contributors
*/
#include "test_elementwise_metric.h"
namespace xgboost::metric {
TEST(Metric, DeclareUnifiedTest(RMSE)) { VerifyRMSE(); }
TEST(Metric, DeclareUnifiedTest(RMSLE)) { VerifyRMSLE(); }
TEST(Metric, DeclareUnifiedTest(MAE)) { VerifyMAE(); }
TEST(Metric, DeclareUnifiedTest(MAPE)) { VerifyMAPE(); }
TEST(Metric, DeclareUnifiedTest(MPHE)) { VerifyMPHE(); }
TEST(Metric, DeclareUnifiedTest(LogLoss)) { VerifyLogLoss(); }
TEST(Metric, DeclareUnifiedTest(Error)) { VerifyError(); }
TEST(Metric, DeclareUnifiedTest(PoissonNegLogLik)) { VerifyPoissonNegLogLik(); }
TEST(Metric, DeclareUnifiedTest(MultiRMSE)) { VerifyMultiRMSE(); }
TEST(Metric, DeclareUnifiedTest(Quantile)) { VerifyQuantile(); }
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RMSERowSplit) {
DoTest(VerifyRMSE, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RMSEColumnSplit) {
DoTest(VerifyRMSE, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RMSLERowSplit) {
DoTest(VerifyRMSLE, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), RMSLEColumnSplit) {
DoTest(VerifyRMSLE, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MAERowSplit) {
DoTest(VerifyMAE, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MAEColumnSplit) {
DoTest(VerifyMAE, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MAPERowSplit) {
DoTest(VerifyMAPE, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MAPEColumnSplit) {
DoTest(VerifyMAPE, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MPHERowSplit) {
DoTest(VerifyMPHE, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MPHEColumnSplit) {
DoTest(VerifyMPHE, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), LogLossRowSplit) {
DoTest(VerifyLogLoss, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), LogLossColumnSplit) {
DoTest(VerifyLogLoss, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), ErrorRowSplit) {
DoTest(VerifyError, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), ErrorColumnSplit) {
DoTest(VerifyError, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), PoissonNegLogLikRowSplit) {
DoTest(VerifyPoissonNegLogLik, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), PoissonNegLogLikColumnSplit) {
DoTest(VerifyPoissonNegLogLik, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiRMSERowSplit) {
DoTest(VerifyMultiRMSE, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiRMSEColumnSplit) {
DoTest(VerifyMultiRMSE, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), QuantileRowSplit) {
DoTest(VerifyQuantile, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), QuantileColumnSplit) {
DoTest(VerifyQuantile, DataSplitMode::kCol);
}
} // namespace xgboost::metric

View File

@@ -1,5 +0,0 @@
/*!
* Copyright 2018 XGBoost contributors
*/
// Dummy file to keep the CUDA conditional compile trick.
#include "test_elementwise_metric.cc"

View File

@@ -42,8 +42,8 @@ inline void CheckDeterministicMetricElementWise(StringView name, int32_t device)
}
}
inline void VerifyRMSE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyRMSE(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
xgboost::Metric * metric = xgboost::Metric::Create("rmse", &ctx);
metric->Configure({});
ASSERT_STREQ(metric->Name(), "rmse");
@@ -68,11 +68,11 @@ inline void VerifyRMSE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
0.6708f, 0.001f);
delete metric;
CheckDeterministicMetricElementWise(StringView{"rmse"}, GPUIDX);
CheckDeterministicMetricElementWise(StringView{"rmse"}, device.ordinal);
}
inline void VerifyRMSLE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyRMSLE(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
xgboost::Metric * metric = xgboost::Metric::Create("rmsle", &ctx);
metric->Configure({});
ASSERT_STREQ(metric->Name(), "rmsle");
@@ -97,11 +97,11 @@ inline void VerifyRMSLE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
0.2415f, 1e-4);
delete metric;
CheckDeterministicMetricElementWise(StringView{"rmsle"}, GPUIDX);
CheckDeterministicMetricElementWise(StringView{"rmsle"}, device.ordinal);
}
inline void VerifyMAE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyMAE(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
xgboost::Metric * metric = xgboost::Metric::Create("mae", &ctx);
metric->Configure({});
ASSERT_STREQ(metric->Name(), "mae");
@@ -126,11 +126,11 @@ inline void VerifyMAE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
0.54f, 0.001f);
delete metric;
CheckDeterministicMetricElementWise(StringView{"mae"}, GPUIDX);
CheckDeterministicMetricElementWise(StringView{"mae"}, device.ordinal);
}
inline void VerifyMAPE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyMAPE(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
xgboost::Metric * metric = xgboost::Metric::Create("mape", &ctx);
metric->Configure({});
ASSERT_STREQ(metric->Name(), "mape");
@@ -155,11 +155,11 @@ inline void VerifyMAPE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
1.3250f, 0.001f);
delete metric;
CheckDeterministicMetricElementWise(StringView{"mape"}, GPUIDX);
CheckDeterministicMetricElementWise(StringView{"mape"}, device.ordinal);
}
inline void VerifyMPHE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyMPHE(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
std::unique_ptr<xgboost::Metric> metric{xgboost::Metric::Create("mphe", &ctx)};
metric->Configure({});
ASSERT_STREQ(metric->Name(), "mphe");
@@ -183,7 +183,7 @@ inline void VerifyMPHE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
{ 1, 2, 9, 8}, {}, data_split_mode),
0.1922f, 1e-4);
CheckDeterministicMetricElementWise(StringView{"mphe"}, GPUIDX);
CheckDeterministicMetricElementWise(StringView{"mphe"}, device.ordinal);
metric->Configure({{"huber_slope", "0.1"}});
EXPECT_NEAR(GetMetricEval(metric.get(),
@@ -193,8 +193,8 @@ inline void VerifyMPHE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
0.0461686f, 1e-4);
}
inline void VerifyLogLoss(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyLogLoss(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
xgboost::Metric * metric = xgboost::Metric::Create("logloss", &ctx);
metric->Configure({});
ASSERT_STREQ(metric->Name(), "logloss");
@@ -223,11 +223,11 @@ inline void VerifyLogLoss(DataSplitMode data_split_mode = DataSplitMode::kRow) {
1.3138f, 0.001f);
delete metric;
CheckDeterministicMetricElementWise(StringView{"logloss"}, GPUIDX);
CheckDeterministicMetricElementWise(StringView{"logloss"}, device.ordinal);
}
inline void VerifyError(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyError(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
xgboost::Metric * metric = xgboost::Metric::Create("error", &ctx);
metric->Configure({});
ASSERT_STREQ(metric->Name(), "error");
@@ -285,11 +285,11 @@ inline void VerifyError(DataSplitMode data_split_mode = DataSplitMode::kRow) {
0.45f, 0.001f);
delete metric;
CheckDeterministicMetricElementWise(StringView{"error@0.5"}, GPUIDX);
CheckDeterministicMetricElementWise(StringView{"error@0.5"}, device.ordinal);
}
inline void VerifyPoissonNegLogLik(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyPoissonNegLogLik(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
xgboost::Metric * metric = xgboost::Metric::Create("poisson-nloglik", &ctx);
metric->Configure({});
ASSERT_STREQ(metric->Name(), "poisson-nloglik");
@@ -318,11 +318,11 @@ inline void VerifyPoissonNegLogLik(DataSplitMode data_split_mode = DataSplitMode
1.5783f, 0.001f);
delete metric;
CheckDeterministicMetricElementWise(StringView{"poisson-nloglik"}, GPUIDX);
CheckDeterministicMetricElementWise(StringView{"poisson-nloglik"}, device.ordinal);
}
inline void VerifyMultiRMSE(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyMultiRMSE(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
size_t n_samples = 32, n_targets = 8;
linalg::Tensor<float, 2> y{{n_samples, n_targets}, ctx.Device()};
auto &h_y = y.Data()->HostVector();
@@ -343,8 +343,8 @@ inline void VerifyMultiRMSE(DataSplitMode data_split_mode = DataSplitMode::kRow)
ASSERT_FLOAT_EQ(ret, loss_w);
}
inline void VerifyQuantile(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyQuantile(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
std::unique_ptr<Metric> metric{Metric::Create("quantile", &ctx)};
HostDeviceVector<float> predts{0.1f, 0.9f, 0.1f, 0.9f};

View File

@@ -1,29 +0,0 @@
// Copyright by Contributors
#include "test_multiclass_metric.h"
#include <string>
namespace xgboost {
namespace metric {
TEST(Metric, DeclareUnifiedTest(MultiClassError)) { VerifyMultiClassError(); }
TEST(Metric, DeclareUnifiedTest(MultiClassLogLoss)) { VerifyMultiClassLogLoss(); }
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassErrorRowSplit) {
DoTest(VerifyMultiClassError, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassErrorColumnSplit) {
DoTest(VerifyMultiClassError, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassLogLossRowSplit) {
DoTest(VerifyMultiClassLogLoss, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MultiClassLogLossColumnSplit) {
DoTest(VerifyMultiClassLogLoss, DataSplitMode::kCol);
}
} // namespace metric
} // namespace xgboost

View File

@@ -1,5 +0,0 @@
/*!
* Copyright 2019 XGBoost contributors
*/
// Dummy file to keep the CUDA conditional compile trick.
#include "test_multiclass_metric.cc"

View File

@@ -44,8 +44,8 @@ inline void CheckDeterministicMetricMultiClass(StringView name, int32_t device)
}
}
inline void TestMultiClassError(int device, DataSplitMode data_split_mode) {
auto ctx = MakeCUDACtx(device);
inline void TestMultiClassError(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
xgboost::Metric * metric = xgboost::Metric::Create("merror", &ctx);
metric->Configure({});
ASSERT_STREQ(metric->Name(), "merror");
@@ -59,13 +59,13 @@ inline void TestMultiClassError(int device, DataSplitMode data_split_mode) {
delete metric;
}
inline void VerifyMultiClassError(DataSplitMode data_split_mode = DataSplitMode::kRow) {
TestMultiClassError(GPUIDX, data_split_mode);
CheckDeterministicMetricMultiClass(StringView{"merror"}, GPUIDX);
inline void VerifyMultiClassError(DataSplitMode data_split_mode, DeviceOrd device) {
TestMultiClassError(data_split_mode, device);
CheckDeterministicMetricMultiClass(StringView{"merror"}, device.ordinal);
}
inline void TestMultiClassLogLoss(int device, DataSplitMode data_split_mode) {
auto ctx = MakeCUDACtx(device);
inline void TestMultiClassLogLoss(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
xgboost::Metric * metric = xgboost::Metric::Create("mlogloss", &ctx);
metric->Configure({});
ASSERT_STREQ(metric->Name(), "mlogloss");
@@ -80,9 +80,9 @@ inline void TestMultiClassLogLoss(int device, DataSplitMode data_split_mode) {
delete metric;
}
inline void VerifyMultiClassLogLoss(DataSplitMode data_split_mode = DataSplitMode::kRow) {
TestMultiClassLogLoss(GPUIDX, data_split_mode);
CheckDeterministicMetricMultiClass(StringView{"mlogloss"}, GPUIDX);
inline void VerifyMultiClassLogLoss(DataSplitMode data_split_mode, DeviceOrd device) {
TestMultiClassLogLoss(data_split_mode, device);
CheckDeterministicMetricMultiClass(StringView{"mlogloss"}, device.ordinal);
}
} // namespace metric

View File

@@ -1,84 +1,29 @@
/**
* Copyright 2016-2023 by XGBoost Contributors
* Copyright 2016-2023, XGBoost Contributors
*/
#include <gtest/gtest.h> // for Test, EXPECT_NEAR, ASSERT_STREQ
#include <xgboost/context.h> // for Context
#include <xgboost/data.h> // for MetaInfo, DMatrix
#include <xgboost/linalg.h> // for Matrix
#include <xgboost/metric.h> // for Metric
#include <algorithm> // for max
#include <memory> // for unique_ptr
#include <vector> // for vector
#include "test_rank_metric.h"
#include "../helpers.h" // for GetMetricEval, CreateEmptyGe...
#include "xgboost/base.h" // for bst_float, kRtEps
#include "xgboost/host_device_vector.h" // for HostDeviceVector
#include "xgboost/json.h" // for Json, String, Object
namespace xgboost {
namespace metric {
#include <gtest/gtest.h> // for Test, EXPECT_NEAR, ASSERT_STREQ
#include <xgboost/context.h> // for Context
#include <xgboost/metric.h> // for Metric
#if !defined(__CUDACC__)
#include <memory> // for unique_ptr
#include "../helpers.h" // for GetMetricEval, CreateEmptyGe...
#include "xgboost/base.h" // for bst_float, kRtEps
namespace xgboost::metric {
TEST(Metric, AMS) {
auto ctx = MakeCUDACtx(GPUIDX);
EXPECT_ANY_THROW(Metric::Create("ams", &ctx));
Metric* metric = Metric::Create("ams@0.5f", &ctx);
std::unique_ptr<Metric> metric{Metric::Create("ams@0.5f", &ctx)};
ASSERT_STREQ(metric->Name(), "ams@0.5");
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0.311f, 0.001f);
EXPECT_NEAR(GetMetricEval(metric,
{0.1f, 0.9f, 0.1f, 0.9f},
{ 0, 0, 1, 1}),
0.29710f, 0.001f);
EXPECT_NEAR(GetMetricEval(metric.get(), {0, 1}, {0, 1}), 0.311f, 0.001f);
EXPECT_NEAR(GetMetricEval(metric.get(), {0.1f, 0.9f, 0.1f, 0.9f}, {0, 0, 1, 1}), 0.29710f,
0.001f);
delete metric;
metric = Metric::Create("ams@0", &ctx);
metric.reset(Metric::Create("ams@0", &ctx));
ASSERT_STREQ(metric->Name(), "ams@0");
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}), 0.311f, 0.001f);
delete metric;
EXPECT_NEAR(GetMetricEval(metric.get(), {0, 1}, {0, 1}), 0.311f, 0.001f);
}
#endif
TEST(Metric, DeclareUnifiedTest(Precision)) { VerifyPrecision(); }
TEST(Metric, DeclareUnifiedTest(NDCG)) { VerifyNDCG(); }
TEST(Metric, DeclareUnifiedTest(MAP)) { VerifyMAP(); }
TEST(Metric, DeclareUnifiedTest(NDCGExpGain)) { VerifyNDCGExpGain(); }
TEST_F(DeclareUnifiedDistributedTest(MetricTest), PrecisionRowSplit) {
DoTest(VerifyPrecision, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), PrecisionColumnSplit) {
DoTest(VerifyPrecision, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), NDCGRowSplit) {
DoTest(VerifyNDCG, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), NDCGColumnSplit) {
DoTest(VerifyNDCG, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MAPRowSplit) {
DoTest(VerifyMAP, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), MAPColumnSplit) {
DoTest(VerifyMAP, DataSplitMode::kCol);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), NDCGExpGainRowSplit) {
DoTest(VerifyNDCGExpGain, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), NDCGExpGainColumnSplit) {
DoTest(VerifyNDCGExpGain, DataSplitMode::kCol);
}
} // namespace metric
} // namespace xgboost
} // namespace xgboost::metric

View File

@@ -1,5 +0,0 @@
/*!
* Copyright 2019 XGBoost contributors
*/
// Dummy file to keep the CUDA conditional compile trick.
#include "test_rank_metric.cc"

View File

@@ -19,8 +19,8 @@
namespace xgboost::metric {
inline void VerifyPrecision(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyPrecision(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
std::unique_ptr<xgboost::Metric> metric{Metric::Create("pre", &ctx)};
ASSERT_STREQ(metric->Name(), "pre");
EXPECT_NEAR(GetMetricEval(metric.get(), {0, 1}, {0, 1}, {}, {}, data_split_mode), 0.5, 1e-7);
@@ -43,8 +43,8 @@ inline void VerifyPrecision(DataSplitMode data_split_mode = DataSplitMode::kRow)
0.5f, 1e-7);
}
inline void VerifyNDCG(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyNDCG(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
Metric * metric = xgboost::Metric::Create("ndcg", &ctx);
ASSERT_STREQ(metric->Name(), "ndcg");
EXPECT_ANY_THROW(GetMetricEval(metric, {0, 1}, {}, {}, {}, data_split_mode));
@@ -101,8 +101,8 @@ inline void VerifyNDCG(DataSplitMode data_split_mode = DataSplitMode::kRow) {
delete metric;
}
inline void VerifyMAP(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyMAP(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
Metric * metric = xgboost::Metric::Create("map", &ctx);
ASSERT_STREQ(metric->Name(), "map");
EXPECT_NEAR(GetMetricEval(metric, {0, 1}, {0, 1}, {}, {}, data_split_mode), 1, kRtEps);
@@ -149,8 +149,8 @@ inline void VerifyMAP(DataSplitMode data_split_mode = DataSplitMode::kRow) {
delete metric;
}
inline void VerifyNDCGExpGain(DataSplitMode data_split_mode = DataSplitMode::kRow) {
Context ctx = MakeCUDACtx(GPUIDX);
inline void VerifyNDCGExpGain(DataSplitMode data_split_mode, DeviceOrd device) {
Context ctx = MakeCUDACtx(device.ordinal);
auto p_fmat = xgboost::RandomDataGenerator{0, 0, 0}.GenerateDMatrix();
MetaInfo& info = p_fmat->Info();

View File

@@ -1,5 +1,5 @@
/*!
* Copyright (c) by Contributors 2020
/**
* Copyright 2020-2023, XGBoost Contributors
*/
#include <gtest/gtest.h>
#include <memory>
@@ -16,8 +16,7 @@
// CUDA conditional compile trick.
#include "test_survival_metric.cu"
namespace xgboost {
namespace common {
namespace xgboost::common {
/** Tests for Survival metrics that should run only on CPU **/
@@ -113,6 +112,4 @@ TEST(AFTLoss, IntervalCensored) {
{ 8.0000, 4.8004, 2.8805, 1.7284, 1.0372, 0.6231, 0.3872, 0.3031, 0.3740, 0.5839, 0.8995,
1.2878, 1.7231, 2.1878, 2.6707, 3.1647, 3.6653, 4.1699, 4.6770, 5.1856 });
}
} // namespace common
} // namespace xgboost
} // namespace xgboost::common

View File

@@ -7,28 +7,7 @@
/** Tests for Survival metrics that should run both on CPU and GPU **/
namespace xgboost {
namespace common {
TEST(Metric, DeclareUnifiedTest(AFTNegLogLik)) { VerifyAFTNegLogLik(); }
TEST_F(DeclareUnifiedDistributedTest(MetricTest), AFTNegLogLikRowSplit) {
DoTest(VerifyAFTNegLogLik, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), AFTNegLogLikColumnSplit) {
DoTest(VerifyAFTNegLogLik, DataSplitMode::kCol);
}
TEST(Metric, DeclareUnifiedTest(IntervalRegressionAccuracy)) { VerifyIntervalRegressionAccuracy(); }
TEST_F(DeclareUnifiedDistributedTest(MetricTest), IntervalRegressionAccuracyRowSplit) {
DoTest(VerifyIntervalRegressionAccuracy, DataSplitMode::kRow);
}
TEST_F(DeclareUnifiedDistributedTest(MetricTest), IntervalRegressionAccuracyColumnSplit) {
DoTest(VerifyIntervalRegressionAccuracy, DataSplitMode::kCol);
}
namespace xgboost::common {
// Test configuration of AFT metric
TEST(AFTNegLogLikMetric, DeclareUnifiedTest(Configuration)) {
auto ctx = MakeCUDACtx(GPUIDX);
@@ -44,5 +23,4 @@ TEST(AFTNegLogLikMetric, DeclareUnifiedTest(Configuration)) {
CheckDeterministicMetricElementWise(StringView{"aft-nloglik"}, GPUIDX);
}
} // namespace common
} // namespace xgboost
} // namespace xgboost::common

View File

@@ -47,8 +47,8 @@ inline void CheckDeterministicMetricElementWise(StringView name, int32_t device)
}
}
inline void VerifyAFTNegLogLik(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyAFTNegLogLik(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
/**
* Test aggregate output from the AFT metric over a small test data set.
@@ -78,8 +78,8 @@ inline void VerifyAFTNegLogLik(DataSplitMode data_split_mode = DataSplitMode::kR
}
}
inline void VerifyIntervalRegressionAccuracy(DataSplitMode data_split_mode = DataSplitMode::kRow) {
auto ctx = MakeCUDACtx(GPUIDX);
inline void VerifyIntervalRegressionAccuracy(DataSplitMode data_split_mode, DeviceOrd device) {
auto ctx = MakeCUDACtx(device.ordinal);
auto p_fmat = EmptyDMatrix();
MetaInfo& info = p_fmat->Info();
@@ -101,7 +101,7 @@ inline void VerifyIntervalRegressionAccuracy(DataSplitMode data_split_mode = Dat
info.labels_lower_bound_.HostVector()[0] = 70.0f;
EXPECT_FLOAT_EQ(metric->Evaluate(preds, p_fmat), 0.25f);
CheckDeterministicMetricElementWise(StringView{"interval-regression-accuracy"}, GPUIDX);
CheckDeterministicMetricElementWise(StringView{"interval-regression-accuracy"}, device.ordinal);
}
} // namespace common
} // namespace xgboost

View File

@@ -50,7 +50,7 @@ class TestDefaultObjConfig : public ::testing::TestWithParam<std::string> {
public:
void Run(std::string objective) {
auto Xy = MakeFmatForObjTest(objective);
auto Xy = MakeFmatForObjTest(objective, 10, 10);
std::unique_ptr<Learner> learner{Learner::Create({Xy})};
std::unique_ptr<ObjFunction> objfn{ObjFunction::Create(objective, &ctx_)};

View File

@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023, XGBoost contributors
* Copyright 2023-2024, XGBoost contributors
*/
#include "objective_helpers.h"
@@ -7,17 +7,17 @@
#include "helpers.h" // for RandomDataGenerator
namespace xgboost {
std::shared_ptr<DMatrix> MakeFmatForObjTest(std::string const& obj) {
auto constexpr kRows = 10, kCols = 10;
auto p_fmat = RandomDataGenerator{kRows, kCols, 0}.GenerateDMatrix(true);
void MakeLabelForObjTest(std::shared_ptr<DMatrix> p_fmat, std::string const& obj) {
auto& h_upper = p_fmat->Info().labels_upper_bound_.HostVector();
auto& h_lower = p_fmat->Info().labels_lower_bound_.HostVector();
h_lower.resize(kRows);
h_upper.resize(kRows);
for (size_t i = 0; i < kRows; ++i) {
h_lower.resize(p_fmat->Info().num_row_);
h_upper.resize(p_fmat->Info().num_row_);
for (size_t i = 0; i < p_fmat->Info().num_row_; ++i) {
h_lower[i] = 1;
h_upper[i] = 10;
}
if (obj.find("rank:") != std::string::npos) {
auto h_label = p_fmat->Info().labels.HostView();
std::size_t k = 0;
@@ -26,6 +26,12 @@ std::shared_ptr<DMatrix> MakeFmatForObjTest(std::string const& obj) {
++k;
}
}
}
std::shared_ptr<DMatrix> MakeFmatForObjTest(std::string const& obj, bst_idx_t n_samples,
bst_feature_t n_features) {
auto p_fmat = RandomDataGenerator{n_samples, n_features, 0}.GenerateDMatrix(true);
MakeLabelForObjTest(p_fmat, obj);
return p_fmat;
};
} // namespace xgboost

View File

@@ -32,5 +32,11 @@ inline std::string ObjTestNameGenerator(const ::testing::TestParamInfo<ParamType
return name;
};
std::shared_ptr<DMatrix> MakeFmatForObjTest(std::string const& obj);
/**
* @brief Construct random label for testing.
*/
void MakeLabelForObjTest(std::shared_ptr<DMatrix> p_fmat, std::string const& obj);
std::shared_ptr<DMatrix> MakeFmatForObjTest(std::string const& obj, bst_idx_t n_samples,
bst_feature_t n_features);
} // namespace xgboost

View File

@@ -108,6 +108,32 @@ TEST_F(FederatedCollTestGPU, Allreduce) {
});
}
TEST(FederatedCollGPUGlobal, Allreduce) {
std::int32_t n_workers = common::AllVisibleGPUs();
TestFederatedGlobal(n_workers, [&] {
auto r = collective::GetRank();
auto world = collective::GetWorldSize();
CHECK_EQ(n_workers, world);
dh::device_vector<std::uint32_t> values(3, r);
auto ctx = MakeCUDACtx(r);
auto rc = collective::Allreduce(
&ctx, linalg::MakeVec(values.data().get(), values.size(), DeviceOrd::CUDA(r)),
Op::kBitwiseOR);
SafeColl(rc);
std::vector<std::uint32_t> expected(values.size(), 0);
for (std::int32_t rank = 0; rank < world; ++rank) {
for (std::size_t i = 0; i < expected.size(); ++i) {
expected[i] |= rank;
}
}
for (std::size_t i = 0; i < expected.size(); ++i) {
CHECK_EQ(expected[i], values[i]);
}
});
}
TEST_F(FederatedCollTestGPU, Broadcast) {
std::int32_t n_workers = common::AllVisibleGPUs();
TestFederated(n_workers, [=](std::shared_ptr<FederatedComm> comm, std::int32_t rank) {

View File

@@ -11,12 +11,24 @@
#include "../../../../plugin/federated/federated_tracker.h"
#include "../../../../src/collective/comm_group.h"
#include "../../../../src/collective/communicator-inl.h"
#include "federated_comm.h" // for FederatedComm
#include "xgboost/json.h" // for Json
namespace xgboost::collective {
inline Json FederatedTestConfig(std::int32_t n_workers, std::int32_t port, std::int32_t i) {
Json config{Object{}};
config["dmlc_communicator"] = std::string{"federated"};
config["dmlc_task_id"] = std::to_string(i);
config["dmlc_retry"] = 2;
config["federated_world_size"] = n_workers;
config["federated_rank"] = i;
config["federated_server_address"] = "0.0.0.0:" + std::to_string(port);
return config;
}
template <typename WorkerFn>
void TestFederated(std::int32_t n_workers, WorkerFn&& fn) {
void TestFederatedImpl(std::int32_t n_workers, WorkerFn&& fn) {
Json config{Object()};
config["federated_secure"] = Boolean{false};
config["n_workers"] = Integer{n_workers};
@@ -30,16 +42,7 @@ void TestFederated(std::int32_t n_workers, WorkerFn&& fn) {
std::int32_t port = tracker.Port();
for (std::int32_t i = 0; i < n_workers; ++i) {
workers.emplace_back([=] {
Json config{Object{}};
config["federated_world_size"] = n_workers;
config["federated_rank"] = i;
config["federated_server_address"] = "0.0.0.0:" + std::to_string(port);
auto comm = std::make_shared<FederatedComm>(
DefaultRetry(), std::chrono::seconds{DefaultTimeoutSec()}, std::to_string(i), config);
fn(comm, i);
});
workers.emplace_back([=] { fn(port, i); });
}
for (auto& t : workers) {
@@ -51,39 +54,33 @@ void TestFederated(std::int32_t n_workers, WorkerFn&& fn) {
ASSERT_TRUE(fut.get().OK());
}
template <typename WorkerFn>
void TestFederated(std::int32_t n_workers, WorkerFn&& fn) {
TestFederatedImpl(n_workers, [&](std::int32_t port, std::int32_t i) {
auto config = FederatedTestConfig(n_workers, port, i);
auto comm = std::make_shared<FederatedComm>(
DefaultRetry(), std::chrono::seconds{DefaultTimeoutSec()}, std::to_string(i), config);
fn(comm, i);
});
}
template <typename WorkerFn>
void TestFederatedGroup(std::int32_t n_workers, WorkerFn&& fn) {
Json config{Object()};
config["federated_secure"] = Boolean{false};
config["n_workers"] = Integer{n_workers};
FederatedTracker tracker{config};
auto fut = tracker.Run();
TestFederatedImpl(n_workers, [&](std::int32_t port, std::int32_t i) {
auto config = FederatedTestConfig(n_workers, port, i);
std::shared_ptr<CommGroup> comm_group{CommGroup::Create(config)};
fn(comm_group, i);
});
}
std::vector<std::thread> workers;
auto rc = tracker.WaitUntilReady();
ASSERT_TRUE(rc.OK()) << rc.Report();
std::int32_t port = tracker.Port();
for (std::int32_t i = 0; i < n_workers; ++i) {
workers.emplace_back([=] {
Json config{Object{}};
config["dmlc_communicator"] = std::string{"federated"};
config["dmlc_task_id"] = std::to_string(i);
config["dmlc_retry"] = 2;
config["federated_world_size"] = n_workers;
config["federated_rank"] = i;
config["federated_server_address"] = "0.0.0.0:" + std::to_string(port);
std::shared_ptr<CommGroup> comm_group{CommGroup::Create(config)};
fn(comm_group, i);
});
}
for (auto& t : workers) {
t.join();
}
rc = tracker.Shutdown();
ASSERT_TRUE(rc.OK()) << rc.Report();
ASSERT_TRUE(fut.get().OK());
template <typename WorkerFn>
void TestFederatedGlobal(std::int32_t n_workers, WorkerFn&& fn) {
TestFederatedImpl(n_workers, [&](std::int32_t port, std::int32_t i) {
auto config = FederatedTestConfig(n_workers, port, i);
collective::Init(config);
fn();
collective::Finalize();
});
}
} // namespace xgboost::collective

View File

@@ -1,99 +0,0 @@
/**
* Copyright 2022-2023, XGBoost contributors
*/
#pragma once
#include <dmlc/omp.h>
#include <grpcpp/server_builder.h>
#include <gtest/gtest.h>
#include <xgboost/json.h>
#include <random>
#include <thread> // for thread, sleep_for
#include "../../../plugin/federated/federated_server.h"
#include "../../../src/collective/communicator-inl.h"
#include "../../../src/common/threading_utils.h"
namespace xgboost {
class ServerForTest {
std::string server_address_;
std::unique_ptr<std::thread> server_thread_;
std::unique_ptr<grpc::Server> server_;
public:
explicit ServerForTest(std::size_t world_size) {
server_thread_.reset(new std::thread([this, world_size] {
grpc::ServerBuilder builder;
xgboost::federated::FederatedService service{static_cast<std::int32_t>(world_size)};
int selected_port;
builder.AddListeningPort("localhost:0", grpc::InsecureServerCredentials(), &selected_port);
builder.RegisterService(&service);
server_ = builder.BuildAndStart();
server_address_ = std::string("localhost:") + std::to_string(selected_port);
server_->Wait();
}));
}
~ServerForTest() {
using namespace std::chrono_literals;
while (!server_) {
std::this_thread::sleep_for(100ms);
}
server_->Shutdown();
while (!server_thread_) {
std::this_thread::sleep_for(100ms);
}
server_thread_->join();
}
auto Address() const {
using namespace std::chrono_literals;
while (server_address_.empty()) {
std::this_thread::sleep_for(100ms);
}
return server_address_;
}
};
class BaseFederatedTest : public ::testing::Test {
protected:
void SetUp() override { server_ = std::make_unique<ServerForTest>(kWorldSize); }
void TearDown() override { server_.reset(nullptr); }
static int constexpr kWorldSize{2};
std::unique_ptr<ServerForTest> server_;
};
template <typename Function, typename... Args>
void RunWithFederatedCommunicator(int32_t world_size, std::string const& server_address,
Function&& function, Args&&... args) {
auto run = [&](auto rank) {
Json config{JsonObject()};
config["xgboost_communicator"] = String("federated");
config["federated_secure"] = false;
config["federated_server_address"] = String(server_address);
config["federated_world_size"] = world_size;
config["federated_rank"] = rank;
xgboost::collective::Init(config);
std::forward<Function>(function)(std::forward<Args>(args)...);
xgboost::collective::Finalize();
};
#if defined(_OPENMP)
common::ParallelFor(world_size, world_size, run);
#else
std::vector<std::thread> threads;
for (auto rank = 0; rank < world_size; rank++) {
threads.emplace_back(run, rank);
}
for (auto& thread : threads) {
thread.join();
}
#endif
}
} // namespace xgboost

View File

@@ -1,97 +0,0 @@
/*!
* Copyright 2022 XGBoost contributors
*/
#include <gtest/gtest.h>
#include <thrust/host_vector.h>
#include <ctime>
#include <iostream>
#include <thread>
#include "../../../plugin/federated/federated_communicator.h"
#include "../../../src/collective/communicator-inl.cuh"
#include "../../../src/collective/device_communicator_adapter.cuh"
#include "../helpers.h"
#include "./helpers.h"
namespace xgboost::collective {
class FederatedAdapterTest : public BaseFederatedTest {};
TEST(FederatedAdapterSimpleTest, ThrowOnInvalidDeviceOrdinal) {
auto construct = []() { DeviceCommunicatorAdapter adapter{-1}; };
EXPECT_THROW(construct(), dmlc::Error);
}
namespace {
void VerifyAllReduceSum() {
auto const world_size = collective::GetWorldSize();
auto const device = GPUIDX;
int count = 3;
common::SetDevice(device);
thrust::device_vector<double> buffer(count, 0);
thrust::sequence(buffer.begin(), buffer.end());
collective::AllReduce<collective::Operation::kSum>(device, buffer.data().get(), count);
thrust::host_vector<double> host_buffer = buffer;
EXPECT_EQ(host_buffer.size(), count);
for (auto i = 0; i < count; i++) {
EXPECT_EQ(host_buffer[i], i * world_size);
}
}
} // anonymous namespace
TEST_F(FederatedAdapterTest, MGPUAllReduceSum) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyAllReduceSum);
}
namespace {
void VerifyAllGather() {
auto const world_size = collective::GetWorldSize();
auto const rank = collective::GetRank();
auto const device = GPUIDX;
common::SetDevice(device);
thrust::device_vector<double> send_buffer(1, rank);
thrust::device_vector<double> receive_buffer(world_size, 0);
collective::AllGather(device, send_buffer.data().get(), receive_buffer.data().get(),
sizeof(double));
thrust::host_vector<double> host_buffer = receive_buffer;
EXPECT_EQ(host_buffer.size(), world_size);
for (auto i = 0; i < world_size; i++) {
EXPECT_EQ(host_buffer[i], i);
}
}
} // anonymous namespace
TEST_F(FederatedAdapterTest, MGPUAllGather) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyAllGather);
}
namespace {
void VerifyAllGatherV() {
auto const world_size = collective::GetWorldSize();
auto const rank = collective::GetRank();
auto const device = GPUIDX;
int const count = rank + 2;
common::SetDevice(device);
thrust::device_vector<char> buffer(count, 0);
thrust::sequence(buffer.begin(), buffer.end());
std::vector<std::size_t> segments(world_size);
dh::caching_device_vector<char> receive_buffer{};
collective::AllGatherV(device, buffer.data().get(), count, &segments, &receive_buffer);
EXPECT_EQ(segments[0], 2);
EXPECT_EQ(segments[1], 3);
thrust::host_vector<char> host_buffer = receive_buffer;
EXPECT_EQ(host_buffer.size(), 5);
int expected[] = {0, 1, 0, 1, 2};
for (auto i = 0; i < 5; i++) {
EXPECT_EQ(host_buffer[i], expected[i]);
}
}
} // anonymous namespace
TEST_F(FederatedAdapterTest, MGPUAllGatherV) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyAllGatherV);
}
} // namespace xgboost::collective

View File

@@ -1,161 +0,0 @@
/*!
* Copyright 2022 XGBoost contributors
*/
#include <dmlc/parameter.h>
#include <gtest/gtest.h>
#include <iostream>
#include <thread>
#include "../../../plugin/federated/federated_communicator.h"
#include "helpers.h"
namespace xgboost::collective {
class FederatedCommunicatorTest : public BaseFederatedTest {
public:
static void VerifyAllgather(int rank, const std::string &server_address) {
FederatedCommunicator comm{kWorldSize, rank, server_address};
CheckAllgather(comm, rank);
}
static void VerifyAllgatherV(int rank, const std::string &server_address) {
FederatedCommunicator comm{kWorldSize, rank, server_address};
CheckAllgatherV(comm, rank);
}
static void VerifyAllreduce(int rank, const std::string &server_address) {
FederatedCommunicator comm{kWorldSize, rank, server_address};
CheckAllreduce(comm);
}
static void VerifyBroadcast(int rank, const std::string &server_address) {
FederatedCommunicator comm{kWorldSize, rank, server_address};
CheckBroadcast(comm, rank);
}
protected:
static void CheckAllgather(FederatedCommunicator &comm, int rank) {
std::string input{static_cast<char>('0' + rank)};
auto output = comm.AllGather(input);
for (auto i = 0; i < kWorldSize; i++) {
EXPECT_EQ(output[i], static_cast<char>('0' + i));
}
}
static void CheckAllgatherV(FederatedCommunicator &comm, int rank) {
std::vector<std::string_view> inputs{"Federated", " Learning!!!"};
auto output = comm.AllGatherV(inputs[rank]);
EXPECT_EQ(output, "Federated Learning!!!");
}
static void CheckAllreduce(FederatedCommunicator &comm) {
int buffer[] = {1, 2, 3, 4, 5};
comm.AllReduce(buffer, sizeof(buffer) / sizeof(buffer[0]), DataType::kInt32, Operation::kSum);
int expected[] = {2, 4, 6, 8, 10};
for (auto i = 0; i < 5; i++) {
EXPECT_EQ(buffer[i], expected[i]);
}
}
static void CheckBroadcast(FederatedCommunicator &comm, int rank) {
if (rank == 0) {
std::string buffer{"hello"};
comm.Broadcast(&buffer[0], buffer.size(), 0);
EXPECT_EQ(buffer, "hello");
} else {
std::string buffer{" "};
comm.Broadcast(&buffer[0], buffer.size(), 0);
EXPECT_EQ(buffer, "hello");
}
}
};
TEST(FederatedCommunicatorSimpleTest, ThrowOnWorldSizeTooSmall) {
auto construct = [] { FederatedCommunicator comm{0, 0, "localhost:0", "", "", ""}; };
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(FederatedCommunicatorSimpleTest, ThrowOnRankTooSmall) {
auto construct = [] { FederatedCommunicator comm{1, -1, "localhost:0", "", "", ""}; };
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(FederatedCommunicatorSimpleTest, ThrowOnRankTooBig) {
auto construct = [] { FederatedCommunicator comm{1, 1, "localhost:0", "", "", ""}; };
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(FederatedCommunicatorSimpleTest, ThrowOnWorldSizeNotInteger) {
auto construct = [] {
Json config{JsonObject()};
config["federated_server_address"] = std::string("localhost:0");
config["federated_world_size"] = std::string("1");
config["federated_rank"] = Integer(0);
FederatedCommunicator::Create(config);
};
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(FederatedCommunicatorSimpleTest, ThrowOnRankNotInteger) {
auto construct = [] {
Json config{JsonObject()};
config["federated_server_address"] = std::string("localhost:0");
config["federated_world_size"] = 1;
config["federated_rank"] = std::string("0");
FederatedCommunicator::Create(config);
};
EXPECT_THROW(construct(), dmlc::Error);
}
TEST(FederatedCommunicatorSimpleTest, GetWorldSizeAndRank) {
FederatedCommunicator comm{6, 3, "localhost:0"};
EXPECT_EQ(comm.GetWorldSize(), 6);
EXPECT_EQ(comm.GetRank(), 3);
}
TEST(FederatedCommunicatorSimpleTest, IsDistributed) {
FederatedCommunicator comm{2, 1, "localhost:0"};
EXPECT_TRUE(comm.IsDistributed());
}
TEST_F(FederatedCommunicatorTest, Allgather) {
std::vector<std::thread> threads;
for (auto rank = 0; rank < kWorldSize; rank++) {
threads.emplace_back(&FederatedCommunicatorTest::VerifyAllgather, rank, server_->Address());
}
for (auto &thread : threads) {
thread.join();
}
}
TEST_F(FederatedCommunicatorTest, AllgatherV) {
std::vector<std::thread> threads;
for (auto rank = 0; rank < kWorldSize; rank++) {
threads.emplace_back(&FederatedCommunicatorTest::VerifyAllgatherV, rank, server_->Address());
}
for (auto &thread : threads) {
thread.join();
}
}
TEST_F(FederatedCommunicatorTest, Allreduce) {
std::vector<std::thread> threads;
for (auto rank = 0; rank < kWorldSize; rank++) {
threads.emplace_back(&FederatedCommunicatorTest::VerifyAllreduce, rank, server_->Address());
}
for (auto &thread : threads) {
thread.join();
}
}
TEST_F(FederatedCommunicatorTest, Broadcast) {
std::vector<std::thread> threads;
for (auto rank = 0; rank < kWorldSize; rank++) {
threads.emplace_back(&FederatedCommunicatorTest::VerifyBroadcast, rank, server_->Address());
}
for (auto &thread : threads) {
thread.join();
}
}
} // namespace xgboost::collective

View File

@@ -6,16 +6,13 @@
#include <thread>
#include "../../../plugin/federated/federated_server.h"
#include "../../../src/collective/communicator-inl.h"
#include "../filesystem.h"
#include "../helpers.h"
#include "helpers.h"
#include "federated/test_worker.h"
namespace xgboost {
class FederatedDataTest : public BaseFederatedTest {};
void VerifyLoadUri() {
auto const rank = collective::GetRank();
@@ -47,7 +44,8 @@ void VerifyLoadUri() {
}
}
TEST_F(FederatedDataTest, LoadUri) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyLoadUri);
TEST(FederatedDataTest, LoadUri) {
static int constexpr kWorldSize{2};
collective::TestFederatedGlobal(kWorldSize, [] { VerifyLoadUri(); });
}
} // namespace xgboost

View File

@@ -1,17 +1,19 @@
/*!
* Copyright 2023 XGBoost contributors
/**
* Copyright 2023-2024, XGBoost contributors
*
* Some other tests for federated learning are in the main test suite (test_learner.cc),
* gaurded by the `XGBOOST_USE_FEDERATED`.
*/
#include <dmlc/parameter.h>
#include <gtest/gtest.h>
#include <xgboost/data.h>
#include <xgboost/objective.h>
#include "../../../plugin/federated/federated_server.h"
#include "../../../src/collective/communicator-inl.h"
#include "../../../src/common/linalg_op.h"
#include "../../../src/common/linalg_op.h" // for begin, end
#include "../helpers.h"
#include "../objective_helpers.h" // for MakeObjNamesForTest, ObjTestNameGenerator
#include "helpers.h"
#include "federated/test_worker.h"
namespace xgboost {
namespace {
@@ -36,32 +38,16 @@ auto MakeModel(std::string tree_method, std::string device, std::string objectiv
return model;
}
void VerifyObjective(size_t rows, size_t cols, float expected_base_score, Json expected_model,
std::string tree_method, std::string device, std::string objective) {
auto const world_size = collective::GetWorldSize();
auto const rank = collective::GetRank();
void VerifyObjective(std::size_t rows, std::size_t cols, float expected_base_score,
Json expected_model, std::string const &tree_method, std::string device,
std::string const &objective) {
auto rank = collective::GetRank();
std::shared_ptr<DMatrix> dmat{RandomDataGenerator{rows, cols, 0}.GenerateDMatrix(rank == 0)};
if (rank == 0) {
auto &h_upper = dmat->Info().labels_upper_bound_.HostVector();
auto &h_lower = dmat->Info().labels_lower_bound_.HostVector();
h_lower.resize(rows);
h_upper.resize(rows);
for (size_t i = 0; i < rows; ++i) {
h_lower[i] = 1;
h_upper[i] = 10;
}
if (objective.find("rank:") != std::string::npos) {
auto h_label = dmat->Info().labels.HostView();
std::size_t k = 0;
for (auto &v : h_label) {
v = k % 2 == 0;
++k;
}
}
MakeLabelForObjTest(dmat, objective);
}
std::shared_ptr<DMatrix> sliced{dmat->SliceCol(world_size, rank)};
std::shared_ptr<DMatrix> sliced{dmat->SliceCol(collective::GetWorldSize(), rank)};
auto model = MakeModel(tree_method, device, objective, sliced);
auto base_score = GetBaseScore(model);
@@ -71,18 +57,15 @@ void VerifyObjective(size_t rows, size_t cols, float expected_base_score, Json e
} // namespace
class VerticalFederatedLearnerTest : public ::testing::TestWithParam<std::string> {
std::unique_ptr<ServerForTest> server_;
static int constexpr kWorldSize{3};
protected:
void SetUp() override { server_ = std::make_unique<ServerForTest>(kWorldSize); }
void TearDown() override { server_.reset(nullptr); }
void Run(std::string tree_method, std::string device, std::string objective) {
static auto constexpr kRows{16};
static auto constexpr kCols{16};
std::shared_ptr<DMatrix> dmat{RandomDataGenerator{kRows, kCols, 0}.GenerateDMatrix(true)};
MakeLabelForObjTest(dmat, objective);
auto &h_upper = dmat->Info().labels_upper_bound_.HostVector();
auto &h_lower = dmat->Info().labels_lower_bound_.HostVector();
@@ -103,9 +86,9 @@ class VerticalFederatedLearnerTest : public ::testing::TestWithParam<std::string
auto model = MakeModel(tree_method, device, objective, dmat);
auto score = GetBaseScore(model);
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyObjective, kRows, kCols,
score, model, tree_method, device, objective);
collective::TestFederatedGlobal(kWorldSize, [&]() {
VerifyObjective(kRows, kCols, score, model, tree_method, device, objective);
});
}
};

View File

@@ -1,243 +0,0 @@
/*!
* Copyright 2023 XGBoost contributors
*/
#include <gtest/gtest.h>
#include "../metric/test_auc.h"
#include "../metric/test_elementwise_metric.h"
#include "../metric/test_multiclass_metric.h"
#include "../metric/test_rank_metric.h"
#include "../metric/test_survival_metric.h"
#include "helpers.h"
namespace {
class FederatedMetricTest : public xgboost::BaseFederatedTest {};
} // anonymous namespace
namespace xgboost {
namespace metric {
TEST_F(FederatedMetricTest, BinaryAUCRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyBinaryAUC,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, BinaryAUCColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyBinaryAUC,
DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, MultiClassAUCRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMultiClassAUC,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, MultiClassAUCColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMultiClassAUC,
DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, RankingAUCRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyRankingAUC,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, RankingAUCColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyRankingAUC,
DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, PRAUCRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyPRAUC, DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, PRAUCColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyPRAUC, DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, MultiClassPRAUCRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMultiClassPRAUC,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, MultiClassPRAUCColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMultiClassPRAUC,
DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, RankingPRAUCRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyRankingPRAUC,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, RankingPRAUCColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyRankingPRAUC,
DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, RMSERowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyRMSE, DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, RMSEColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyRMSE, DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, RMSLERowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyRMSLE, DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, RMSLEColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyRMSLE, DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, MAERowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMAE, DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, MAEColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMAE, DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, MAPERowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMAPE, DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, MAPEColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMAPE, DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, MPHERowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMPHE, DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, MPHEColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMPHE, DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, LogLossRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyLogLoss, DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, LogLossColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyLogLoss, DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, ErrorRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyError, DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, ErrorColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyError, DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, PoissonNegLogLikRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyPoissonNegLogLik,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, PoissonNegLogLikColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyPoissonNegLogLik,
DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, MultiRMSERowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMultiRMSE,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, MultiRMSEColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMultiRMSE,
DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, QuantileRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyQuantile,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, QuantileColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyQuantile,
DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, MultiClassErrorRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMultiClassError,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, MultiClassErrorColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMultiClassError,
DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, MultiClassLogLossRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMultiClassLogLoss,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, MultiClassLogLossColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMultiClassLogLoss,
DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, PrecisionRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyPrecision,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, PrecisionColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyPrecision,
DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, NDCGRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyNDCG, DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, NDCGColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyNDCG, DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, MAPRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMAP, DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, MAPColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyMAP, DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, NDCGExpGainRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyNDCGExpGain,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, NDCGExpGainColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyNDCGExpGain,
DataSplitMode::kCol);
}
} // namespace metric
} // namespace xgboost
namespace xgboost {
namespace common {
TEST_F(FederatedMetricTest, AFTNegLogLikRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyAFTNegLogLik,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, AFTNegLogLikColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyAFTNegLogLik,
DataSplitMode::kCol);
}
TEST_F(FederatedMetricTest, IntervalRegressionAccuracyRowSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyIntervalRegressionAccuracy,
DataSplitMode::kRow);
}
TEST_F(FederatedMetricTest, IntervalRegressionAccuracyColumnSplit) {
RunWithFederatedCommunicator(kWorldSize, server_->Address(), &VerifyIntervalRegressionAccuracy,
DataSplitMode::kCol);
}
} // namespace common
} // namespace xgboost

View File

@@ -1,133 +0,0 @@
/*!
* Copyright 2017-2020 XGBoost contributors
*/
#include <gtest/gtest.h>
#include <iostream>
#include <thread>
#include "federated_client.h"
#include "helpers.h"
namespace xgboost {
class FederatedServerTest : public BaseFederatedTest {
public:
static void VerifyAllgather(int rank, const std::string& server_address) {
federated::FederatedClient client{server_address, rank};
CheckAllgather(client, rank);
}
static void VerifyAllgatherV(int rank, const std::string& server_address) {
federated::FederatedClient client{server_address, rank};
CheckAllgatherV(client, rank);
}
static void VerifyAllreduce(int rank, const std::string& server_address) {
federated::FederatedClient client{server_address, rank};
CheckAllreduce(client);
}
static void VerifyBroadcast(int rank, const std::string& server_address) {
federated::FederatedClient client{server_address, rank};
CheckBroadcast(client, rank);
}
static void VerifyMixture(int rank, const std::string& server_address) {
federated::FederatedClient client{server_address, rank};
for (auto i = 0; i < 10; i++) {
CheckAllgather(client, rank);
CheckAllreduce(client);
CheckBroadcast(client, rank);
}
}
protected:
static void CheckAllgather(federated::FederatedClient& client, int rank) {
int data[] = {rank};
std::string send_buffer(reinterpret_cast<char const*>(data), sizeof(data));
auto reply = client.Allgather(send_buffer);
auto const* result = reinterpret_cast<int const*>(reply.data());
for (auto i = 0; i < kWorldSize; i++) {
EXPECT_EQ(result[i], i);
}
}
static void CheckAllgatherV(federated::FederatedClient& client, int rank) {
std::vector<std::string_view> inputs{"Hello,", " World!"};
auto reply = client.AllgatherV(inputs[rank]);
EXPECT_EQ(reply, "Hello, World!");
}
static void CheckAllreduce(federated::FederatedClient& client) {
int data[] = {1, 2, 3, 4, 5};
std::string send_buffer(reinterpret_cast<char const*>(data), sizeof(data));
auto reply = client.Allreduce(send_buffer, federated::INT32, federated::SUM);
auto const* result = reinterpret_cast<int const*>(reply.data());
int expected[] = {2, 4, 6, 8, 10};
for (auto i = 0; i < 5; i++) {
EXPECT_EQ(result[i], expected[i]);
}
}
static void CheckBroadcast(federated::FederatedClient& client, int rank) {
std::string send_buffer{};
if (rank == 0) {
send_buffer = "hello broadcast";
}
auto reply = client.Broadcast(send_buffer, 0);
EXPECT_EQ(reply, "hello broadcast") << "rank " << rank;
}
};
TEST_F(FederatedServerTest, Allgather) {
std::vector<std::thread> threads;
for (auto rank = 0; rank < kWorldSize; rank++) {
threads.emplace_back(&FederatedServerTest::VerifyAllgather, rank, server_->Address());
}
for (auto& thread : threads) {
thread.join();
}
}
TEST_F(FederatedServerTest, AllgatherV) {
std::vector<std::thread> threads;
for (auto rank = 0; rank < kWorldSize; rank++) {
threads.emplace_back(&FederatedServerTest::VerifyAllgatherV, rank, server_->Address());
}
for (auto& thread : threads) {
thread.join();
}
}
TEST_F(FederatedServerTest, Allreduce) {
std::vector<std::thread> threads;
for (auto rank = 0; rank < kWorldSize; rank++) {
threads.emplace_back(&FederatedServerTest::VerifyAllreduce, rank, server_->Address());
}
for (auto& thread : threads) {
thread.join();
}
}
TEST_F(FederatedServerTest, Broadcast) {
std::vector<std::thread> threads;
for (auto rank = 0; rank < kWorldSize; rank++) {
threads.emplace_back(&FederatedServerTest::VerifyBroadcast, rank, server_->Address());
}
for (auto& thread : threads) {
thread.join();
}
}
TEST_F(FederatedServerTest, Mixture) {
std::vector<std::thread> threads;
for (auto rank = 0; rank < kWorldSize; rank++) {
threads.emplace_back(&FederatedServerTest::VerifyMixture, rank, server_->Address());
}
for (auto& thread : threads) {
thread.join();
}
}
} // namespace xgboost

View File

@@ -12,6 +12,7 @@
#include "../../../src/data/proxy_dmatrix.h"
#include "../../../src/gbm/gbtree.h"
#include "../../../src/gbm/gbtree_model.h"
#include "../collective/test_worker.h" // for TestDistributedGlobal
#include "../filesystem.h" // dmlc::TemporaryDirectory
#include "../helpers.h"
#include "test_predictor.h"
@@ -43,7 +44,7 @@ void TestColumnSplit() {
TEST(CpuPredictor, BasicColumnSplit) {
auto constexpr kWorldSize = 2;
RunWithInMemoryCommunicator(kWorldSize, TestColumnSplit);
collective::TestDistributedGlobal(kWorldSize, TestColumnSplit);
}
TEST(CpuPredictor, IterationRange) {
@@ -157,7 +158,7 @@ TEST(CPUPredictor, CategoricalPrediction) {
TEST(CPUPredictor, CategoricalPredictionColumnSplit) {
auto constexpr kWorldSize = 2;
RunWithInMemoryCommunicator(kWorldSize, TestCategoricalPrediction, false, true);
collective::TestDistributedGlobal(kWorldSize, [] { TestCategoricalPrediction(false, true); });
}
TEST(CPUPredictor, CategoricalPredictLeaf) {
@@ -168,7 +169,7 @@ TEST(CPUPredictor, CategoricalPredictLeaf) {
TEST(CPUPredictor, CategoricalPredictLeafColumnSplit) {
auto constexpr kWorldSize = 2;
Context ctx;
RunWithInMemoryCommunicator(kWorldSize, TestCategoricalPredictLeaf, &ctx, true);
collective::TestDistributedGlobal(kWorldSize, [&] { TestCategoricalPredictLeaf(&ctx, true); });
}
TEST(CpuPredictor, UpdatePredictionCache) {
@@ -183,7 +184,8 @@ TEST(CpuPredictor, LesserFeatures) {
TEST(CpuPredictor, LesserFeaturesColumnSplit) {
auto constexpr kWorldSize = 2;
RunWithInMemoryCommunicator(kWorldSize, TestPredictionWithLesserFeaturesColumnSplit, false);
collective::TestDistributedGlobal(kWorldSize,
[] { TestPredictionWithLesserFeaturesColumnSplit(false); });
}
TEST(CpuPredictor, Sparse) {

View File

@@ -12,6 +12,7 @@
#include "../../../src/data/device_adapter.cuh"
#include "../../../src/data/proxy_dmatrix.h"
#include "../../../src/gbm/gbtree_model.h"
#include "../collective/test_worker.h" // for TestDistributedGlobal, BaseMGPUTest
#include "../helpers.h"
#include "test_predictor.h"
@@ -85,7 +86,7 @@ void VerifyBasicColumnSplit(std::array<std::vector<float>, 32> const& expected_r
}
} // anonymous namespace
class MGPUPredictorTest : public BaseMGPUTest {};
class MGPUPredictorTest : public collective::BaseMGPUTest {};
TEST_F(MGPUPredictorTest, BasicColumnSplit) {
auto ctx = MakeCUDACtx(0);
@@ -111,7 +112,8 @@ TEST_F(MGPUPredictorTest, BasicColumnSplit) {
result[i - 1] = out_predictions_h;
}
DoTest(VerifyBasicColumnSplit, result);
this->DoTest([&] { VerifyBasicColumnSplit(result); }, true);
this->DoTest([&] { VerifyBasicColumnSplit(result); }, false);
}
TEST(GPUPredictor, EllpackBasic) {
@@ -209,7 +211,8 @@ TEST(GpuPredictor, LesserFeatures) {
}
TEST_F(MGPUPredictorTest, LesserFeaturesColumnSplit) {
RunWithInMemoryCommunicator(world_size_, TestPredictionWithLesserFeaturesColumnSplit, true);
this->DoTest([] { TestPredictionWithLesserFeaturesColumnSplit(true); }, true);
this->DoTest([] { TestPredictionWithLesserFeaturesColumnSplit(true); }, false);
}
// Very basic test of empty model
@@ -277,7 +280,7 @@ TEST(GPUPredictor, IterationRange) {
}
TEST_F(MGPUPredictorTest, IterationRangeColumnSplit) {
TestIterationRangeColumnSplit(world_size_, true);
TestIterationRangeColumnSplit(common::AllVisibleGPUs(), true);
}
TEST(GPUPredictor, CategoricalPrediction) {
@@ -285,7 +288,8 @@ TEST(GPUPredictor, CategoricalPrediction) {
}
TEST_F(MGPUPredictorTest, CategoricalPredictionColumnSplit) {
RunWithInMemoryCommunicator(world_size_, TestCategoricalPrediction, true, true);
this->DoTest([] { TestCategoricalPrediction(true, true); }, true);
this->DoTest([] { TestCategoricalPrediction(true, true); }, false);
}
TEST(GPUPredictor, CategoricalPredictLeaf) {
@@ -294,8 +298,18 @@ TEST(GPUPredictor, CategoricalPredictLeaf) {
}
TEST_F(MGPUPredictorTest, CategoricalPredictionLeafColumnSplit) {
auto ctx = MakeCUDACtx(common::AllVisibleGPUs() == 1 ? 0 : collective::GetRank());
RunWithInMemoryCommunicator(world_size_, TestCategoricalPredictLeaf, &ctx, true);
this->DoTest(
[&] {
auto ctx = MakeCUDACtx(collective::GetRank());
TestCategoricalPredictLeaf(&ctx, true);
},
true);
this->DoTest(
[&] {
auto ctx = MakeCUDACtx(collective::GetRank());
TestCategoricalPredictLeaf(&ctx, true);
},
false);
}
TEST(GPUPredictor, PredictLeafBasic) {
@@ -325,7 +339,7 @@ TEST(GPUPredictor, Sparse) {
}
TEST_F(MGPUPredictorTest, SparseColumnSplit) {
TestSparsePredictionColumnSplit(world_size_, true, 0.2);
TestSparsePredictionColumnSplit(world_size_, true, 0.8);
TestSparsePredictionColumnSplit(common::AllVisibleGPUs(), true, 0.2);
TestSparsePredictionColumnSplit(common::AllVisibleGPUs(), true, 0.8);
}
} // namespace xgboost::predictor

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2020-2023 by XGBoost Contributors
* Copyright 2020-2024, XGBoost Contributors
*/
#include "test_predictor.h"
@@ -10,7 +10,6 @@
#include <xgboost/predictor.h> // for PredictionCacheEntry, Predictor, Predic...
#include <xgboost/string_view.h> // for StringView
#include <algorithm> // for max
#include <limits> // for numeric_limits
#include <memory> // for shared_ptr
#include <unordered_map> // for unordered_map
@@ -18,6 +17,7 @@
#include "../../../src/common/bitfield.h" // for LBitField32
#include "../../../src/data/iterative_dmatrix.h" // for IterativeDMatrix
#include "../../../src/data/proxy_dmatrix.h" // for DMatrixProxy
#include "../collective/test_worker.h" // for TestDistributedGlobal
#include "../helpers.h" // for GetDMatrixFromData, RandomDataGenerator
#include "xgboost/json.h" // for Json, Object, get, String
#include "xgboost/linalg.h" // for MakeVec, Tensor, TensorView, Vector
@@ -593,9 +593,23 @@ void TestIterationRangeColumnSplit(int world_size, bool use_gpu) {
Json sliced_model{Object{}};
sliced->SaveModel(&sliced_model);
RunWithInMemoryCommunicator(world_size, VerifyIterationRangeColumnSplit, use_gpu, ranged_model,
sliced_model, kRows, kCols, kClasses, margin_ranged, margin_sliced,
leaf_ranged, leaf_sliced);
#if !defined(XGBOOST_USE_NCCL)
if (use_gpu) {
GTEST_SKIP_("Not compiled with NCCL");
return;
}
#endif // defined(XGBOOST_USE_NCCL)
collective::TestDistributedGlobal(world_size, [&] {
VerifyIterationRangeColumnSplit(use_gpu, ranged_model, sliced_model, kRows, kCols, kClasses,
margin_ranged, margin_sliced, leaf_ranged, leaf_sliced);
});
#if defined(XGBOOST_USE_FEDERATED)
collective::TestFederatedGlobal(world_size, [&] {
VerifyIterationRangeColumnSplit(use_gpu, ranged_model, sliced_model, kRows, kCols, kClasses,
margin_ranged, margin_sliced, leaf_ranged, leaf_sliced);
});
#endif // defined(XGBOOST_USE_FEDERATED)
}
void TestSparsePrediction(Context const *ctx, float sparsity) {
@@ -701,8 +715,23 @@ void TestSparsePredictionColumnSplit(int world_size, bool use_gpu, float sparsit
learner->SetParam("device", ctx.DeviceName());
learner->Predict(Xy, false, &sparse_predt, 0, 0);
RunWithInMemoryCommunicator(world_size, VerifySparsePredictionColumnSplit, use_gpu, model,
kRows, kCols, sparsity, sparse_predt.HostVector());
#if !defined(XGBOOST_USE_NCCL)
if (use_gpu) {
GTEST_SKIP_("Not compiled with NCCL.");
return;
}
#endif // defined(XGBOOST_USE_CUDA)
collective::TestDistributedGlobal(world_size, [&] {
VerifySparsePredictionColumnSplit(use_gpu, model, kRows, kCols, sparsity,
sparse_predt.HostVector());
});
#if defined(XGBOOST_USE_FEDERATED)
collective::TestFederatedGlobal(world_size, [&] {
VerifySparsePredictionColumnSplit(use_gpu, model, kRows, kCols, sparsity,
sparse_predt.HostVector());
});
#endif // defined(XGBOOST_USE_FEDERATED)
}
void TestVectorLeafPrediction(Context const *ctx) {

View File

@@ -1,42 +0,0 @@
#define RABIT_CXXTESTDEFS_H
#if !defined(_WIN32)
#include <gtest/gtest.h>
#include <string>
#include <iostream>
#include "../../../rabit/src/allreduce_base.h"
TEST(AllreduceBase, InitTask)
{
rabit::engine::AllreduceBase base;
std::string rabit_task_id = "rabit_task_id=1";
char cmd[rabit_task_id.size()+1];
std::copy(rabit_task_id.begin(), rabit_task_id.end(), cmd);
cmd[rabit_task_id.size()] = '\0';
char* argv[] = {cmd};
base.Init(1, argv);
EXPECT_EQ(base.task_id, "1");
}
TEST(AllreduceBase, InitWithRingReduce)
{
rabit::engine::AllreduceBase base;
std::string rabit_task_id = "rabit_task_id=1";
char cmd[rabit_task_id.size()+1];
std::copy(rabit_task_id.begin(), rabit_task_id.end(), cmd);
cmd[rabit_task_id.size()] = '\0';
std::string rabit_reduce_ring_mincount = "rabit_reduce_ring_mincount=1";
char cmd2[rabit_reduce_ring_mincount.size()+1];
std::copy(rabit_reduce_ring_mincount.begin(), rabit_reduce_ring_mincount.end(), cmd2);
cmd2[rabit_reduce_ring_mincount.size()] = '\0';
char* argv[] = {cmd, cmd2};
base.Init(2, argv);
EXPECT_EQ(base.task_id, "1");
EXPECT_EQ(base.reduce_ring_mincount, 1ul);
}
#endif // !defined(_WIN32)

View File

@@ -1,6 +0,0 @@
#include <gtest/gtest.h>
#include <rabit/internal/utils.h>
TEST(Utils, Assert) {
EXPECT_THROW({rabit::utils::Assert(false, "foo");}, dmlc::Error);
}

View File

@@ -1,15 +1,14 @@
/**
* Copyright 2017-2024, XGBoost contributors
*/
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <xgboost/learner.h> // for Learner
#include <xgboost/logging.h> // for LogCheck_NE, CHECK_NE, LogCheck_EQ
#include <xgboost/objective.h> // for ObjFunction
#include <xgboost/version_config.h> // for XGBOOST_VER_MAJOR, XGBOOST_VER_MINOR
#include <gtest/gtest.h>
#include <xgboost/learner.h> // for Learner
#include <xgboost/logging.h> // for LogCheck_NE, CHECK_NE, LogCheck_EQ
#include <xgboost/objective.h> // for ObjFunction
#include <xgboost/version_config.h> // for XGBOOST_VER_MAJOR, XGBOOST_VER_MINOR
#include <algorithm> // for equal, transform
#include <cinttypes> // for int32_t, int64_t, uint32_t
#include <cstddef> // for size_t
#include <iosfwd> // for ofstream
#include <limits> // for numeric_limits
@@ -27,6 +26,7 @@
#include "../../src/common/io.h" // for LoadSequentialFile
#include "../../src/common/linalg_op.h" // for ElementWiseTransformHost, begin, end
#include "../../src/common/random.h" // for GlobalRandom
#include "./collective/test_worker.h" // for TestDistributedGlobal
#include "dmlc/io.h" // for Stream
#include "dmlc/omp.h" // for omp_get_max_threads
#include "filesystem.h" // for TemporaryDirectory
@@ -658,7 +658,7 @@ class TestColumnSplit : public ::testing::TestWithParam<std::string> {
auto const world_size = collective::GetWorldSize();
auto const rank = collective::GetRank();
auto p_fmat = MakeFmatForObjTest(objective);
auto p_fmat = MakeFmatForObjTest(objective, 10, 10);
std::shared_ptr<DMatrix> sliced{p_fmat->SliceCol(world_size, rank)};
std::unique_ptr<Learner> learner{Learner::Create({sliced})};
learner->SetParam("tree_method", "approx");
@@ -682,7 +682,7 @@ class TestColumnSplit : public ::testing::TestWithParam<std::string> {
public:
void Run(std::string objective) {
auto p_fmat = MakeFmatForObjTest(objective);
auto p_fmat = MakeFmatForObjTest(objective, 10, 10);
std::unique_ptr<Learner> learner{Learner::Create({p_fmat})};
learner->SetParam("tree_method", "approx");
learner->SetParam("objective", objective);
@@ -703,7 +703,9 @@ class TestColumnSplit : public ::testing::TestWithParam<std::string> {
auto constexpr kWorldSize{3};
auto call = [this, &objective](auto&... args) { TestBaseScore(objective, args...); };
auto score = GetBaseScore(config);
RunWithInMemoryCommunicator(kWorldSize, call, score, model);
collective::TestDistributedGlobal(kWorldSize, [&] {
call(score, model);
});
}
};
@@ -736,7 +738,7 @@ void VerifyColumnSplitWithArgs(std::string const& tree_method, bool use_gpu, Arg
Json const& expected_model) {
auto const world_size = collective::GetWorldSize();
auto const rank = collective::GetRank();
auto p_fmat = MakeFmatForObjTest("");
auto p_fmat = MakeFmatForObjTest("", 10, 10);
std::shared_ptr<DMatrix> sliced{p_fmat->SliceCol(world_size, rank)};
std::string device = "cpu";
if (use_gpu) {
@@ -747,82 +749,99 @@ void VerifyColumnSplitWithArgs(std::string const& tree_method, bool use_gpu, Arg
ASSERT_EQ(model, expected_model);
}
void TestColumnSplitWithArgs(std::string const& tree_method, bool use_gpu, Args const& args) {
auto p_fmat = MakeFmatForObjTest("");
void TestColumnSplitWithArgs(std::string const& tree_method, bool use_gpu, Args const& args,
bool federated) {
auto p_fmat = MakeFmatForObjTest("", 10, 10);
std::string device = use_gpu ? "cuda:0" : "cpu";
auto model = GetModelWithArgs(p_fmat, tree_method, device, args);
auto world_size{3};
if (use_gpu) {
world_size = common::AllVisibleGPUs();
// Simulate MPU on a single GPU.
if (world_size == 1) {
// Simulate MPU on a single GPU. Federated doesn't use nccl, can run multiple
// instances on the same GPU.
if (world_size == 1 && federated) {
world_size = 3;
}
}
RunWithInMemoryCommunicator(world_size, VerifyColumnSplitWithArgs, tree_method, use_gpu, args,
model);
if (federated) {
#if defined(XGBOOST_USE_FEDERATED)
collective::TestFederatedGlobal(
world_size, [&] { VerifyColumnSplitWithArgs(tree_method, use_gpu, args, model); });
#else
GTEST_SKIP_("Not compiled with federated learning.");
#endif // defined(XGBOOST_USE_FEDERATED)
} else {
#if !defined(XGBOOST_USE_NCCL)
if (use_gpu) {
GTEST_SKIP_("Not compiled with NCCL.");
return;
}
#endif // defined(XGBOOST_USE_NCCL)
collective::TestDistributedGlobal(
world_size, [&] { VerifyColumnSplitWithArgs(tree_method, use_gpu, args, model); });
}
}
void TestColumnSplitColumnSampler(std::string const& tree_method, bool use_gpu) {
Args args{{"colsample_bytree", "0.5"}, {"colsample_bylevel", "0.6"}, {"colsample_bynode", "0.7"}};
TestColumnSplitWithArgs(tree_method, use_gpu, args);
}
class ColumnSplitTrainingTest
: public ::testing::TestWithParam<std::tuple<std::string, bool, bool>> {
public:
static void TestColumnSplitColumnSampler(std::string const& tree_method, bool use_gpu,
bool federated) {
Args args{
{"colsample_bytree", "0.5"}, {"colsample_bylevel", "0.6"}, {"colsample_bynode", "0.7"}};
TestColumnSplitWithArgs(tree_method, use_gpu, args, federated);
}
static void TestColumnSplitInteractionConstraints(std::string const& tree_method, bool use_gpu,
bool federated) {
Args args{{"interaction_constraints", "[[0, 5, 7], [2, 8, 9], [1, 3, 6]]"}};
TestColumnSplitWithArgs(tree_method, use_gpu, args, federated);
}
static void TestColumnSplitMonotoneConstraints(std::string const& tree_method, bool use_gpu,
bool federated) {
Args args{{"monotone_constraints", "(1,-1,0,1,1,-1,-1,0,0,1)"}};
TestColumnSplitWithArgs(tree_method, use_gpu, args, federated);
}
};
void TestColumnSplitInteractionConstraints(std::string const& tree_method, bool use_gpu) {
Args args{{"interaction_constraints", "[[0, 5, 7], [2, 8, 9], [1, 3, 6]]"}};
TestColumnSplitWithArgs(tree_method, use_gpu, args);
}
void TestColumnSplitMonotoneConstraints(std::string const& tree_method, bool use_gpu) {
Args args{{"monotone_constraints", "(1,-1,0,1,1,-1,-1,0,0,1)"}};
TestColumnSplitWithArgs(tree_method, use_gpu, args);
auto MakeParamsForTest() {
std::vector<std::tuple<std::string, bool, bool>> configs;
for (auto tm : {"hist", "approx"}) {
#if defined(XGBOOST_USE_CUDA)
std::array<bool, 2> use_gpu{true, false};
#else
std::array<bool, 1> use_gpu{false};
#endif
for (auto i : use_gpu) {
#if defined(XGBOOST_USE_FEDERATED)
std::array<bool, 2> fed{true, false};
#else
std::array<bool, 1> fed{false};
#endif
for (auto j : fed) {
configs.emplace_back(tm, i, j);
}
}
}
return configs;
}
} // anonymous namespace
TEST(ColumnSplitColumnSampler, Approx) { TestColumnSplitColumnSampler("approx", false); }
TEST(ColumnSplitColumnSampler, Hist) { TestColumnSplitColumnSampler("hist", false); }
#if defined(XGBOOST_USE_CUDA)
TEST(MGPUColumnSplitColumnSampler, GPUApprox) { TestColumnSplitColumnSampler("approx", true); }
TEST(MGPUColumnSplitColumnSampler, GPUHist) { TestColumnSplitColumnSampler("hist", true); }
#endif // defined(XGBOOST_USE_CUDA)
TEST(ColumnSplitInteractionConstraints, Approx) {
TestColumnSplitInteractionConstraints("approx", false);
TEST_P(ColumnSplitTrainingTest, ColumnSampler) {
auto param = GetParam();
std::apply(TestColumnSplitColumnSampler, param);
}
TEST(ColumnSplitInteractionConstraints, Hist) {
TestColumnSplitInteractionConstraints("hist", false);
TEST_P(ColumnSplitTrainingTest, InteractionConstraints) {
auto param = GetParam();
std::apply(TestColumnSplitInteractionConstraints, param);
}
#if defined(XGBOOST_USE_CUDA)
TEST(MGPUColumnSplitInteractionConstraints, GPUApprox) {
TestColumnSplitInteractionConstraints("approx", true);
TEST_P(ColumnSplitTrainingTest, MonotoneConstraints) {
auto param = GetParam();
std::apply(TestColumnSplitMonotoneConstraints, param);
}
TEST(MGPUColumnSplitInteractionConstraints, GPUHist) {
TestColumnSplitInteractionConstraints("hist", true);
}
#endif // defined(XGBOOST_USE_CUDA)
TEST(ColumnSplitMonotoneConstraints, Approx) {
TestColumnSplitMonotoneConstraints("approx", false);
}
TEST(ColumnSplitMonotoneConstraints, Hist) {
TestColumnSplitMonotoneConstraints("hist", false);
}
#if defined(XGBOOST_USE_CUDA)
TEST(MGPUColumnSplitMonotoneConstraints, GPUApprox) {
TestColumnSplitMonotoneConstraints("approx", true);
}
TEST(MGPUColumnSplitMonotoneConstraints, GPUHist) {
TestColumnSplitMonotoneConstraints("hist", true);
}
#endif // defined(XGBOOST_USE_CUDA)
INSTANTIATE_TEST_SUITE_P(ColumnSplit, ColumnSplitTrainingTest,
::testing::ValuesIn(MakeParamsForTest()));
} // namespace xgboost

View File

@@ -1,15 +1,16 @@
// Copyright by Contributors
/**
* Copyright 2016-2024, XGBoost Contributors
*/
#include <gtest/gtest.h>
#include <xgboost/base.h>
#include <xgboost/logging.h>
#include <string>
#include <memory>
#include <vector>
#include "helpers.h"
int main(int argc, char ** argv) {
xgboost::Args args {{"verbosity", "2"}};
int main(int argc, char** argv) {
xgboost::Args args{{"verbosity", "2"}};
xgboost::ConsoleLogger::Configure(args);
testing::InitGoogleTest(&argc, argv);

View File

@@ -1,12 +1,12 @@
/**
* Copyright 2020-2023, XGBoost contributors
* Copyright 2020-2024, XGBoost contributors
*/
#include <gtest/gtest.h>
#include <thrust/host_vector.h>
#include "../../../../src/tree/gpu_hist/evaluate_splits.cuh"
#include "../../collective/test_worker.h" // for BaseMGPUTest
#include "../../helpers.h"
#include "../../histogram_helpers.h"
#include "../test_evaluate_splits.h" // TestPartitionBasedSplit
namespace xgboost::tree {
@@ -17,13 +17,13 @@ auto ZeroParam() {
tparam.UpdateAllowUnknown(args);
return tparam;
}
} // anonymous namespace
inline GradientQuantiser DummyRoundingFactor(Context const* ctx) {
GradientQuantiser DummyRoundingFactor(Context const* ctx) {
thrust::device_vector<GradientPair> gpair(1);
gpair[0] = {1000.f, 1000.f}; // Tests should not exceed sum of 1000
return {ctx, dh::ToSpan(gpair), MetaInfo()};
}
} // anonymous namespace
thrust::device_vector<GradientPairInt64> ConvertToInteger(Context const* ctx,
std::vector<GradientPairPrecise> x) {
@@ -546,7 +546,7 @@ TEST_F(TestPartitionBasedSplit, GpuHist) {
ASSERT_NEAR(split.loss_chg, best_score_, 1e-2);
}
class MGPUHistTest : public BaseMGPUTest {};
class MGPUHistTest : public collective::BaseMGPUTest {};
namespace {
void VerifyColumnSplitEvaluateSingleSplit(bool is_categorical) {
@@ -589,21 +589,29 @@ void VerifyColumnSplitEvaluateSingleSplit(bool is_categorical) {
evaluator.Reset(cuts, dh::ToSpan(feature_types), feature_set.size(), tparam, true, ctx.Device());
DeviceSplitCandidate result = evaluator.EvaluateSingleSplit(&ctx, input, shared_inputs).split;
EXPECT_EQ(result.findex, 1) << "rank: " << rank;
EXPECT_EQ(result.findex, 1);
if (is_categorical) {
ASSERT_TRUE(std::isnan(result.fvalue));
} else {
EXPECT_EQ(result.fvalue, 11.0) << "rank: " << rank;
EXPECT_EQ(result.fvalue, 11.0);
}
EXPECT_EQ(result.left_sum + result.right_sum, parent_sum) << "rank: " << rank;
EXPECT_EQ(result.left_sum + result.right_sum, parent_sum);
}
} // anonymous namespace
TEST_F(MGPUHistTest, ColumnSplitEvaluateSingleSplit) {
DoTest(VerifyColumnSplitEvaluateSingleSplit, false);
if (common::AllVisibleGPUs() > 1) {
// We can't emulate multiple GPUs with NCCL.
this->DoTest([] { VerifyColumnSplitEvaluateSingleSplit(false); }, false, true);
}
this->DoTest([] { VerifyColumnSplitEvaluateSingleSplit(false); }, true, true);
}
TEST_F(MGPUHistTest, ColumnSplitEvaluateSingleCategoricalSplit) {
DoTest(VerifyColumnSplitEvaluateSingleSplit, true);
if (common::AllVisibleGPUs() > 1) {
// We can't emulate multiple GPUs with NCCL.
this->DoTest([] { VerifyColumnSplitEvaluateSingleSplit(true); }, false, true);
}
this->DoTest([] { VerifyColumnSplitEvaluateSingleSplit(true); }, true, true);
}
} // namespace xgboost::tree

View File

@@ -33,6 +33,7 @@
#include "../../../../src/tree/hist/histogram.h" // for HistogramBuilder
#include "../../../../src/tree/hist/param.h" // for HistMakerTrainParam
#include "../../categorical_helpers.h" // for OneHotEncodeFeature
#include "../../collective/test_worker.h" // for TestDistributedGlobal
#include "../../helpers.h" // for RandomDataGenerator, GenerateRa...
namespace xgboost::tree {
@@ -300,8 +301,8 @@ TEST(CPUHistogram, BuildHist) {
TEST(CPUHistogram, BuildHistColSplit) {
auto constexpr kWorkers = 4;
RunWithInMemoryCommunicator(kWorkers, TestBuildHistogram, true, true, true);
RunWithInMemoryCommunicator(kWorkers, TestBuildHistogram, true, false, true);
collective::TestDistributedGlobal(kWorkers, [] { TestBuildHistogram(true, true, true); });
collective::TestDistributedGlobal(kWorkers, [] { TestBuildHistogram(true, false, true); });
}
namespace {

View File

@@ -1,15 +1,15 @@
/**
* Copyright 2021-2023 by XGBoost contributors.
* Copyright 2021-2024, XGBoost contributors.
*/
#include <gtest/gtest.h>
#include "../../../src/common/numeric.h"
#include "../../../src/tree/common_row_partitioner.h"
#include "../collective/test_worker.h" // for TestDistributedGlobal
#include "../helpers.h"
#include "test_partitioner.h"
namespace xgboost {
namespace tree {
namespace xgboost::tree {
namespace {
std::vector<float> GenerateHess(size_t n_samples) {
auto grad = GenerateRandomGradients(n_samples);
@@ -145,8 +145,9 @@ TEST(Approx, PartitionerColSplit) {
}
auto constexpr kWorkers = 4;
RunWithInMemoryCommunicator(kWorkers, TestColumnSplitPartitioner, n_samples, base_rowid, Xy,
&hess, min_value, mid_value, mid_partitioner);
collective::TestDistributedGlobal(kWorkers, [&] {
TestColumnSplitPartitioner(n_samples, base_rowid, Xy, &hess, min_value, mid_value,
mid_partitioner);
});
}
} // namespace tree
} // namespace xgboost
} // namespace xgboost::tree

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2022-2023 by XGBoost Contributors
* Copyright 2022-2024, XGBoost Contributors
*/
#include <gtest/gtest.h>
#include <xgboost/base.h> // for GradientPairInternal, GradientPairPrecise
@@ -14,7 +14,6 @@
#include <limits> // for numeric_limits
#include <numeric> // for iota
#include <tuple> // for make_tuple, tie, tuple
#include <utility> // for pair
#include <vector> // for vector
#include "../../../src/common/hist_util.h" // for HistogramCuts, HistCollection, GHistRow
@@ -23,7 +22,6 @@
#include "../../../src/tree/param.h" // for TrainParam, GradStats
#include "../../../src/tree/split_evaluator.h" // for TreeEvaluator
#include "../helpers.h" // for SimpleLCG, SimpleRealUniformDistribution
#include "gtest/gtest_pred_impl.h" // for AssertionResult, ASSERT_EQ, ASSERT_TRUE
namespace xgboost::tree {
/**
@@ -96,13 +94,11 @@ class TestPartitionBasedSplit : public ::testing::Test {
// enumerate all possible partitions to find the optimal split
do {
int32_t thresh;
float score;
std::vector<GradientPairPrecise> sorted_hist(node_hist.size());
for (size_t i = 0; i < sorted_hist.size(); ++i) {
sorted_hist[i] = node_hist[sorted_idx_[i]];
}
std::tie(thresh, score) = enumerate({sorted_hist}, total_gpair_);
auto [thresh, score] = enumerate({sorted_hist}, total_gpair_);
if (score > best_score_) {
best_score_ = score;
}

View File

@@ -1,11 +1,12 @@
/**
* Copyright 2022-2023, XGBoost Contributors
* Copyright 2022-2024, XGBoost Contributors
*/
#include <gtest/gtest.h>
#include <xgboost/linalg.h>
#include "../../src/common/linalg_op.h"
#include "../../src/tree/fit_stump.h"
#include "../collective/test_worker.h" // for TestDistributedGlobal
#include "../helpers.h"
namespace xgboost::tree {
@@ -43,7 +44,7 @@ TEST(InitEstimation, FitStump) {
#if defined(XGBOOST_USE_CUDA)
TEST(InitEstimation, GPUFitStump) {
Context ctx;
ctx.UpdateAllowUnknown(Args{{"gpu_id", "0"}});
ctx.UpdateAllowUnknown(Args{{"device", "cuda"}});
TestFitStump(&ctx);
}
#endif // defined(XGBOOST_USE_CUDA)
@@ -51,6 +52,6 @@ TEST(InitEstimation, GPUFitStump) {
TEST(InitEstimation, FitStumpColumnSplit) {
Context ctx;
auto constexpr kWorldSize{3};
RunWithInMemoryCommunicator(kWorldSize, &TestFitStump, &ctx, DataSplitMode::kCol);
collective::TestDistributedGlobal(kWorldSize, [&] { TestFitStump(&ctx, DataSplitMode::kCol); });
}
} // namespace xgboost::tree

View File

@@ -13,14 +13,19 @@
#include "../../../src/common/common.h"
#include "../../../src/data/ellpack_page.cuh" // for EllpackPageImpl
#include "../../../src/data/ellpack_page.h" // for EllpackPage
#include "../../../src/tree/param.h" // for TrainParam
#include "../../../src/tree/param.h" // for TrainParam
#include "../../../src/tree/updater_gpu_hist.cu"
#include "../filesystem.h" // dmlc::TemporaryDirectory
#include "../collective/test_worker.h" // for BaseMGPUTest
#include "../filesystem.h" // dmlc::TemporaryDirectory
#include "../helpers.h"
#include "../histogram_helpers.h"
#include "xgboost/context.h"
#include "xgboost/json.h"
#if defined(XGBOOST_USE_FEDERATED)
#include "../plugin/federated/test_worker.h" // for TestFederatedGlobal
#endif // defined(XGBOOST_USE_FEDERATED)
namespace xgboost::tree {
TEST(GpuHist, DeviceHistogram) {
// Ensures that node allocates correctly after reaching `kStopGrowingSize`.
@@ -458,9 +463,9 @@ void VerifyHistColumnSplit(bst_idx_t rows, bst_feature_t cols, RegTree const& ex
}
} // anonymous namespace
class MGPUHistTest : public BaseMGPUTest {};
class MGPUHistTest : public collective::BaseMGPUTest {};
TEST_F(MGPUHistTest, GPUHistColumnSplit) {
TEST_F(MGPUHistTest, HistColumnSplit) {
auto constexpr kRows = 32;
auto constexpr kCols = 16;
@@ -468,7 +473,8 @@ TEST_F(MGPUHistTest, GPUHistColumnSplit) {
auto dmat = RandomDataGenerator{kRows, kCols, 0}.GenerateDMatrix(true);
RegTree expected_tree = GetHistTree(&ctx, dmat.get());
DoTest(VerifyHistColumnSplit, kRows, kCols, expected_tree);
this->DoTest([&] { VerifyHistColumnSplit(kRows, kCols, expected_tree); }, true);
this->DoTest([&] { VerifyHistColumnSplit(kRows, kCols, expected_tree); }, false);
}
namespace {
@@ -508,7 +514,7 @@ void VerifyApproxColumnSplit(bst_idx_t rows, bst_feature_t cols, RegTree const&
}
} // anonymous namespace
class MGPUApproxTest : public BaseMGPUTest {};
class MGPUApproxTest : public collective::BaseMGPUTest {};
TEST_F(MGPUApproxTest, GPUApproxColumnSplit) {
auto constexpr kRows = 32;
@@ -518,6 +524,7 @@ TEST_F(MGPUApproxTest, GPUApproxColumnSplit) {
auto dmat = RandomDataGenerator{kRows, kCols, 0}.GenerateDMatrix(true);
RegTree expected_tree = GetApproxTree(&ctx, dmat.get());
DoTest(VerifyApproxColumnSplit, kRows, kCols, expected_tree);
this->DoTest([&] { VerifyApproxColumnSplit(kRows, kCols, expected_tree); }, true);
this->DoTest([&] { VerifyApproxColumnSplit(kRows, kCols, expected_tree); }, false);
}
} // namespace xgboost::tree

View File

@@ -5,7 +5,8 @@
#include <xgboost/tree_model.h>
#include <xgboost/tree_updater.h>
#include "../../../src/tree/param.h" // for TrainParam
#include "../../../src/tree/param.h" // for TrainParam
#include "../collective/test_worker.h" // for TestDistributedGlobal
#include "../helpers.h"
namespace xgboost::tree {
@@ -118,8 +119,8 @@ void TestColumnSplit(bool categorical) {
}
auto constexpr kWorldSize = 2;
RunWithInMemoryCommunicator(kWorldSize, VerifyColumnSplit, kRows, kCols, categorical,
std::cref(expected_tree));
collective::TestDistributedGlobal(
kWorldSize, [&] { VerifyColumnSplit(kRows, kCols, categorical, expected_tree); });
}
} // anonymous namespace

View File

@@ -11,26 +11,26 @@ namespace {
auto MakeTreeForTest() {
bst_target_t n_targets{3};
bst_feature_t n_features{4};
RegTree tree{n_targets, n_features};
CHECK(tree.IsMultiTarget());
std::unique_ptr<RegTree> tree{std::make_unique<RegTree>(n_targets, n_features)};
CHECK(tree->IsMultiTarget());
linalg::Vector<float> base_weight{{1.0f, 2.0f, 3.0f}, {3ul}, DeviceOrd::CPU()};
linalg::Vector<float> left_weight{{2.0f, 3.0f, 4.0f}, {3ul}, DeviceOrd::CPU()};
linalg::Vector<float> right_weight{{3.0f, 4.0f, 5.0f}, {3ul}, DeviceOrd::CPU()};
tree.ExpandNode(RegTree::kRoot, /*split_idx=*/1, 0.5f, true, base_weight.HostView(),
left_weight.HostView(), right_weight.HostView());
tree->ExpandNode(RegTree::kRoot, /*split_idx=*/1, 0.5f, true, base_weight.HostView(),
left_weight.HostView(), right_weight.HostView());
return tree;
}
} // namespace
TEST(MultiTargetTree, JsonIO) {
auto tree = MakeTreeForTest();
ASSERT_EQ(tree.NumNodes(), 3);
ASSERT_EQ(tree.NumTargets(), 3);
ASSERT_EQ(tree.GetMultiTargetTree()->Size(), 3);
ASSERT_EQ(tree.Size(), 3);
ASSERT_EQ(tree->NumNodes(), 3);
ASSERT_EQ(tree->NumTargets(), 3);
ASSERT_EQ(tree->GetMultiTargetTree()->Size(), 3);
ASSERT_EQ(tree->Size(), 3);
Json jtree{Object{}};
tree.SaveModel(&jtree);
tree->SaveModel(&jtree);
auto check_jtree = [](Json jtree, RegTree const& tree) {
ASSERT_EQ(get<String const>(jtree["tree_param"]["num_nodes"]), std::to_string(tree.NumNodes()));
@@ -40,7 +40,7 @@ TEST(MultiTargetTree, JsonIO) {
ASSERT_EQ(get<I32Array const>(jtree["left_children"]).size(), tree.NumNodes());
ASSERT_EQ(get<I32Array const>(jtree["right_children"]).size(), tree.NumNodes());
};
check_jtree(jtree, tree);
check_jtree(jtree, *tree);
RegTree loaded;
loaded.LoadModel(jtree);
@@ -49,18 +49,18 @@ TEST(MultiTargetTree, JsonIO) {
Json jtree1{Object{}};
loaded.SaveModel(&jtree1);
check_jtree(jtree1, tree);
check_jtree(jtree1, *tree);
}
TEST(MultiTargetTree, DumpDot) {
auto tree = MakeTreeForTest();
auto n_features = tree.NumFeatures();
auto n_features = tree->NumFeatures();
FeatureMap fmap;
for (bst_feature_t f = 0; f < n_features; ++f) {
auto name = "feat_" + std::to_string(f);
fmap.PushBack(f, name.c_str(), "q");
}
auto str = tree.DumpModel(fmap, true, "dot");
auto str = tree->DumpModel(fmap, true, "dot");
ASSERT_NE(str.find("leaf=[2, 3, 4]"), std::string::npos);
ASSERT_NE(str.find("leaf=[3, 4, 5]"), std::string::npos);

View File

@@ -13,6 +13,7 @@
#include "../../../src/tree/common_row_partitioner.h"
#include "../../../src/tree/hist/expand_entry.h" // for MultiExpandEntry, CPUExpandEntry
#include "../../../src/tree/param.h"
#include "../collective/test_worker.h" // for TestDistributedGlobal
#include "../helpers.h"
#include "test_partitioner.h"
#include "xgboost/data.h"
@@ -190,9 +191,10 @@ void TestColumnSplitPartitioner(bst_target_t n_targets) {
}
auto constexpr kWorkers = 4;
RunWithInMemoryCommunicator(kWorkers, VerifyColumnSplitPartitioner<ExpandEntry>, n_targets,
n_samples, n_features, base_rowid, Xy, min_value, mid_value,
mid_partitioner);
collective::TestDistributedGlobal(kWorkers, [&] {
VerifyColumnSplitPartitioner<ExpandEntry>(n_targets, n_samples, n_features, base_rowid, Xy,
min_value, mid_value, mid_partitioner);
});
}
} // anonymous namespace
@@ -245,8 +247,9 @@ void TestColumnSplit(bst_target_t n_targets) {
}
auto constexpr kWorldSize = 2;
RunWithInMemoryCommunicator(kWorldSize, VerifyColumnSplit, &ctx, kRows, kCols, n_targets,
std::cref(expected_tree));
collective::TestDistributedGlobal(kWorldSize, [&] {
VerifyColumnSplit(&ctx, kRows, kCols, n_targets, std::cref(expected_tree));
});
}
} // anonymous namespace