Files
2026-07-13 13:35:51 +08:00

391 lines
12 KiB
Python

import multiprocessing as mp
import os
import time
import unittest
import backend as F
import dgl
from numpy.testing import assert_array_equal
from utils import generate_ip_config, reset_envs
# Create an one-part Graph
node_map = {"_N": F.tensor([[0, 6]], F.int64)}
edge_map = {("_N", "_E", "_N"): F.tensor([[0, 7]], F.int64)}
global_nid = F.tensor([0, 1, 2, 3, 4, 5], F.int64)
global_eid = F.tensor([0, 1, 2, 3, 4, 5, 6], F.int64)
g = dgl.graph([])
g.add_nodes(6)
g.add_edges(0, 1) # 0
g.add_edges(0, 2) # 1
g.add_edges(0, 3) # 2
g.add_edges(2, 3) # 3
g.add_edges(1, 1) # 4
g.add_edges(0, 4) # 5
g.add_edges(2, 5) # 6
g.ndata[dgl.NID] = global_nid
g.edata[dgl.EID] = global_eid
gpb = dgl.distributed.graph_partition_book.RangePartitionBook(
part_id=0,
num_parts=1,
node_map=node_map,
edge_map=edge_map,
ntypes={ntype: i for i, ntype in enumerate(g.ntypes)},
etypes={etype: i for i, etype in enumerate(g.canonical_etypes)},
)
node_policy = dgl.distributed.PartitionPolicy(
policy_str="node~_N", partition_book=gpb
)
edge_policy = dgl.distributed.PartitionPolicy(
policy_str="edge~_N:_E:_N", partition_book=gpb
)
data_0 = F.tensor(
[[1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0], [1.0, 1.0]],
F.float32,
)
data_0_1 = F.tensor([1.0, 2.0, 3.0, 4.0, 5.0, 6.0], F.float32)
data_0_2 = F.tensor([1, 2, 3, 4, 5, 6], F.int32)
data_0_3 = F.tensor([1, 2, 3, 4, 5, 6], F.int64)
data_1 = F.tensor(
[
[2.0, 2.0],
[2.0, 2.0],
[2.0, 2.0],
[2.0, 2.0],
[2.0, 2.0],
[2.0, 2.0],
[2.0, 2.0],
],
F.float32,
)
data_2 = F.tensor(
[[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0]],
F.float32,
)
def init_zero_func(shape, dtype):
return F.zeros(shape, dtype, F.cpu())
def udf_push(target, name, id_tensor, data_tensor):
target[name][id_tensor] = data_tensor * data_tensor
def add_push(target, name, id_tensor, data_tensor):
target[name][id_tensor] += data_tensor
@unittest.skipIf(
os.name == "nt" or os.getenv("DGLBACKEND") == "tensorflow",
reason="Do not support windows and TF yet",
)
def test_partition_policy():
assert node_policy.part_id == 0
assert edge_policy.part_id == 0
local_nid = node_policy.to_local(F.tensor([0, 1, 2, 3, 4, 5]))
local_eid = edge_policy.to_local(F.tensor([0, 1, 2, 3, 4, 5, 6]))
assert_array_equal(
F.asnumpy(local_nid), F.asnumpy(F.tensor([0, 1, 2, 3, 4, 5], F.int64))
)
assert_array_equal(
F.asnumpy(local_eid),
F.asnumpy(F.tensor([0, 1, 2, 3, 4, 5, 6], F.int64)),
)
nid_partid = node_policy.to_partid(F.tensor([0, 1, 2, 3, 4, 5], F.int64))
eid_partid = edge_policy.to_partid(F.tensor([0, 1, 2, 3, 4, 5, 6], F.int64))
assert_array_equal(
F.asnumpy(nid_partid), F.asnumpy(F.tensor([0, 0, 0, 0, 0, 0], F.int64))
)
assert_array_equal(
F.asnumpy(eid_partid),
F.asnumpy(F.tensor([0, 0, 0, 0, 0, 0, 0], F.int64)),
)
assert node_policy.get_part_size() == len(local_nid)
assert edge_policy.get_part_size() == len(local_eid)
def start_server(server_id, num_clients, num_servers):
# Init kvserver
print("Sleep 5 seconds to test client re-connect.")
time.sleep(5)
kvserver = dgl.distributed.KVServer(
server_id=server_id,
ip_config="kv_ip_config.txt",
num_servers=num_servers,
num_clients=num_clients,
)
kvserver.add_part_policy(node_policy)
kvserver.add_part_policy(edge_policy)
if kvserver.is_backup_server():
kvserver.init_data("data_0", "node~_N")
kvserver.init_data("data_0_1", "node~_N")
kvserver.init_data("data_0_2", "node~_N")
kvserver.init_data("data_0_3", "node~_N")
else:
kvserver.init_data("data_0", "node~_N", data_0)
kvserver.init_data("data_0_1", "node~_N", data_0_1)
kvserver.init_data("data_0_2", "node~_N", data_0_2)
kvserver.init_data("data_0_3", "node~_N", data_0_3)
# start server
server_state = dgl.distributed.ServerState(
kv_store=kvserver, local_g=None, partition_book=None
)
dgl.distributed.start_server(
server_id=server_id,
ip_config="kv_ip_config.txt",
num_servers=num_servers,
num_clients=num_clients,
server_state=server_state,
)
def start_server_mul_role(server_id, num_clients, num_servers):
# Init kvserver
kvserver = dgl.distributed.KVServer(
server_id=server_id,
ip_config="kv_ip_mul_config.txt",
num_servers=num_servers,
num_clients=num_clients,
)
kvserver.add_part_policy(node_policy)
if kvserver.is_backup_server():
kvserver.init_data("data_0", "node~_N")
else:
kvserver.init_data("data_0", "node~_N", data_0)
# start server
server_state = dgl.distributed.ServerState(
kv_store=kvserver, local_g=None, partition_book=None
)
dgl.distributed.start_server(
server_id=server_id,
ip_config="kv_ip_mul_config.txt",
num_servers=num_servers,
num_clients=num_clients,
server_state=server_state,
)
def start_client(num_clients, num_servers):
os.environ["DGL_DIST_MODE"] = "distributed"
# Note: connect to server first !
dgl.distributed.initialize(ip_config="kv_ip_config.txt")
# Init kvclient
kvclient = dgl.distributed.KVClient(
ip_config="kv_ip_config.txt", num_servers=num_servers
)
kvclient.map_shared_data(partition_book=gpb)
assert dgl.distributed.get_num_client() == num_clients
kvclient.init_data(
name="data_1",
shape=F.shape(data_1),
dtype=F.dtype(data_1),
part_policy=edge_policy,
init_func=init_zero_func,
)
kvclient.init_data(
name="data_2",
shape=F.shape(data_2),
dtype=F.dtype(data_2),
part_policy=node_policy,
init_func=init_zero_func,
)
# Test data_name_list
name_list = kvclient.data_name_list()
print(name_list)
assert "data_0" in name_list
assert "data_0_1" in name_list
assert "data_0_2" in name_list
assert "data_0_3" in name_list
assert "data_1" in name_list
assert "data_2" in name_list
# Test get_meta_data
meta = kvclient.get_data_meta("data_0")
dtype, shape, policy = meta
assert dtype == F.dtype(data_0)
assert shape == F.shape(data_0)
assert policy.policy_str == "node~_N"
meta = kvclient.get_data_meta("data_0_1")
dtype, shape, policy = meta
assert dtype == F.dtype(data_0_1)
assert shape == F.shape(data_0_1)
assert policy.policy_str == "node~_N"
meta = kvclient.get_data_meta("data_0_2")
dtype, shape, policy = meta
assert dtype == F.dtype(data_0_2)
assert shape == F.shape(data_0_2)
assert policy.policy_str == "node~_N"
meta = kvclient.get_data_meta("data_0_3")
dtype, shape, policy = meta
assert dtype == F.dtype(data_0_3)
assert shape == F.shape(data_0_3)
assert policy.policy_str == "node~_N"
meta = kvclient.get_data_meta("data_1")
dtype, shape, policy = meta
assert dtype == F.dtype(data_1)
assert shape == F.shape(data_1)
assert policy.policy_str == "edge~_N:_E:_N"
meta = kvclient.get_data_meta("data_2")
dtype, shape, policy = meta
assert dtype == F.dtype(data_2)
assert shape == F.shape(data_2)
assert policy.policy_str == "node~_N"
# Test push and pull
id_tensor = F.tensor([0, 2, 4], F.int64)
data_tensor = F.tensor([[6.0, 6.0], [6.0, 6.0], [6.0, 6.0]], F.float32)
kvclient.push(name="data_0", id_tensor=id_tensor, data_tensor=data_tensor)
kvclient.push(name="data_1", id_tensor=id_tensor, data_tensor=data_tensor)
kvclient.push(name="data_2", id_tensor=id_tensor, data_tensor=data_tensor)
res = kvclient.pull(name="data_0", id_tensor=id_tensor)
assert_array_equal(F.asnumpy(res), F.asnumpy(data_tensor))
res = kvclient.pull(name="data_1", id_tensor=id_tensor)
assert_array_equal(F.asnumpy(res), F.asnumpy(data_tensor))
res = kvclient.pull(name="data_2", id_tensor=id_tensor)
assert_array_equal(F.asnumpy(res), F.asnumpy(data_tensor))
# Register new push handler
kvclient.register_push_handler("data_0", udf_push)
kvclient.register_push_handler("data_1", udf_push)
kvclient.register_push_handler("data_2", udf_push)
# Test push and pull
kvclient.push(name="data_0", id_tensor=id_tensor, data_tensor=data_tensor)
kvclient.push(name="data_1", id_tensor=id_tensor, data_tensor=data_tensor)
kvclient.push(name="data_2", id_tensor=id_tensor, data_tensor=data_tensor)
kvclient.barrier()
data_tensor = data_tensor * data_tensor
res = kvclient.pull(name="data_0", id_tensor=id_tensor)
assert_array_equal(F.asnumpy(res), F.asnumpy(data_tensor))
res = kvclient.pull(name="data_1", id_tensor=id_tensor)
assert_array_equal(F.asnumpy(res), F.asnumpy(data_tensor))
res = kvclient.pull(name="data_2", id_tensor=id_tensor)
assert_array_equal(F.asnumpy(res), F.asnumpy(data_tensor))
# Test delete data
kvclient.delete_data("data_0")
kvclient.delete_data("data_1")
kvclient.delete_data("data_2")
# Register new push handler
kvclient.init_data(
name="data_3",
shape=F.shape(data_2),
dtype=F.dtype(data_2),
part_policy=node_policy,
init_func=init_zero_func,
)
kvclient.register_push_handler("data_3", add_push)
data_tensor = F.tensor([[6.0, 6.0], [6.0, 6.0], [6.0, 6.0]], F.float32)
kvclient.barrier()
time.sleep(kvclient.client_id + 1)
print("add...")
kvclient.push(name="data_3", id_tensor=id_tensor, data_tensor=data_tensor)
kvclient.barrier()
res = kvclient.pull(name="data_3", id_tensor=id_tensor)
data_tensor = data_tensor * num_clients
assert_array_equal(F.asnumpy(res), F.asnumpy(data_tensor))
def start_client_mul_role(i):
os.environ["DGL_DIST_MODE"] = "distributed"
# Initialize creates kvstore !
dgl.distributed.initialize(ip_config="kv_ip_mul_config.txt")
if i == 0: # block one trainer
time.sleep(5)
kvclient = dgl.distributed.kvstore.get_kvstore()
kvclient.barrier()
print("i: %d role: %s" % (i, kvclient.role))
assert dgl.distributed.role.get_num_trainers() == 2
assert dgl.distributed.role.get_trainer_rank() < 2
print(
"trainer rank: %d, global rank: %d"
% (
dgl.distributed.role.get_trainer_rank(),
dgl.distributed.role.get_global_rank(),
)
)
dgl.distributed.exit_client()
@unittest.skipIf(
os.name == "nt" or os.getenv("DGLBACKEND") == "tensorflow",
reason="Do not support windows and TF yet",
)
def test_kv_store():
reset_envs()
num_servers = 2
num_clients = 2
generate_ip_config("kv_ip_config.txt", 1, num_servers)
ctx = mp.get_context("spawn")
pserver_list = []
pclient_list = []
os.environ["DGL_NUM_SERVER"] = str(num_servers)
for i in range(num_servers):
pserver = ctx.Process(
target=start_server, args=(i, num_clients, num_servers)
)
pserver.start()
pserver_list.append(pserver)
for i in range(num_clients):
pclient = ctx.Process(
target=start_client, args=(num_clients, num_servers)
)
pclient.start()
pclient_list.append(pclient)
for i in range(num_clients):
pclient_list[i].join()
for i in range(num_servers):
pserver_list[i].join()
@unittest.skipIf(
os.name == "nt" or os.getenv("DGLBACKEND") == "tensorflow",
reason="Do not support windows and TF yet",
)
def test_kv_multi_role():
reset_envs()
num_servers = 2
num_trainers = 2
num_samplers = 2
generate_ip_config("kv_ip_mul_config.txt", 1, num_servers)
# There are two trainer processes and each trainer process has two sampler processes.
num_clients = num_trainers * (1 + num_samplers)
ctx = mp.get_context("spawn")
pserver_list = []
pclient_list = []
os.environ["DGL_NUM_SAMPLER"] = str(num_samplers)
os.environ["DGL_NUM_SERVER"] = str(num_servers)
for i in range(num_servers):
pserver = ctx.Process(
target=start_server_mul_role, args=(i, num_clients, num_servers)
)
pserver.start()
pserver_list.append(pserver)
for i in range(num_trainers):
pclient = ctx.Process(target=start_client_mul_role, args=(i,))
pclient.start()
pclient_list.append(pclient)
for i in range(num_trainers):
pclient_list[i].join()
for i in range(num_servers):
pserver_list[i].join()
if __name__ == "__main__":
test_partition_policy()
test_kv_store()
test_kv_multi_role()