summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-09-05 00:52:13 -0700
committerScott Gasch <[email protected]>2026-09-05 00:52:13 -0700
commit57502d6e205bc802fc9b89fd1534add5efc4b9ea (patch)
treecd400f2538d093a41ceaee932fc21df5dc5f54e1
parent2ce3570dc6aa67f82dd6f02fc4e518be3e33aabf (diff)
Retire pawns' bvAttacks writes in favor of pos->bbPawnAttacks[2]
First piece type converted per board_representation/EVAL.md section 2: pawns no longer write into rgSquare[c|8].bvAttacks at all. Every consumer of "does a pawn attack this square" now reads pos->bbPawnAttacks[2] directly -- a plain bitboard, computed fresh each Eval() call from pos->bbPawns[] via the same shift-and-mask technique generate.c's _GenerateAllPawnMovesBB already uses (zero per-pawn mailbox iteration, vs. up to 16 delta+IS_ON_BOARD checks before). Knight/bishop/rook/queen/king are unchanged -- still populate/read their own bvAttacks bits (uMinor/uRook/uQueen/uKing) the old way until their own conversions land. Consumer changes, all in eval.c: - UNSAFE_FOR_MINOR retired as a macro (it only ever tested the pawn bit) -- its 3 call sites (knight, bishop x2) now test bbPawnAttacks directly. - UNSAFE_FOR_ROOK/_QUEEN masks narrowed to drop the now-dead pawn bit; call sites OR in an explicit bbPawnAttacks test alongside the narrowed bvAttacks read. - Two direct .small.uPawn reads (bishop's transient-pawn mobility credit, bishop's defended-pawn bonus) switched to bbPawnAttacks tests. - _EvalKing's bvAttack/bvDefend (the real king-danger computation) OR the bitboard bit back in at both read points, careful to preserve the original ordering where bvDefend must reflect the king's own just-set defend bit. - _WhoControlsSquareFast (used by passer-race/trapped-piece/danger code) ORs the bitboard bit back into its g_SwapTable index at the same bit position PAWN_BIT always occupied. - Two bugs caught by manually auditing every remaining |8 site after the fact (not by any test failing): _EvalPawns' own pawn-duo and backward-pawn detection read bvAttacks.uWholeThing at a point in Eval()'s sequence where only pawns could have written it -- once pawns stopped writing there, both checks went permanently dead silently. Fixed to read bbPawnAttacks directly. No self-test caught this; it's exactly the gap EVAL.md section 5's planned exact-score harness is meant to close. EVAL_TIME instrumentation: split _EvalPawns' cycle counter into pawn-hash hit/miss buckets (chess.h/eval.c/root.c), answering whether the hash is still worth it now that attack-bit population is nearly free. Measured on one sd12 benchmark position: hits average 90.4 cycles, misses average 1741.3 cycles (~19x), 97.15% hit rate -- the hash stays a clear win; the miss cost was never mostly attack-bit population (that's a separate ~1% bucket now, down from ~3.6%), it's the isolated/doubled/duo/backward-pawn scoring loops, which still do real per-pawn work on a miss. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01P6g6iF6mD1Hau6nCZCzwYj
-rwxr-xr-xsrc/chess.h51
-rwxr-xr-xsrc/eval.c209
-rwxr-xr-xsrc/root.c16
3 files changed, 177 insertions, 99 deletions
diff --git a/src/chess.h b/src/chess.h
index 41c8734..7ff9994 100755
--- a/src/chess.h
+++ b/src/chess.h
@@ -595,9 +595,17 @@ typedef union _ATTACK_BITV
}
ATTACK_BITV;
-#define UNSAFE_FOR_MINOR(x) ((ULONG)((x).uWholeThing) & 0x00000080UL)
-#define UNSAFE_FOR_ROOK(x) ((ULONG)((x).uWholeThing) & 0x000000C0UL)
-#define UNSAFE_FOR_QUEEN(x) ((ULONG)((x).uWholeThing) & 0x000000E0UL)
+// UNSAFE_FOR_MINOR retired as a bvAttacks-based macro 2026-09-05:
+// pawns no longer write PAWN_BIT into bvAttacks (see POSITION's
+// bbPawnAttacks[2] comment, chess.h) -- it was PAWN_BIT alone, so
+// every former call site now just tests
+// (pos->bbPawnAttacks[enemy] & COOR_TO_BB(sq)) directly, no macro
+// needed. UNSAFE_FOR_ROOK/_QUEEN's masks below are narrowed to drop
+// PAWN_BIT (0x80), which would otherwise silently always read 0 now
+// that nothing sets it -- callers combine these with an explicit
+// bbPawnAttacks test instead (see eval.c's _EvalRook/_EvalQueen).
+#define UNSAFE_FOR_ROOK(x) ((ULONG)((x).uWholeThing) & 0x00000040UL)
+#define UNSAFE_FOR_QUEEN(x) ((ULONG)((x).uWholeThing) & 0x00000060UL)
#define PAWN_BIT 0x00000080UL
#define MINOR_BIT 0x00000040UL
@@ -688,6 +696,26 @@ typedef struct _POSITION
// not as something callers should call directly anymore.
BITBOARD bbOccupied;
+ // First mover of board_representation/EVAL.md section 2's
+ // bvAttacks replacement, added 2026-09-05: "which squares does
+ // this side's pawns attack," computed fresh once per Eval() call
+ // from bbPawns via a single shift-and-mask (see
+ // _PopulatePawnAttackBits in eval.c, same technique as
+ // generate.c's _GenerateAllPawnMovesBB) -- zero per-pawn mailbox
+ // iteration, so unlike bbPieces/bbPawns/bbOccupied above this is
+ // NOT incrementally maintained across moves; it's plain Eval()-
+ // scoped scratch space, recomputed every call the same way
+ // pos->iScore[] is. Pawns no longer write their attack bit into
+ // rgSquare[c|8].bvAttacks at all -- every consumer of "does an
+ // enemy/friendly pawn attack this square" reads this bitboard
+ // directly instead (UNSAFE_FOR_MINOR/_ROOK/_QUEEN's pawn
+ // component, _EvalKing's bvAttack/bvDefend). Knight/bishop/rook/
+ // queen/king still populate/read bvAttacks for their own bits
+ // (uMinor/uRook/uQueen/uKing) until their own conversions land --
+ // see EVAL.md section 2 for the planned bbMinorAttacks/
+ // bbRookAttacks/bbQueenAttacks that will retire the rest of it.
+ BITBOARD bbPawnAttacks[2];
+
ULONG uWhiteSqBishopCount[2]; // num bishops on white squares
SCORE iMaterialBalance[2]; // material balance
@@ -926,6 +954,23 @@ typedef struct _COUNTERS
// its own accumulator directly.
//
UINT64 u64CyclesEvalPawns;
+ //
+ // Split of the counter above by pawn-hash outcome (board_
+ // representation/EVAL.md section 0c/0d's "is the hash still
+ // worth it now that attack-bit population is cheap" question)
+ // -- Hit = _EvalPawns returned early via the hash (fDeferred
+ // TRUE); Miss = full recompute, including
+ // _PopulatePawnAttackBits and the isolated/doubled/duo/
+ // backward-pawn scoring loops (fDeferred FALSE). Hit+Miss
+ // calls sum to u64FullEvals-ish call count, not to
+ // u64CyclesEvalPawns exactly (that's still measured as one
+ // contiguous window; these are the same window, just bucketed
+ // by outcome after the fact).
+ //
+ UINT64 u64CyclesEvalPawnsHit;
+ UINT64 u64CyclesEvalPawnsMiss;
+ UINT64 u64CountEvalPawnsHit;
+ UINT64 u64CountEvalPawnsMiss;
UINT64 u64CyclesEvalKnight;
UINT64 u64CyclesEvalBishop;
UINT64 u64CyclesEvalRook;
diff --git a/src/eval.c b/src/eval.c
index 17793da..35ee663 100755
--- a/src/eval.c
+++ b/src/eval.c
@@ -1176,10 +1176,21 @@ Return value:
**/
{
+ //
+ // bvAttacks no longer carries the pawn bit (pawns write
+ // pos->bbPawnAttacks[2] directly instead, see
+ // _PopulatePawnAttackBits) -- OR it back in at its usual bit
+ // position (PAWN_BIT) so g_SwapTable's indexing below sees the
+ // same bit shape it always has.
+ //
ULONG uWhite = pos->rgSquare[c|8].bvAttacks[WHITE].uSmall |
- pos->rgSquare[c|8].bvAttacks[WHITE].uXray;
+ pos->rgSquare[c|8].bvAttacks[WHITE].uXray |
+ ((pos->bbPawnAttacks[WHITE] & COOR_TO_BB(c)) ?
+ PAWN_BIT : 0);
ULONG uBlack = pos->rgSquare[c|8].bvAttacks[BLACK].uSmall |
- pos->rgSquare[c|8].bvAttacks[BLACK].uXray;
+ pos->rgSquare[c|8].bvAttacks[BLACK].uXray |
+ ((pos->bbPawnAttacks[BLACK] & COOR_TO_BB(c)) ?
+ PAWN_BIT : 0);
ULONG u;
PIECE p;
CHAR ch;
@@ -1763,9 +1774,30 @@ Return value:
}
-#define PAWN_ATTACK_BLACK_DELTA (+15)
-#define PAWN_ATTACK_WHITE_DELTA (-17)
-
+// board_representation/EVAL.md section 2/0d: bitboard replacement for
+// the old per-pawn mailbox delta+IS_ON_BOARD population this function
+// used to do. pos->bbPawns[2] already has every pawn location
+// (incrementally maintained, chess.h) with zero per-pawn iteration
+// needed to query it -- the exact shift-and-mask technique
+// generate.c's _GenerateAllPawnMovesBB already uses to bulk-generate a
+// whole side's pawn captures (its bbCapLeft/bbCapRight, before being
+// masked down to actual enemy-occupied squares) is exactly "every
+// square this side's pawns attack" -- reused here verbatim, minus the
+// enemy-occupancy mask, since attack-bit population doesn't care what
+// (if anything) sits on the attacked square. See that function's
+// header comment for the full square-numbering/shift-direction
+// derivation (WHITE forward = bb >> 8, BLACK forward = bb << 8,
+// diagonals need the *opposite* file excluded to prevent same-row
+// wraparound) -- not repeated here.
+//
+// Unlike the old version, this does NOT write PAWN_BIT into
+// rgSquare[c|8].bvAttacks -- pos->bbPawnAttacks[2] (chess.h) is now
+// the single source of truth for "does a pawn attack this square",
+// read directly by every consumer (UNSAFE_FOR_MINOR's old callers,
+// UNSAFE_FOR_ROOK/_QUEEN, _EvalKing's bvAttack/bvDefend). Knight/
+// bishop/rook/queen/king still populate/read the rest of bvAttacks
+// (uMinor/uRook/uQueen/uKing) the old way, so _ClearAttackTables(pos)
+// still needs to run here first.
static void
_PopulatePawnAttackBits(IN OUT POSITION *pos)
/**
@@ -1784,81 +1816,15 @@ Return value:
**/
{
- ULONG u;
- COOR c;
- COOR cAttack;
-
- //
- // IDEA: combine clearing and initial population somehow?
- //
- // r - - - - - - l : only check L- and R-
- // r - - - - - - l
- // R A A A A A A L
- // R A A A A A A L
- // R A A A A A A L
- // R A A A A A A L
- // r - - - - - - l
- // r - - - - - - l : only check L+ and R+
_ClearAttackTables(pos);
- ASSERT(pos->uPawnCount[BLACK] <= 8);
- for (u = 0;
- u < pos->uPawnCount[BLACK];
- u++)
- {
- c = pos->cPawns[BLACK][u];
- ASSERT(IS_ON_BOARD(c));
- ASSERT(pos->rgSquare[c].pPiece);
- ASSERT(IS_PAWN(pos->rgSquare[c].pPiece));
- ASSERT(GET_COLOR(pos->rgSquare[c].pPiece) == BLACK);
-
- //
- // IDEA: These IS_ON_BOARD checks can be removed by a lookup table.
- //
- cAttack = c + PAWN_ATTACK_BLACK_DELTA;
- if (IS_ON_BOARD(cAttack))
- {
- ASSERT((cAttack + 8) == (cAttack|8));
- ASSERT(!IS_ON_BOARD(cAttack|8));
- pos->rgSquare[cAttack|8].bvAttacks[BLACK].small.uPawn = 1;
- }
- cAttack += 2;
- if (IS_ON_BOARD(cAttack))
- {
- ASSERT((cAttack + 8) == (cAttack|8));
- cAttack |= 8;
- ASSERT(!IS_ON_BOARD(cAttack));
- pos->rgSquare[cAttack].bvAttacks[BLACK].small.uPawn = 1;
- }
- }
-
- ASSERT(pos->uPawnCount[WHITE] <= 8);
- for (u = 0;
- u < pos->uPawnCount[WHITE];
- u++)
- {
- c = pos->cPawns[WHITE][u];
- ASSERT(IS_ON_BOARD(c));
- ASSERT(pos->rgSquare[c].pPiece);
- ASSERT(IS_PAWN(pos->rgSquare[c].pPiece));
- ASSERT(GET_COLOR(pos->rgSquare[c].pPiece) == WHITE);
+ pos->bbPawnAttacks[BLACK] =
+ ((pos->bbPawns[BLACK] & ~BBFILE[0]) << 7) |
+ ((pos->bbPawns[BLACK] & ~BBFILE[7]) << 9);
- cAttack = c + PAWN_ATTACK_WHITE_DELTA;
- if (IS_ON_BOARD(cAttack))
- {
- ASSERT((cAttack + 8) == (cAttack|8));
- ASSERT(!IS_ON_BOARD(cAttack|8));
- pos->rgSquare[cAttack|8].bvAttacks[WHITE].small.uPawn = 1;
- }
- cAttack += 2;
- if (IS_ON_BOARD(cAttack))
- {
- ASSERT((cAttack + 8) == (cAttack|8));
- cAttack |= 8;
- ASSERT(!IS_ON_BOARD(cAttack));
- pos->rgSquare[cAttack].bvAttacks[WHITE].small.uPawn = 1;
- }
- }
+ pos->bbPawnAttacks[WHITE] =
+ ((pos->bbPawns[WHITE] & ~BBFILE[0]) >> 9) |
+ ((pos->bbPawns[WHITE] & ~BBFILE[7]) >> 7);
}
@@ -2170,8 +2136,16 @@ Return value:
//
cSquare = c + d1;
ASSERT(IS_ON_BOARD(cSquare));
- ASSERT((cSquare + 8) == (cSquare | 8));
- if (pos->rgSquare[cSquare|8].bvAttacks[uColor].uWholeThing)
+ //
+ // At this point in Eval()'s sequence (inside _EvalPawns,
+ // before any other piece type has run this call), pawns
+ // are the only thing that could possibly have marked an
+ // attack -- bvAttacks itself no longer carries the pawn
+ // bit at all (see _PopulatePawnAttackBits), so this reads
+ // pos->bbPawnAttacks directly instead of the (permanently
+ // zero, at this point in execution) bvAttacks word.
+ //
+ if (pos->bbPawnAttacks[uColor] & COOR_TO_BB(cSquare))
{
//
// Count pawn duos. See "Pawn Power in Chess" pp 10-16
@@ -2193,7 +2167,7 @@ Return value:
(FLIP(uColor) * (9 - RANK(cSquare))));
ASSERT(iDuos[uColor] <= (7 * 8));
}
- else if (pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)].uWholeThing)
+ else if (pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare))
{
//
// Detect backwards pawns. See "Pawn Power in Chess" pp 25-27
@@ -2665,7 +2639,7 @@ Return value:
ASSERT(GET_COLOR(pos->rgSquare[cSquare].pPiece) == FLIP(uColor));
ASSERT(pos->bbPawns[FLIP(uColor)] & COOR_TO_BB(cSquare));
i +=
- (pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)].small.uPawn != 0) *
+ ((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) != 0) *
TRANSIENT_PAWN_ON_BISHOP_COLOR[cSquare] / 2;
}
EVAL_TERM(uColor,
@@ -2723,13 +2697,13 @@ Return value:
{
case BMOB_EMPTY:
uCurrentMobility +=
- !UNSAFE_FOR_MINOR(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]);
+ !(pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare));
fStop = FALSE;
break;
case BMOB_ENEMY_PAWN:
uCurrentMobility +=
- !UNSAFE_FOR_MINOR(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]);
+ !(pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare));
fStop = TRUE;
break;
@@ -2824,7 +2798,7 @@ Return value:
if (TRUE == _IsSquareSafeFromEnemyPawn(pos, c, bb))
{
// Defended (and defending) a friendly pawn.
- if (pos->rgSquare[c|8].bvAttacks[uColor].small.uPawn)
+ if (pos->bbPawnAttacks[uColor] & COOR_TO_BB(c))
{
#ifdef DEBUG
p = BLACK_PAWN | uColor;
@@ -3097,7 +3071,7 @@ Return value:
{
case NMOB_MOBILE_SQUARE:
uMobilitySquares +=
- !UNSAFE_FOR_MINOR(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]);
+ !(pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare));
break;
case NMOB_ENEMY_OTHER:
@@ -3406,13 +3380,15 @@ Return value:
{
case RMOB_EMPTY:
uCurrentMobility +=
- !UNSAFE_FOR_ROOK(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]);
+ !((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) ||
+ UNSAFE_FOR_ROOK(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]));
fStop = FALSE;
break;
case RMOB_ENEMY_LESS:
uCurrentMobility +=
- !UNSAFE_FOR_ROOK(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]);
+ !((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) ||
+ UNSAFE_FOR_ROOK(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]));
fStop = TRUE;
break;
@@ -3707,13 +3683,15 @@ Return value:
{
case QMOB_EMPTY:
uTotalMobility +=
- !UNSAFE_FOR_QUEEN(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]);
+ !((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) ||
+ UNSAFE_FOR_QUEEN(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]));
fStop = FALSE;
break;
case QMOB_ENEMY_LESS:
uTotalMobility +=
- !UNSAFE_FOR_QUEEN(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]);
+ !((pos->bbPawnAttacks[FLIP(uColor)] & COOR_TO_BB(cSquare)) ||
+ UNSAFE_FOR_QUEEN(pos->rgSquare[cSquare|8].bvAttacks[FLIP(uColor)]));
fStop = TRUE;
break;
@@ -3943,11 +3921,30 @@ Return value:
{
p = pos->rgSquare[cSquare].pPiece;
- cSquare |= 8;
- bvAttack = pos->rgSquare[cSquare].bvAttacks[ufColor].uSmall;
- bvXray = pos->rgSquare[cSquare].bvAttacks[ufColor].uXray;
- pos->rgSquare[cSquare].bvAttacks[uColor].small.uKing = 1;
- bvDefend = pos->rgSquare[cSquare].bvAttacks[uColor].uSmall;
+ //
+ // bvAttacks no longer carries the pawn bit (pawns write
+ // pos->bbPawnAttacks[2] directly instead, see
+ // _PopulatePawnAttackBits) -- OR it back in here from the
+ // bitboard, keyed off the real board square (cSquare,
+ // before the |8 below flips it into the invisible-half
+ // storage index bvAttacks itself uses). bvDefend's OR-in
+ // (and the .small.uKing = 1 write) must stay after this
+ // point, same order as before -- bvDefend is meant to
+ // include this king's own just-set attack/defend bit on
+ // the square.
+ //
+ bvAttack = pos->rgSquare[cSquare|8].bvAttacks[ufColor].uSmall |
+ ((pos->bbPawnAttacks[ufColor] & COOR_TO_BB(cSquare)) ?
+ PAWN_BIT : 0);
+ {
+ COOR cRealSquare = cSquare;
+ cSquare |= 8;
+ bvXray = pos->rgSquare[cSquare].bvAttacks[ufColor].uXray;
+ pos->rgSquare[cSquare].bvAttacks[uColor].small.uKing = 1;
+ bvDefend = pos->rgSquare[cSquare].bvAttacks[uColor].uSmall |
+ ((pos->bbPawnAttacks[uColor] & COOR_TO_BB(cRealSquare)) ?
+ PAWN_BIT : 0);
+ }
//
// Count squares near the king the enemy queen specifically
@@ -4850,8 +4847,28 @@ Return value:
// save some time. BEFORE ANY CODE BELOW TOUCHES THE ATTACK
// TABLES IT NEEDS TO CLEAR/POPULATE THEM THOUGH!!!
//
- TIMED_EVAL_CALL(ctx, u64CyclesEvalPawns,
- pHash = _EvalPawns(ctx, &fDeferred));
+#ifdef EVAL_TIME
+ {
+ UINT64 u64PawnTimer = SystemReadTimeStampCounter();
+ pHash = _EvalPawns(ctx, &fDeferred);
+ {
+ UINT64 u64Elapsed = SystemReadTimeStampCounter() - u64PawnTimer;
+ ctx->sCounters.tree.u64CyclesEvalPawns += u64Elapsed;
+ if (fDeferred)
+ {
+ ctx->sCounters.tree.u64CyclesEvalPawnsHit += u64Elapsed;
+ ctx->sCounters.tree.u64CountEvalPawnsHit++;
+ }
+ else
+ {
+ ctx->sCounters.tree.u64CyclesEvalPawnsMiss += u64Elapsed;
+ ctx->sCounters.tree.u64CountEvalPawnsMiss++;
+ }
+ }
+ }
+#else
+ pHash = _EvalPawns(ctx, &fDeferred);
+#endif
ASSERT(NULL != pHash);
ASSERT(IS_VALID_FLAG(fDeferred));
pos->iScore[WHITE] += pHash->iScore[WHITE];
diff --git a/src/root.c b/src/root.c
index 5ce4c9e..0ab42f7 100755
--- a/src/root.c
+++ b/src/root.c
@@ -614,6 +614,22 @@ Return value:
Trace(" attack-table pop: %5.1f%% unaccounted: %5.1f%%\n",
(u64Total ? (100.0 * (double)u64AttackPop / (double)u64Total) : 0.0),
(u64Total ? (100.0 * (double)u64Unaccounted / (double)u64Total) : 0.0));
+ {
+ UINT64 u64PHits = ctx->sCounters.tree.u64CountEvalPawnsHit;
+ UINT64 u64PMisses = ctx->sCounters.tree.u64CountEvalPawnsMiss;
+ UINT64 u64PHitCycles = ctx->sCounters.tree.u64CyclesEvalPawnsHit;
+ UINT64 u64PMissCycles = ctx->sCounters.tree.u64CyclesEvalPawnsMiss;
+ Trace(" pawn hash: %" COMPILER_LONGLONG_UNSIGNED_FORMAT
+ " hits (avg %.1f cyc), %" COMPILER_LONGLONG_UNSIGNED_FORMAT
+ " misses (avg %.1f cyc), hit rate %.2f%%\n",
+ u64PHits,
+ (u64PHits ? (double)u64PHitCycles / (double)u64PHits : 0.0),
+ u64PMisses,
+ (u64PMisses ? (double)u64PMissCycles / (double)u64PMisses : 0.0),
+ ((u64PHits + u64PMisses) ?
+ (100.0 * (double)u64PHits / (double)(u64PHits + u64PMisses)) :
+ 0.0));
+ }
}
#endif
#endif