换位表大小的增加会降低搜索速度

时间:2018-08-25 11:46:25

标签: java arrays performance artificial-intelligence hashtable

我正在为我要参加的大学的锦标赛编写Kalah AI。 在开发过程中,我偶然发现了一些不确定的行为,这些行为取决于转置表的大小,这取决于我无法向自己解释的计算时间的变化。本质上,问题在于:增加转置表的大小会减少搜索到的位置数量,但不会减少搜索这些位置所花费的时间。

换位表

转位表本质上是一个哈希表,已搜索的位置及其搜索结果存储在该哈希表中。这类似于动态编程,在此过程中您要用计算时间来交换内存。 因此,直观地讲,增加可用内存将加快计算速度。在下面,您可以查看控制换位表行为的代码。 (是的,程序是用Java编写的。我知道在C ++中这样会更容易,但是要参与Java程序是必须的)

初始化,在搜索之前调用

public TranspositionTable(int power) {
    entries = (int) Math.pow(2, power); 
    table = new Entry[entries];
    hashmask = entries - 1;
    for(int i = 0; i < table.length; i++) {
        table[i] = new Entry();
        table[i].depth = Byte.MIN_VALUE;
    }
}

写访问权限:

public void add(NodeType type, short value, byte bestMove, byte depth, long hash) {
    int idx = (int) (hash & hashmask);
    Entry tte = table[idx];
    synchronized (tte) {
        if(!tte.used) {
            tte.used = true;
            curEntries++;
            rewrites--;
        }
        //don't overwrite higher search depth entries with lower ones.
        if(tte.depth <= depth){
            rewrites++;
            tte.hash = hash;
            tte.type = type;
            tte.value = value;
            tte.bestMove = bestMove;
            tte.depth = depth;
        }
    }
}

请注意,我目前仅使用一个线程进行搜索,因此同步语句目前是徒劳的。

读取权限:

public Entry get(long hash) {
    int idx = (int) (hash & hashmask);
    Entry e = table[idx];
    synchronized (e) {
        if(!e.used) {
            misses++;
            return null; 
        }
        if(hash == e.hash) {
            accesses++;
            return e;
        }
    }
    misses++;
    return null;
}

我正在使用64位Jenkins哈希函数生成密钥。

搜索

调用转座表是在迭代加深的alpha-beta搜索中进行的,它使用吸引窗口,静默搜索和其他一些与此主题可能无关的优化方法。搜索过程如下所示:

public static int search(int alpha, int beta, int stackIdx, MutableState node, int depth, TranspositionTable tt) {
    ...
    long hash = tt.getHash(node);
    Entry e = tt.get(hash);
    if (e != null) {// entry is valid?
        if (e.depth >= depth) {
            switch (e.type) {
            case EXACT:
                ss[stackIdx].pvIdx = e.bestMove;
                return e.value;
            case LOWERBOUND:
                if (e.value > alpha)
                    alpha = e.value;
                break;
            case UPPERBOUND:
                if (e.value < beta)
                    beta = e.value;
                break;
            }
            if (alpha >= beta) {
                ss[stackIdx].pvIdx = e.bestMove;
                return alpha;
            }
        }
    }
    ...
    // store node in TT
    NodeType type;
    if (best <= alphaOriginal) {
        type = NodeType.UPPERBOUND;
    } else if (best >= beta) {
        type = NodeType.LOWERBOUND;
    } else {
        type = NodeType.EXACT;
    }
    tt.add(type, (short) bestValue, (byte) bestMove, (byte) depth, hash);
    return best;
}

搜索指标

下面列出了选定的搜索和转置表访问度量,显示了三个运行平均的经过时间(毫秒),搜索到的位置数量,转置表使用的条目,负载(使用的条目除以总条目),成功访问,重写(从Kalah(7,7)起始位置进行的13次半深度深度搜索中捕获的表大小不等的未命中的错误。

table size 2^20:
Passed: 5581 ms
nodesSearched    6_781_473
qsnodesSearched 10_355_860
TT: entries:       966_427
TT: load:        0.9216566
TT: accesses:      426_747
TT: rewrites:    1_479_636
TT: misses:      2_800_001

table size 2^21:
Passed: 5473 ms
nodesSearched    6_571_630
qsnodesSearched 10_156_993
TT: entries:     1_487_042
TT: load:        0.7090769
TT: accesses:      471_566
TT: rewrites:    1_174_011
TT: misses:      2_665_098

table size 2^22:
Passed: 5688  ms
nodesSearched    6_449_400
qsnodesSearched 10_019_367
TT: entries:     1_908_758
TT: load:       0.45508337
TT: accesses:      501_231
TT: rewrites:      863_635
TT: misses:      2_585_731

table size 2^23:
Passed: 5667 ms
nodesSearched    6_380_913
qsnodesSearched  9_945_442
TT: entries:     2_174_936
TT: load:       0.25927258
TT: accesses:      517_884
TT: rewrites:      649_352
TT: misses:      2_538_265

table size 2^24:
Passed: 6015 ms
nodesSearched    6_356_761
qsnodesSearched  9_920_876
TT: entries:     2_329_595
TT: load:       0.13885468
TT: accesses:      526_862
TT: rewrites:      525_318
TT: misses:      2_519_007

table size 2^25:
Passed: 5924 ms
nodesSearched    6_332_790
qsnodesSearched  9_895_421
TT: entries:     2_408_029
TT: load:       0.07176486
TT: accesses:      531_226
TT: rewrites:      457_211
TT: misses:      2_504_210

注意:

  • 节点有两种类型,静默搜索节点和“正常”搜索节点。关于换位表,它们的区别仅在于在静默搜索过程中该表不用于检索或存储搜索结果。
  • 对于不同的起始位置产生相似的结果。
  • 换位表初始化不包括在时间测量中。

您可能已经观察到,随着表大小的增加,搜索到的节点数量会减少,但是经过的时间却没有。

我的第一个理论是,Java中的数组索引(计算特定数组项的内存地址)会随着数组大小的增加而花费更多的时间,通过使节点的搜索成本更高,从而使具有较小搜索树的速度无效。但我觉得情况并非如此,因为您希望搜索结果具有线性。

因此,我的问题是:是什么原因导致换位表尺寸增加时搜索花费更长的时间?

谢谢! :)

0 个答案:

没有答案
相关问题