Skip to content

Commit

Permalink
ノード管理の変更
Browse files Browse the repository at this point in the history
- 子ノードへのポインタ配列を初回アクセス時に作成する
- メモリ節約のため、move_countでevaledかを表しevaledを削除する
- メモリ節約のため、moveの最上位バイトでWin/Lose/Drawの状態を表す
- メモリ節約のため、visitor_tにvalue_winを移す
  • Loading branch information
TadaoYamaoka committed Jan 9, 2021
1 parent aedc384 commit f6d8ee9
Show file tree
Hide file tree
Showing 3 changed files with 195 additions and 180 deletions.
69 changes: 41 additions & 28 deletions usi/Node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,37 +65,50 @@ NodeGarbageCollector gNodeGc;

uct_node_t* uct_node_t::ReleaseChildrenExceptOne(const Move move)
{
// 一つを残して削除する
bool found = false;
for (int i = 0; i < child_num; ++i) {
auto& uct_child = child[i];
if (uct_child.move == move) {
found = true;
if (!uct_child.node) {
// 新しいノードを作成する
uct_child.node = std::make_unique<uct_node_t>();
if (child_num > 0 && child_nodes) {
// 一つを残して削除する
bool found = false;
for (int i = 0; i < child_num; ++i) {
auto& uct_child = child[i];
auto& child_node = child_nodes[i];
if (uct_child.move == move) {
found = true;
if (!child_node) {
// 新しいノードを作成する
child_node = std::make_unique<uct_node_t>();
}
// 0番目の要素に移動する
if (i != 0) {
child[0] = std::move(uct_child);
child_nodes[0] = std::move(child_node);
}
}
// 0番目の要素に移動する
if (i != 0)
child[0] = std::move(uct_child);
else {
// 子ノードを削除(ガベージコレクタに追加)
if (child_node)
gNodeGc.AddToGcQueue(std::move(child_node));
}
}

if (found) {
// 子ノードを一つにする
child_num = 1;
return child_nodes[0].get();
}
else {
// 子ノードを削除(ガベージコレクタに追加)
if (uct_child.node)
gNodeGc.AddToGcQueue(std::move(uct_child.node));
// 合法手に不成を生成していないため、ノードが存在しても見つからない場合がある
// 子ノードが見つからなかった場合、新しいノードを作成する
CreateSingleChildNode(move);
InitChildNodes();
return (child_nodes[0] = std::make_unique<uct_node_t>()).get();
}
}

if (found) {
// 子ノードを一つにする
child_num = 1;
return child[0].node.get();
}
else {
// 子ノードが見つからなかった場合、新しいノードを作成する
// 子ノード未展開、または子ノードへのポインタ配列が未初期化の場合
CreateSingleChildNode(move);
child[0].node = std::make_unique<uct_node_t>();
return child[0].node.get();
// 子ノードへのポインタ配列を初期化する
InitChildNodes();
return (child_nodes[0] = std::make_unique<uct_node_t>()).get();
}
}

Expand Down Expand Up @@ -136,10 +149,10 @@ bool NodeTree::ResetToPosition(const Key starting_pos_key, const std::vector<Mov
if (!seen_old_head && current_head_ != old_head) {
if (prev_head) {
assert(prev_head->child_num == 1);
auto& prev_uct_child = prev_head->child[0];
gNodeGc.AddToGcQueue(std::move(prev_uct_child.node));
prev_uct_child.node = std::make_unique<uct_node_t>();
current_head_ = prev_uct_child.node.get();
auto& prev_uct_child_node = prev_head->child_nodes[0];
gNodeGc.AddToGcQueue(std::move(prev_uct_child_node));
prev_uct_child_node = std::make_unique<uct_node_t>();
current_head_ = prev_uct_child_node.get();
}
else {
// 開始局面に戻った場合
Expand Down
52 changes: 31 additions & 21 deletions usi/Node.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,41 +12,54 @@ typedef double WinType;
typedef float WinType;
#endif

// 詰み探索で詰みの場合の定数
constexpr u32 VALUE_WIN = 0x1000000;
constexpr u32 VALUE_LOSE = 0x2000000;
// 千日手の場合のvalue_winの定数
constexpr u32 VALUE_DRAW = 0x4000000;

// ノード未展開を表す定数
constexpr int NOT_EXPANDED = -1;

struct uct_node_t;
struct child_node_t {
child_node_t() : move_count(0), win(0.0f), nnrate(0.0f) {}
child_node_t(const Move move)
: move(move), move_count(0), win(0.0f), nnrate(0.0f) {}
// ムーブコンストラクタ
child_node_t(child_node_t&& o) noexcept
: move(o.move), move_count(0), win(0.0f), nnrate(0.0f), node(std::move(o.node)) {}
: move(o.move), move_count(0), win(0.0f), nnrate(0.0f) {}
// ムーブ代入演算子
child_node_t& operator=(child_node_t&& o) noexcept {
move = o.move;
move_count = (int)o.move_count;
win = (float)o.win;
nnrate = (float)o.nnrate;
node = std::move(o.node);
return *this;
}

// 子ノード作成
uct_node_t* CreateChildNode() {
node = std::make_unique<uct_node_t>();
return node.get();
}
// メモリ節約のため、moveの最上位バイトでWin/Lose/Drawの状態を表す
bool IsWin() const { return move.value() & VALUE_WIN; }
void SetWin() { move |= Move(VALUE_WIN); }
bool IsLose() const { return move.value() & VALUE_LOSE; }
void SetLose() { move |= Move(VALUE_LOSE); }
bool IsDraw() const { return move.value() & VALUE_DRAW; }
void SetDraw() { move |= Move(VALUE_DRAW); }

Move move; // 着手する座標
std::atomic<int> move_count; // 探索回数
std::atomic<WinType> win; // 勝った回数
float nnrate; // ニューラルネットワークでのレート
std::unique_ptr<uct_node_t> node; // 子ノードへのポインタ
};

struct uct_node_t {
uct_node_t()
: move_count(0), win(0.0f), evaled(false), value_win(0.0f), visited_nnrate(0.0f), child_num(0) {}
: move_count(NOT_EXPANDED), win(0), visited_nnrate(0.0f), child_num(0) {}

// 子ノード作成
uct_node_t* CreateChildNode(int i) {
return (child_nodes[i] = std::make_unique<uct_node_t>()).get();
}
// 子ノード一つのみで初期化する
void CreateSingleChildNode(const Move move) {
child_num = 1;
Expand All @@ -56,33 +69,30 @@ struct uct_node_t {
// 候補手の展開
void ExpandNode(const Position* pos) {
MoveList<Legal> ml(*pos);
child_num = ml.size();
child_num = (short)ml.size();
child = std::make_unique<child_node_t[]>(ml.size());
auto* child_node = child.get();
for (; !ml.end(); ++ml) child_node++->move = ml.move();
}
// 子ノードへのポインタ配列の初期化
void InitChildNodes() {
child_nodes = std::make_unique<std::unique_ptr<uct_node_t>[]>(child_num);
}

// 1つを除くすべての子を削除する
// 1つも見つからない場合、新しいノードを作成する
// 残したノードを返す
uct_node_t* ReleaseChildrenExceptOne(const Move move);

void Lock() {
mtx.lock();
}
void UnLock() {
mtx.unlock();
}
bool IsEvaled() const { return move_count != NOT_EXPANDED; }
void SetEvaled() { move_count = 0; }

std::atomic<int> move_count;
std::atomic<WinType> win;
std::atomic<bool> evaled; // 評価済か
std::atomic<float> value_win;
std::atomic<float> visited_nnrate;
int child_num; // 子ノードの数
short child_num; // 子ノードの数
std::unique_ptr<child_node_t[]> child; // 子ノードの情報

std::mutex mtx;
std::unique_ptr<std::unique_ptr<uct_node_t>[]> child_nodes; // 子ノードへのポインタ配列
};

class NodeTree {
Expand Down
Loading

0 comments on commit f6d8ee9

Please sign in to comment.