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,6 +1,6 @@
FROM ubuntu:18.04
ARG JDK_VERSION=8
ARG SPARK_VERSION=3.0.0
ARG SPARK_VERSION=3.4.0
# Environment
ENV DEBIAN_FRONTEND noninteractive

View File

@@ -18,7 +18,6 @@ fi
rm -rf build/
cd jvm-packages
export RABIT_MOCK=ON
if [ "x$gpu_arch" != "x" ]; then
export GPU_ARCH_FLAG=$gpu_arch

View File

@@ -1,10 +0,0 @@
#!/usr/bin/env bash
set -e
rm -rf build
mkdir build
cd build
cmake -DRABIT_MOCK=ON -DCMAKE_VERBOSE_MAKEFILE=ON ..
make clean
make -j$(nproc)
cd ..

View File

@@ -53,7 +53,6 @@ def pack_rpackage() -> Path:
# rabit
rabit = Path("rabit")
os.mkdir(dest / "src" / rabit)
shutil.copytree(rabit / "src", dest / "src" / "rabit" / "src")
shutil.copytree(rabit / "include", dest / "src" / "rabit" / "include")
# dmlc-core
dmlc_core = Path("dmlc-core")

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

View File

@@ -1,16 +1,14 @@
import multiprocessing
import socket
import sys
import time
from threading import Thread
import numpy as np
import pytest
import xgboost as xgb
from xgboost import RabitTracker, build_info, federated
if sys.platform.startswith("win"):
pytest.skip("Skipping collective tests on Windows", allow_module_level=True)
from xgboost import testing as tm
def run_rabit_worker(rabit_env, world_size):
@@ -18,20 +16,21 @@ def run_rabit_worker(rabit_env, world_size):
assert xgb.collective.get_world_size() == world_size
assert xgb.collective.is_distributed()
assert xgb.collective.get_processor_name() == socket.gethostname()
ret = xgb.collective.broadcast('test1234', 0)
assert str(ret) == 'test1234'
ret = xgb.collective.broadcast("test1234", 0)
assert str(ret) == "test1234"
ret = xgb.collective.allreduce(np.asarray([1, 2, 3]), xgb.collective.Op.SUM)
assert np.array_equal(ret, np.asarray([2, 4, 6]))
def test_rabit_communicator():
def test_rabit_communicator() -> None:
world_size = 2
tracker = RabitTracker(host_ip='127.0.0.1', n_workers=world_size)
tracker.start(world_size)
tracker = RabitTracker(host_ip="127.0.0.1", n_workers=world_size)
tracker.start()
workers = []
for _ in range(world_size):
worker = multiprocessing.Process(target=run_rabit_worker,
args=(tracker.worker_envs(), world_size))
worker = multiprocessing.Process(
target=run_rabit_worker, args=(tracker.worker_args(), world_size)
)
workers.append(worker)
worker.start()
for worker in workers:
@@ -39,39 +38,44 @@ def test_rabit_communicator():
assert worker.exitcode == 0
def run_federated_worker(port, world_size, rank):
with xgb.collective.CommunicatorContext(xgboost_communicator='federated',
federated_server_address=f'localhost:{port}',
federated_world_size=world_size,
federated_rank=rank):
def run_federated_worker(port: int, world_size: int, rank: int) -> None:
with xgb.collective.CommunicatorContext(
dmlc_communicator="federated",
federated_server_address=f"localhost:{port}",
federated_world_size=world_size,
federated_rank=rank,
):
assert xgb.collective.get_world_size() == world_size
assert xgb.collective.is_distributed()
assert xgb.collective.get_processor_name() == f'rank{rank}'
ret = xgb.collective.broadcast('test1234', 0)
assert str(ret) == 'test1234'
ret = xgb.collective.allreduce(np.asarray([1, 2, 3]), xgb.collective.Op.SUM)
assert np.array_equal(ret, np.asarray([2, 4, 6]))
assert xgb.collective.get_processor_name() == f"rank:{rank}"
bret = xgb.collective.broadcast("test1234", 0)
assert str(bret) == "test1234"
aret = xgb.collective.allreduce(np.asarray([1, 2, 3]), xgb.collective.Op.SUM)
assert np.array_equal(aret, np.asarray([2, 4, 6]))
@pytest.mark.skipif(**tm.skip_win())
def test_federated_communicator():
if not build_info()["USE_FEDERATED"]:
pytest.skip("XGBoost not built with federated learning enabled")
port = 9091
world_size = 2
server = multiprocessing.Process(target=xgb.federated.run_federated_server, args=(port, world_size))
server.start()
time.sleep(1)
if not server.is_alive():
tracker = multiprocessing.Process(
target=federated.run_federated_server,
kwargs={"port": port, "n_workers": world_size},
)
tracker.start()
if not tracker.is_alive():
raise Exception("Error starting Federated Learning server")
workers = []
for rank in range(world_size):
worker = multiprocessing.Process(target=run_federated_worker,
args=(port, world_size, rank))
worker = multiprocessing.Process(
target=run_federated_worker, args=(port, world_size, rank)
)
workers.append(worker)
worker.start()
for worker in workers:
worker.join()
assert worker.exitcode == 0
server.terminate()

View File

@@ -3,33 +3,33 @@ import sys
import numpy as np
import pytest
from hypothesis import HealthCheck, given, settings, strategies
import xgboost as xgb
from xgboost import RabitTracker, collective
from xgboost import testing as tm
if sys.platform.startswith("win"):
pytest.skip("Skipping dask tests on Windows", allow_module_level=True)
def test_rabit_tracker():
tracker = RabitTracker(host_ip="127.0.0.1", n_workers=1)
tracker.start(1)
with xgb.collective.CommunicatorContext(**tracker.worker_envs()):
tracker.start()
with xgb.collective.CommunicatorContext(**tracker.worker_args()):
ret = xgb.collective.broadcast("test1234", 0)
assert str(ret) == "test1234"
@pytest.mark.skipif(**tm.not_linux())
def test_socket_error():
tracker = RabitTracker(host_ip="127.0.0.1", n_workers=1)
tracker.start(1)
env = tracker.worker_envs()
env["DMLC_TRACKER_PORT"] = 0
env["DMLC_WORKER_CONNECT_RETRY"] = 1
with pytest.raises(ValueError, match="127.0.0.1:0\n.*refused"):
tracker = RabitTracker(host_ip="127.0.0.1", n_workers=2)
tracker.start()
env = tracker.worker_args()
env["dmlc_tracker_port"] = 0
env["dmlc_retry"] = 1
with pytest.raises(ValueError, match="Failed to bootstrap the communication."):
with xgb.collective.CommunicatorContext(**env):
pass
with pytest.raises(ValueError):
tracker.free()
def run_rabit_ops(client, n_workers):
@@ -70,6 +70,40 @@ def test_rabit_ops():
run_rabit_ops(client, n_workers)
def run_allreduce(client) -> None:
from xgboost.dask import CommunicatorContext, _get_dask_config, _get_rabit_args
workers = tm.get_client_workers(client)
rabit_args = client.sync(_get_rabit_args, len(workers), _get_dask_config(), client)
n_workers = len(workers)
def local_test(worker_id: int) -> None:
x = np.full(shape=(1024 * 1024 * 32), fill_value=1.0)
with CommunicatorContext(**rabit_args):
k = np.asarray([1.0])
for i in range(128):
m = collective.allreduce(k, collective.Op.SUM)
assert m == n_workers
y = collective.allreduce(x, collective.Op.SUM)
np.testing.assert_allclose(y, np.full_like(y, fill_value=float(n_workers)))
futures = client.map(local_test, range(len(workers)), workers=workers)
results = client.gather(futures)
@pytest.mark.skipif(**tm.no_dask())
def test_allreduce() -> None:
from distributed import Client, LocalCluster
n_workers = 4
for i in range(2):
with LocalCluster(n_workers=n_workers) as cluster:
with Client(cluster) as client:
for i in range(2):
run_allreduce(client)
def run_broadcast(client):
from xgboost.dask import _get_dask_config, _get_rabit_args
@@ -109,6 +143,7 @@ def test_rabit_ops_ipv6():
run_rabit_ops(client, n_workers)
@pytest.mark.skipif(**tm.no_dask())
def test_rank_assignment() -> None:
from distributed import Client, LocalCluster
@@ -133,3 +168,107 @@ def test_rank_assignment() -> None:
futures = client.map(local_test, range(len(workers)), workers=workers)
client.gather(futures)
@pytest.fixture
def local_cluster():
from distributed import LocalCluster
n_workers = 8
with LocalCluster(n_workers=n_workers, dashboard_address=":0") as cluster:
yield cluster
ops_strategy = strategies.lists(
strategies.sampled_from(["broadcast", "allreduce_max", "allreduce_sum"])
)
@pytest.mark.skipif(**tm.no_dask())
@given(ops=ops_strategy, size=strategies.integers(2**4, 2**16))
@settings(
deadline=None,
print_blob=True,
max_examples=10,
suppress_health_check=[HealthCheck.function_scoped_fixture],
)
def test_ops_restart_comm(local_cluster, ops, size) -> None:
from distributed import Client
def local_test(w: int, n_workers: int) -> None:
a = np.arange(0, n_workers)
with xgb.dask.CommunicatorContext(**args):
for op in ops:
if op == "broadcast":
b = collective.broadcast(a, root=1)
np.testing.assert_allclose(b, a)
elif op == "allreduce_max":
b = collective.allreduce(a, collective.Op.MAX)
np.testing.assert_allclose(b, a)
elif op == "allreduce_sum":
b = collective.allreduce(a, collective.Op.SUM)
np.testing.assert_allclose(a * n_workers, b)
else:
raise ValueError()
with Client(local_cluster) as client:
workers = tm.get_client_workers(client)
args = client.sync(
xgb.dask._get_rabit_args,
len(workers),
None,
client,
)
workers = tm.get_client_workers(client)
n_workers = len(workers)
futures = client.map(
local_test, range(len(workers)), workers=workers, n_workers=n_workers
)
client.gather(futures)
@pytest.mark.skipif(**tm.no_dask())
def test_ops_reuse_comm(local_cluster) -> None:
from distributed import Client
rng = np.random.default_rng(1994)
n_examples = 10
ops = rng.choice(
["broadcast", "allreduce_sum", "allreduce_max"], size=n_examples
).tolist()
def local_test(w: int, n_workers: int) -> None:
a = np.arange(0, n_workers)
with xgb.dask.CommunicatorContext(**args):
for op in ops:
if op == "broadcast":
b = collective.broadcast(a, root=1)
assert np.allclose(b, a)
elif op == "allreduce_max":
c = np.full_like(a, collective.get_rank())
b = collective.allreduce(c, collective.Op.MAX)
assert np.allclose(b, n_workers - 1), b
elif op == "allreduce_sum":
b = collective.allreduce(a, collective.Op.SUM)
assert np.allclose(a * 8, b)
else:
raise ValueError()
with Client(local_cluster) as client:
workers = tm.get_client_workers(client)
args = client.sync(
xgb.dask._get_rabit_args,
len(workers),
None,
client,
)
n_workers = len(workers)
futures = client.map(
local_test, range(len(workers)), workers=workers, n_workers=n_workers
)
client.gather(futures)

View File

@@ -8,19 +8,14 @@ import xgboost as xgb
from xgboost import testing as tm
from xgboost.core import DataSplitMode
try:
import pandas as pd
import pyarrow as pa
import pyarrow.csv as pc
except ImportError:
pass
pytestmark = pytest.mark.skipif(
tm.no_arrow()["condition"] or tm.no_pandas()["condition"],
reason=tm.no_arrow()["reason"] + " or " + tm.no_pandas()["reason"],
)
dpath = "demo/data/"
import pandas as pd
import pyarrow as pa
import pyarrow.csv as pc
class TestArrowTable:

View File

@@ -1098,9 +1098,10 @@ def test_pandas_input():
np.testing.assert_equal(model.feature_names_in_, np.array(feature_names))
columns = list(train.columns)
random.shuffle(columns)
rng.shuffle(columns)
df_incorrect = df[columns]
with pytest.raises(ValueError):
with pytest.raises(ValueError, match="feature_names mismatch"):
model.predict(df_incorrect)
clf_isotonic = CalibratedClassifierCV(model, cv="prefit", method="isotonic")