Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

Packed like sardines TT #3

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
76 changes: 46 additions & 30 deletions src/tt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,15 @@ TranspositionTable TT; // Our global transposition table

void TranspositionTable::resize(uint64_t mbSize) {

assert(msb((mbSize << 20) / sizeof(TTEntry)) < 32);
uint32_t newClusters = 1 << msb((mbSize << 20) / sizeof(Cluster));

uint32_t size = ClusterSize << msb((mbSize << 20) / sizeof(TTEntry[ClusterSize]));

if (hashMask == size - ClusterSize)
if (newClusters == clusters)
return;
clusters = newClusters;

hashMask = size - ClusterSize;
hashMask = (clusters - 1) * sizeof(Cluster);
free(mem);
mem = calloc(size * sizeof(TTEntry) + CACHE_LINE_SIZE - 1, 1);
mem = calloc(clusters * sizeof(Cluster) + CACHE_LINE_SIZE - 1, 1);

if (!mem)
{
Expand All @@ -60,7 +59,7 @@ void TranspositionTable::resize(uint64_t mbSize) {

void TranspositionTable::clear() {

std::memset(table, 0, (hashMask + ClusterSize) * sizeof(TTEntry));
std::memset(table, 0, clusters * sizeof(Cluster));
}


Expand All @@ -71,16 +70,12 @@ void TranspositionTable::clear() {
const TTEntry* TranspositionTable::probe(const Key key) const {

TTEntry* tte = first_entry(key);
uint32_t key32 = key >> 32;

for (unsigned i = 0; i < ClusterSize; ++i, ++tte)
if (tte->key32 == key32)
{
tte->generation8 = generation; // Refresh
return tte;
}
const uint16_t key16 = key >> 48;

return NULL;
if (tte->key != key16 && (++tte)->key != key16 && (++tte)->key != key16)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code leads to undefined behaviour. Never use several ++ operators on the same variable within an expression. Simply use tte[1] and tte[2] instead.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lucasart, that is incorrect. It doesn't lead to "undefined behavior". According to ISO/IEC C++ Standard 14882 搂5.14 Logical AND operator:

The && operator groups left-to-right. The operands are both implicitly converted to type bool (clause 4). The result is true if both operands are true and false otherwise. Unlike &, && guarantees left-to-right evaluation: the second operand is not evaluated if the first operand is false.

Also, I want tte pointing to the matching entry when/if the statement gets short-circuited. Using tte[1] and tte[2] doesn't accomplish that.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, you're right. The lazy boolean evaluation is guaranteed by the standard, so no compiler can screw up that code.

return NULL;
tte->genBound = generation | tte->genBound & 0x3; // Refresh
return tte;
}


Expand All @@ -94,28 +89,49 @@ const TTEntry* TranspositionTable::probe(const Key key) const {

void TranspositionTable::store(const Key key, Value v, Bound b, Depth d, Move m, Value statV) {

TTEntry *tte, *replace;
uint32_t key32 = key >> 32; // Use the high 32 bits as key inside the cluster

tte = replace = first_entry(key);
TTEntry* tte = first_entry(key);
const TTEntry* last = tte + ClusterSize - 1;
TTEntry* replace = tte;
uint16_t key16 = key >> 48; // Use the high 16 bits as key inside the cluster

for (unsigned i = 0; i < ClusterSize; ++i, ++tte)
for (;;)
{
if (!tte->key32 || tte->key32 == key32) // Empty or overwrite old

// Empty entry?
if (!tte->key)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should remove this if block. tte->key will be 0 a lot of the time for valid keys as well, so we could replace an entry with high depth here.

But maybe this is worth a test against itself, as it will be difficult to measure the effect against normal SF.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I agree. Good point.

You are correct, store will always overwrite a 0-key, even if it's not the best candidate for replacement. Even though this 0-key check allows the TT to fill faster (as it breaks immediately upon finding an "empty" entry), that probably doesn't buy much under tournament-like conditions with a full TT.

{
if (!m)
m = tte->move(); // Preserve any existing ttMove
tte->key = key16;
tte->move16 = (uint16_t)m;
break;
}

replace = tte;
// Overwrite old?
if (tte->key == key16)
{
if (m)
// Only store move if there is one
tte->move16 = (uint16_t)m;
break;
}

if (tte == last)
{
tte = replace;
tte->key = key16;
tte->move16 = (uint16_t)m;
break;
}

// Implement replace strategy
if ( ( tte->generation8 == generation || tte->bound() == BOUND_EXACT)
- (replace->generation8 == generation)
- (tte->depth16 < replace->depth16) < 0)
++tte;
// Is the next entry a better candidate for replacement?
if ((tte->gen() == generation || tte->bound() == BOUND_EXACT)
- (replace->gen() == generation)
- (tte->depth8 < replace->depth8) < 0)
replace = tte;
}

replace->save(key32, v, b, d, m, generation, statV);
tte->value16 = (int16_t)v;
tte->evalValue = (int16_t)statV;
tte->depth8 = (uint8_t)(d - DEPTH_NONE);
tte->genBound = generation | (uint8_t)b;
}
50 changes: 19 additions & 31 deletions src/tt.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,45 +23,27 @@
#include "misc.h"
#include "types.h"

/// The TTEntry is the 14 bytes transposition table entry, defined as below:
///
/// key 32 bit
/// move 16 bit
/// bound type 8 bit
/// generation 8 bit
/// value 16 bit
/// depth 16 bit
/// eval value 16 bit

struct TTEntry {

Move move() const { return (Move )move16; }
Bound bound() const { return (Bound)bound8; }
Bound bound() const { return (Bound)(genBound & 0x3); }
Value value() const { return (Value)value16; }
Depth depth() const { return (Depth)depth16; }
Depth depth() const { return (Depth)(depth8 + DEPTH_NONE); }
Value eval_value() const { return (Value)evalValue; }

private:
friend class TranspositionTable;

void save(uint32_t k, Value v, Bound b, Depth d, Move m, uint8_t g, Value ev) {

key32 = (uint32_t)k;
move16 = (uint16_t)m;
bound8 = (uint8_t)b;
generation8 = (uint8_t)g;
value16 = (int16_t)v;
depth16 = (int16_t)d;
evalValue = (int16_t)ev;
}
uint8_t gen() const { return genBound & 0xfc; }

uint32_t key32;
uint16_t key;
uint16_t move16;
uint8_t bound8, generation8;
int16_t value16, depth16, evalValue;
int16_t value16;
int16_t evalValue;
uint8_t depth8;
uint8_t genBound;
};


/// A TranspositionTable consists of a power of 2 number of clusters and each
/// cluster consists of ClusterSize number of TTEntry. Each non-empty entry
/// contains information of exactly one position. The size of a cluster should
Expand All @@ -70,11 +52,11 @@ struct TTEntry {

class TranspositionTable {

static const unsigned ClusterSize = 4;
static const unsigned ClusterSize = 3;

public:
~TranspositionTable() { free(mem); }
void new_search() { ++generation; }
void new_search() { generation += 4; }

const TTEntry* probe(const Key key) const;
TTEntry* first_entry(const Key key) const;
Expand All @@ -83,10 +65,16 @@ class TranspositionTable {
void store(const Key key, Value v, Bound type, Depth d, Move m, Value statV);

private:
uint32_t hashMask;
uint64_t hashMask;
TTEntry* table;
void* mem;
uint8_t generation; // Size must be not bigger than TTEntry::generation8
uint32_t clusters;
uint8_t generation;

struct Cluster {
TTEntry entry[ClusterSize];
char pad[2];
};
};

extern TranspositionTable TT;
Expand All @@ -98,7 +86,7 @@ extern TranspositionTable TT;

inline TTEntry* TranspositionTable::first_entry(const Key key) const {

return table + ((uint32_t)key & hashMask);
return (TTEntry*)((char*)table + (key & hashMask));
}

#endif // #ifndef TT_H_INCLUDED
2 changes: 1 addition & 1 deletion src/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ enum Depth {
DEPTH_QS_NO_CHECKS = -1 * ONE_PLY,
DEPTH_QS_RECAPTURES = -5 * ONE_PLY,

DEPTH_NONE = -127 * ONE_PLY
DEPTH_NONE = -6 * ONE_PLY
};

enum Square {
Expand Down