Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 66 additions & 4 deletions library/src/calc_tables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@

#include "calc_tables.hpp"
#include <algorithm>
#include <limits>
#include <array>
#include <chrono>
#include <limits>
#include <numeric>
#include <utility>
#include <vector>

#include <lookup_tables/lookup_tables.hpp>
Expand Down Expand Up @@ -578,6 +580,21 @@ int STDCALL CalcAllTablesX(
DdTableResults * results,
ParResults * par,
int maxThreads)
{
return calc_all_tables_x(
numDeals, deals, mode, trumpFilter, results, par, maxThreads, nullptr);
}


auto calc_all_tables_x(
int numDeals,
DdTableDeal const * deals,
int mode,
int const trumpFilter[DDS_STRAINS],
DdTableResults * results,
ParResults * par,
int maxThreads,
std::vector<int> * strain_times_us) -> int
{
// C ABI: exceptions must not unwind into a foreign caller (UB). Heap
// allocations below (and parallel_all_boards_n) may throw; map any throw
Expand All @@ -587,7 +604,11 @@ int STDCALL CalcAllTablesX(
if (numDeals < 0)
return RETURN_TOO_MANY_TABLES;
if (numDeals == 0)
{
if (strain_times_us != nullptr)
strain_times_us->clear();
return RETURN_NO_FAULT;
}
if (deals == nullptr || results == nullptr || trumpFilter == nullptr)
return RETURN_UNKNOWN_FAULT;

Expand Down Expand Up @@ -616,6 +637,9 @@ int STDCALL CalcAllTablesX(
const int nboards = numDeals * included;
std::vector<Deal> boards(static_cast<unsigned>(nboards));
std::vector<std::array<int, DDS_HANDS>> scores(static_cast<unsigned>(nboards));
std::vector<int> local_strain_times;
if (strain_times_us != nullptr)
local_strain_times.assign(static_cast<unsigned>(nboards), 0);

int ind = 0;
for (int m = 0; m < numDeals; m++)
Expand Down Expand Up @@ -663,16 +687,34 @@ int STDCALL CalcAllTablesX(
const int err = parallel_all_boards_n(nboards, nthreads,
[&](const int worker_id, const int bno) -> int {
(void)worker_id;
return calc_single_deal_scores(
if (strain_times_us == nullptr)
{
return calc_single_deal_scores(
dds::internal::worker_solver_context(),
boards[static_cast<unsigned>(bno)],
-1, 1, 1,
scores[static_cast<unsigned>(bno)].data());
}

const auto t0 = std::chrono::steady_clock::now();
const int res = calc_single_deal_scores(
dds::internal::worker_solver_context(),
boards[static_cast<unsigned>(bno)],
-1, 1, 1,
scores[static_cast<unsigned>(bno)].data());
const auto dur = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::steady_clock::now() - t0).count();
local_strain_times[static_cast<unsigned>(bno)] =
saturate_board_time_us(dur);
return res;
},
order.empty() ? nullptr : &order);
if (err != RETURN_NO_FAULT)
return err;

if (strain_times_us != nullptr)
*strain_times_us = std::move(local_strain_times);

for (int m = 0; m < numDeals; m++)
{
const int tricks = remaining_tricks_from_holdings(deals[m].cards);
Expand Down Expand Up @@ -717,14 +759,33 @@ int STDCALL CalcAllTablesPBNX(
DdTableResults * results,
ParResults * par,
int maxThreads)
{
return calc_all_tables_pbn_x(
numDeals, deals, mode, trumpFilter, results, par, maxThreads, nullptr);
}


auto calc_all_tables_pbn_x(
int numDeals,
DdTableDealPBN const * deals,
int mode,
int const trumpFilter[DDS_STRAINS],
DdTableResults * results,
ParResults * par,
int maxThreads,
std::vector<int> * strain_times_us) -> int
{
// C ABI: same catch-all contract as CalcAllTablesX / dds_c_api.cpp.
try
{
if (numDeals < 0)
return RETURN_TOO_MANY_TABLES;
if (numDeals == 0)
{
if (strain_times_us != nullptr)
strain_times_us->clear();
return RETURN_NO_FAULT;
}
if (deals == nullptr || results == nullptr || trumpFilter == nullptr)
return RETURN_UNKNOWN_FAULT;

Expand All @@ -742,8 +803,9 @@ int STDCALL CalcAllTablesPBNX(
return RETURN_PBN_FAULT;
}

return CalcAllTablesX(
numDeals, binary.data(), mode, trumpFilter, results, par, maxThreads);
return calc_all_tables_x(
numDeals, binary.data(), mode, trumpFilter, results, par, maxThreads,
strain_times_us);
}
catch (...)
{
Expand Down
32 changes: 32 additions & 0 deletions library/src/calc_tables.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,35 @@ auto remaining_tricks_from_holdings(
auto declarer_tricks_from_leader_score(
int remaining_tricks,
int leader_side_score) -> int;

/**
* @brief Unbounded CalcAllTables with optional per-strain-board timings.
*
* Same behavior as CalcAllTablesX. When @p strain_times_us is non-null, it is
* resized to `numDeals * included_strains` and filled with microseconds spent
* in each strain-board solve (batch-local board index order). A successful
* `numDeals == 0` call clears @p strain_times_us so reused vectors cannot keep
* stale timings.
*/
auto calc_all_tables_x(
int numDeals,
DdTableDeal const * deals,
int mode,
int const trumpFilter[DDS_STRAINS],
DdTableResults * results,
ParResults * par,
int maxThreads,
std::vector<int> * strain_times_us = nullptr) -> int;

/**
* @brief PBN variant of calc_all_tables_x.
*/
auto calc_all_tables_pbn_x(
int numDeals,
DdTableDealPBN const * deals,
int mode,
int const trumpFilter[DDS_STRAINS],
DdTableResults * results,
ParResults * par,
int maxThreads,
std::vector<int> * strain_times_us = nullptr) -> int;
6 changes: 4 additions & 2 deletions library/tests/args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,10 @@ void usage(
" memory via SolverConfig instead of this option.)\n" <<
"\n" <<
"-r, --report Print per-deal timings in ms (two decimals) for every\n" <<
" hand in the input (solve mode), longest first, plus\n" <<
" a min/max/mean/median/stddev summary.\n" <<
" hand in the input (solve and calc modes), longest\n" <<
" first, plus a min/max/mean/median/stddev summary.\n" <<
" For calc, each deal time is the sum of its strain-\n" <<
" board solve times.\n" <<
"\n" <<
endl;
}
Expand Down
22 changes: 19 additions & 3 deletions library/tests/loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "cst.hpp"
#include "dtest_parallel.hpp"
#include "report_board_timings.hpp"
#include <calc_tables.hpp>
#include <solve_board.hpp>
#include "system/scheduler.hpp"

Expand Down Expand Up @@ -135,7 +136,8 @@ auto loop_calc(
DealPBN * deal_list,
DdTableResults * table_list,
const int number,
const int stepsize) -> bool
const int stepsize,
std::vector<std::pair<int, int>>* board_times) -> bool
{
// dtest harness progress only: call CalcAllTablesPBNX repeatedly with
// `stepsize` deals (typically MAXNOOFBOARDS). Each call still expands to
Expand Down Expand Up @@ -170,8 +172,16 @@ auto loop_calc(
timer.start(count);
const int workload = count * strain_count;
const int threads = dtest_effective_threads(options.num_threads_, workload);
const int ret = CalcAllTablesPBNX(
count, deals.data(), -1, filter, results.data(), nullptr, threads);
std::vector<int> strain_times;
const int ret = calc_all_tables_pbn_x(
count,
deals.data(),
-1,
filter,
results.data(),
nullptr,
threads,
board_times != nullptr ? &strain_times : nullptr);
if (ret != RETURN_NO_FAULT)
{
timer.end();
Expand All @@ -182,6 +192,12 @@ auto loop_calc(
}
timer.end();

if (board_times != nullptr)
{
append_calc_batch_deal_times(
*board_times, strain_times, strain_count, i);
}

#ifdef BATCHTIMES
timer.print_running(i + count, number);
#endif
Expand Down
6 changes: 5 additions & 1 deletion library/tests/loop.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,16 @@ auto loop_solve(
/// @param table_list Expected DD table results
/// @param number Number of deals in the test set
/// @param stepsize Deals per CalcAllTablesPBNX batch (typically `MAXNOOFBOARDS`)
/// @param board_times When non-null, appends per-deal timings for every batch
/// with file-relative deal indices (for `dtest -r`); each deal's time
/// is the sum of its strain-board solve times
/// @return false on DDS API fault or first expected-result mismatch
auto loop_calc(
DealPBN * deal_list,
DdTableResults * table_list,
const int number,
const int stepsize) -> bool;
const int stepsize,
std::vector<std::pair<int, int>>* board_times = nullptr) -> bool;

/// PAR loop: calculate PAR scores for multiple deals.
/// @return false on DDS API fault or first expected-result mismatch
Expand Down
21 changes: 21 additions & 0 deletions library/tests/loop_failure_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,27 @@ TEST_F(LoopFailureTest, CalcMismatchInLaterBatchUsesAbsoluteIndex)
EXPECT_EQ(out.find("loop_calc: j 0:"), std::string::npos);
}

TEST_F(LoopFailureTest, CalcReportCollectsPerDealTimesAcrossBatches)
{
// dtest -r for -s calc must publish one (file_index, time_us) per deal,
// remapping batch-local strain aggregates across stepsize chunks.
auto hands = load_hands(
"loop_calc_report_times.txt", two_deal_body(kDealBody, kDealBody));
ASSERT_EQ(hands.number, 2);

std::vector<std::pair<int, int>> board_times;
testing::internal::CaptureStdout();
ASSERT_TRUE(
loop_calc(hands.deal_list, hands.table_list, 2, 1, &board_times));
(void)testing::internal::GetCapturedStdout();

ASSERT_EQ(board_times.size(), 2u);
EXPECT_EQ(board_times[0].first, 0);
EXPECT_EQ(board_times[1].first, 1);
EXPECT_GT(board_times[0].second, 0);
EXPECT_GT(board_times[1].second, 0);
}

TEST_F(LoopFailureTest, PlayStopsOnFirstExpectedMismatch)
{
auto wrong = std::string(kDealBody);
Expand Down
32 changes: 32 additions & 0 deletions library/tests/report_board_timings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <cstdlib>
#include <iomanip>
#include <ios>
#include <limits>
#include <ostream>
#include <sstream>
#include <utility>
Expand Down Expand Up @@ -156,3 +157,34 @@ void append_batch_board_times(
for (const auto& p : batch_times)
accumulated.emplace_back(p.first + file_offset, p.second);
}

void append_calc_batch_deal_times(
std::vector<std::pair<int, int>>& accumulated,
const std::vector<int>& strain_times_us,
int strains_per_deal,
int file_offset)
{
if (strains_per_deal <= 0 || strain_times_us.empty())
return;

const int deal_count =
static_cast<int>(strain_times_us.size() / static_cast<std::size_t>(strains_per_deal));
accumulated.reserve(accumulated.size() + static_cast<std::size_t>(deal_count));

constexpr auto kMax = static_cast<long long>(std::numeric_limits<int>::max());
for (int d = 0; d < deal_count; ++d)
{
long long sum_us = 0;
const int base = d * strains_per_deal;
for (int s = 0; s < strains_per_deal; ++s)
{
sum_us += strain_times_us[static_cast<std::size_t>(base + s)];
if (sum_us >= kMax)
{
sum_us = kMax;
break;
}
}
accumulated.emplace_back(d + file_offset, static_cast<int>(sum_us));
}
}
12 changes: 12 additions & 0 deletions library/tests/report_board_timings.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,15 @@ void append_batch_board_times(
std::vector<std::pair<int, int>>& accumulated,
const std::vector<std::pair<int, int>>& batch_times,
int file_offset);

/// Append one calc-batch's per-strain timings as per-deal file indices.
///
/// Calc expands each deal into @p strains_per_deal boards. @p strain_times_us
/// is a flat batch-local vector of those board times (microseconds). Each
/// deal's time is the saturated sum of its strain boards, stored as
/// `(deal_index_in_batch + file_offset, time_us)`.
void append_calc_batch_deal_times(
std::vector<std::pair<int, int>>& accumulated,
const std::vector<int>& strain_times_us,
int strains_per_deal,
int file_offset);
Loading