chore: archive early implementations and chunk checker tools
This commit is contained in:
parent
7a06f94577
commit
69f7f7a34b
9 changed files with 1408 additions and 0 deletions
523
codex/slime_cuda.cu
Normal file
523
codex/slime_cuda.cu
Normal file
|
|
@ -0,0 +1,523 @@
|
|||
#include <cuda_runtime.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr uint64_t kJavaMultiplier = 0x5DEECE66DULL;
|
||||
constexpr uint64_t kJavaAddend = 0xBULL;
|
||||
constexpr uint64_t kJavaMask = (1ULL << 48) - 1;
|
||||
constexpr uint64_t kSlimeXor = 987234911ULL;
|
||||
constexpr int32_t kTileW = 16;
|
||||
constexpr int32_t kTileH = 16;
|
||||
constexpr int32_t kMaskFastPathMaxN = 49;
|
||||
|
||||
struct Args {
|
||||
int64_t startSeed = 0;
|
||||
int64_t endSeed = 1'000'000;
|
||||
int32_t n = 3;
|
||||
int32_t required = -1;
|
||||
int32_t radius = 100;
|
||||
int64_t seedBatch = 0;
|
||||
int32_t maxResults = 32;
|
||||
bool allLocations = false;
|
||||
};
|
||||
|
||||
struct Hit {
|
||||
int64_t seed;
|
||||
int32_t x;
|
||||
int32_t z;
|
||||
int32_t count;
|
||||
};
|
||||
|
||||
void checkCuda(cudaError_t error, const char* expr, const char* file, int line) {
|
||||
if (error == cudaSuccess) {
|
||||
return;
|
||||
}
|
||||
std::cerr << file << ':' << line << ": CUDA call failed: " << expr
|
||||
<< ": " << cudaGetErrorString(error) << '\n';
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
#define CUDA_CHECK(expr) checkCuda((expr), #expr, __FILE__, __LINE__)
|
||||
|
||||
[[noreturn]] void usage(int exitCode = 1) {
|
||||
std::cerr
|
||||
<< "Usage: slime_cuda [args...]\n"
|
||||
<< " -s <startSeed> First world seed, inclusive (default: 0)\n"
|
||||
<< " -e <endSeed> Last world seed, exclusive (default: 1000000)\n"
|
||||
<< " -n <chunkSize> N for the NxN window (default: 3)\n"
|
||||
<< " -d <numChunks> Required slime chunks in the window (default: N*N)\n"
|
||||
<< " -r <radius> Search start positions from -r..r on both axes (default: 100)\n"
|
||||
<< " --batch <seeds> Seeds per kernel launch (default: max(total/100, 1000000))\n"
|
||||
<< " --max-results <n> Print at most n hits, while still counting all hits (default: 32)\n"
|
||||
<< " --all-locations Count every matching location, not just the first per seed\n"
|
||||
<< " --help Show this help\n";
|
||||
std::exit(exitCode);
|
||||
}
|
||||
|
||||
const char* requireValue(int argc, char** argv, int& i) {
|
||||
if (i + 1 >= argc) {
|
||||
usage();
|
||||
}
|
||||
return argv[++i];
|
||||
}
|
||||
|
||||
Args parseArgs(int argc, char** argv) {
|
||||
Args args;
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
const std::string arg = argv[i];
|
||||
if (arg == "--help") {
|
||||
usage(0);
|
||||
} else if (arg == "-s") {
|
||||
args.startSeed = std::stoll(requireValue(argc, argv, i));
|
||||
} else if (arg == "-e") {
|
||||
args.endSeed = std::stoll(requireValue(argc, argv, i));
|
||||
} else if (arg == "-n") {
|
||||
args.n = std::stoi(requireValue(argc, argv, i));
|
||||
} else if (arg == "-d") {
|
||||
args.required = std::stoi(requireValue(argc, argv, i));
|
||||
} else if (arg == "-r") {
|
||||
args.radius = std::stoi(requireValue(argc, argv, i));
|
||||
} else if (arg == "--batch") {
|
||||
args.seedBatch = std::stoll(requireValue(argc, argv, i));
|
||||
} else if (arg == "--max-results") {
|
||||
args.maxResults = std::stoi(requireValue(argc, argv, i));
|
||||
} else if (arg == "--all-locations") {
|
||||
args.allLocations = true;
|
||||
} else {
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
const int64_t cells = int64_t(args.n) * args.n;
|
||||
if (args.required < 0) {
|
||||
args.required = static_cast<int32_t>(cells);
|
||||
}
|
||||
if (args.startSeed >= args.endSeed || args.n <= 0 || args.radius < 0 ||
|
||||
args.seedBatch < 0 || args.maxResults < 0 || cells > INT32_MAX ||
|
||||
args.required <= 0 || args.required > cells) {
|
||||
usage();
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
__host__ __device__ __forceinline__ uint64_t javaIntAsLongBits(uint32_t value) {
|
||||
return static_cast<uint64_t>(static_cast<int64_t>(static_cast<int32_t>(value)));
|
||||
}
|
||||
|
||||
__host__ __device__ __forceinline__ uint64_t coordXTerm(int32_t chunkX) {
|
||||
const uint32_t x = static_cast<uint32_t>(chunkX);
|
||||
return javaIntAsLongBits(x * x * 4987142u) + javaIntAsLongBits(x * 5947611u);
|
||||
}
|
||||
|
||||
__host__ __device__ __forceinline__ uint64_t coordZTerm(int32_t chunkZ) {
|
||||
const uint32_t z = static_cast<uint32_t>(chunkZ);
|
||||
return javaIntAsLongBits(z * z) * 4392871ULL + javaIntAsLongBits(z * 389711u);
|
||||
}
|
||||
|
||||
__host__ __device__ __forceinline__ uint64_t coordTerm(int32_t chunkX, int32_t chunkZ) {
|
||||
return coordXTerm(chunkX) + coordZTerm(chunkZ);
|
||||
}
|
||||
|
||||
__device__ __forceinline__ bool isSlimeFromTerm(uint64_t worldSeedBits, uint64_t term) {
|
||||
uint64_t randomSeed = ((worldSeedBits + term) ^ kSlimeXor ^ kJavaMultiplier) & kJavaMask;
|
||||
|
||||
for (;;) {
|
||||
randomSeed = (randomSeed * kJavaMultiplier + kJavaAddend) & kJavaMask;
|
||||
const uint32_t bits = static_cast<uint32_t>(randomSeed >> 17);
|
||||
const uint32_t value = bits % 10u;
|
||||
if (bits - value + 9u <= 0x7FFFFFFFu) {
|
||||
return value == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
__device__ __forceinline__ bool isNextInt10ZeroFast(uint32_t bits) {
|
||||
const uint32_t product = bits * 0xCCCCCCCDu;
|
||||
const uint32_t rotated = (product >> 1) | (product << 31);
|
||||
return rotated <= 429496729u;
|
||||
}
|
||||
|
||||
__device__ __forceinline__ bool isSlimeFromSplitTerms(uint64_t worldSeedBits, uint64_t xTerm, uint64_t zTerm) {
|
||||
uint64_t randomSeed = ((worldSeedBits + xTerm + zTerm) ^ kSlimeXor ^ kJavaMultiplier) & kJavaMask;
|
||||
randomSeed = (randomSeed * kJavaMultiplier + kJavaAddend) & kJavaMask;
|
||||
return isNextInt10ZeroFast(static_cast<uint32_t>(randomSeed >> 17));
|
||||
}
|
||||
|
||||
__device__ __forceinline__ bool isSlimeChunkInline(uint64_t worldSeedBits, int32_t chunkX, int32_t chunkZ) {
|
||||
return isSlimeFromSplitTerms(worldSeedBits, coordXTerm(chunkX), coordZTerm(chunkZ));
|
||||
}
|
||||
|
||||
std::vector<uint64_t> makeCoordTerms(int32_t radius, int32_t n) {
|
||||
const int32_t window = radius * 2 + 1;
|
||||
const int32_t chunkWidth = window + n - 1;
|
||||
std::vector<uint64_t> terms(size_t(chunkWidth) * chunkWidth);
|
||||
for (int32_t z = 0; z < chunkWidth; ++z) {
|
||||
for (int32_t x = 0; x < chunkWidth; ++x) {
|
||||
terms[size_t(z) * chunkWidth + x] = coordTerm(x - radius, z - radius);
|
||||
}
|
||||
}
|
||||
return terms;
|
||||
}
|
||||
|
||||
__device__ void recordHit(
|
||||
int32_t onePerSeed,
|
||||
int32_t* seedFound,
|
||||
int64_t seedOffset,
|
||||
unsigned long long* totalHits,
|
||||
Hit* hits,
|
||||
int32_t maxHits,
|
||||
int64_t seed,
|
||||
int32_t x,
|
||||
int32_t z,
|
||||
int32_t count
|
||||
) {
|
||||
if (onePerSeed != 0) {
|
||||
if (atomicCAS(seedFound + seedOffset, 0, 1) != 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned long long index = atomicAdd(totalHits, 1ULL);
|
||||
if (index < static_cast<unsigned long long>(maxHits)) {
|
||||
hits[index] = {seed, x, z, count};
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void searchTilesKernel(
|
||||
int64_t startSeed,
|
||||
int64_t seedCount,
|
||||
const uint64_t* terms,
|
||||
int32_t chunkWidth,
|
||||
int32_t window,
|
||||
int32_t n,
|
||||
int32_t required,
|
||||
int32_t radius,
|
||||
int32_t tilesPerSeed,
|
||||
int32_t tilesX,
|
||||
int32_t onePerSeed,
|
||||
int32_t* seedFound,
|
||||
unsigned long long* totalHits,
|
||||
Hit* hits,
|
||||
int32_t maxHits
|
||||
) {
|
||||
extern __shared__ uint8_t tile[];
|
||||
__shared__ unsigned long long horizontalRows[kTileH + kMaskFastPathMaxN - 1];
|
||||
|
||||
const int64_t linearBlock = blockIdx.x;
|
||||
const int64_t seedOffset = linearBlock / tilesPerSeed;
|
||||
if (seedOffset >= seedCount) {
|
||||
return;
|
||||
}
|
||||
if (onePerSeed != 0 && seedFound[seedOffset] != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int32_t tileId = static_cast<int32_t>(linearBlock - seedOffset * tilesPerSeed);
|
||||
const int32_t tileBaseX = (tileId % tilesX) * kTileW;
|
||||
const int32_t tileBaseZ = (tileId / tilesX) * kTileH;
|
||||
const int32_t actualW = (window - tileBaseX) < kTileW ? (window - tileBaseX) : kTileW;
|
||||
const int32_t actualH = (window - tileBaseZ) < kTileH ? (window - tileBaseZ) : kTileH;
|
||||
const int32_t sharedW = actualW + n - 1;
|
||||
const int32_t sharedH = actualH + n - 1;
|
||||
const int32_t sharedCells = sharedW * sharedH;
|
||||
const int64_t seed = startSeed + seedOffset;
|
||||
const uint64_t seedBits = static_cast<uint64_t>(seed);
|
||||
|
||||
const int32_t localThread = threadIdx.y * blockDim.x + threadIdx.x;
|
||||
const int32_t threadCount = blockDim.x * blockDim.y;
|
||||
for (int32_t index = localThread; index < sharedCells; index += threadCount) {
|
||||
const int32_t localX = index % sharedW;
|
||||
const int32_t localZ = index / sharedW;
|
||||
const size_t termIndex = size_t(tileBaseZ + localZ) * chunkWidth + tileBaseX + localX;
|
||||
tile[index] = isSlimeFromTerm(seedBits, terms[termIndex]) ? 1 : 0;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
if (required == n * n && n <= kMaskFastPathMaxN) {
|
||||
for (int32_t row = localThread; row < sharedH; row += threadCount) {
|
||||
unsigned long long rowMask = 0;
|
||||
for (int32_t x = 0; x < sharedW; ++x) {
|
||||
rowMask |= static_cast<unsigned long long>(tile[row * sharedW + x]) << x;
|
||||
}
|
||||
unsigned long long mask = rowMask;
|
||||
for (int32_t shift = 1; shift < n; ++shift) {
|
||||
mask &= rowMask >> shift;
|
||||
}
|
||||
horizontalRows[row] = mask;
|
||||
}
|
||||
__syncthreads();
|
||||
|
||||
const int32_t localX = threadIdx.x;
|
||||
const int32_t localZ = threadIdx.y;
|
||||
if (localX < actualW && localZ < actualH) {
|
||||
unsigned long long mask = ~0ULL;
|
||||
for (int32_t dz = 0; dz < n; ++dz) {
|
||||
mask &= horizontalRows[localZ + dz];
|
||||
}
|
||||
if (((mask >> localX) & 1ULL) != 0) {
|
||||
recordHit(
|
||||
onePerSeed,
|
||||
seedFound,
|
||||
seedOffset,
|
||||
totalHits,
|
||||
hits,
|
||||
maxHits,
|
||||
seed,
|
||||
tileBaseX + localX - radius,
|
||||
tileBaseZ + localZ - radius,
|
||||
n * n
|
||||
);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const int32_t localX = threadIdx.x;
|
||||
const int32_t localZ = threadIdx.y;
|
||||
if (localX >= actualW || localZ >= actualH) {
|
||||
return;
|
||||
}
|
||||
if (onePerSeed != 0 && seedFound[seedOffset] != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t count = 0;
|
||||
int32_t remaining = n * n;
|
||||
for (int32_t dz = 0; dz < n; ++dz) {
|
||||
const int32_t rowBase = (localZ + dz) * sharedW + localX;
|
||||
for (int32_t dx = 0; dx < n; ++dx) {
|
||||
count += tile[rowBase + dx];
|
||||
--remaining;
|
||||
if (count + remaining < required) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (count >= required) {
|
||||
recordHit(
|
||||
onePerSeed,
|
||||
seedFound,
|
||||
seedOffset,
|
||||
totalHits,
|
||||
hits,
|
||||
maxHits,
|
||||
seed,
|
||||
tileBaseX + localX - radius,
|
||||
tileBaseZ + localZ - radius,
|
||||
count
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
__global__ void searchSeedsFullKernel(
|
||||
int64_t startSeed,
|
||||
int64_t seedCount,
|
||||
int32_t window,
|
||||
int32_t n,
|
||||
int32_t radius,
|
||||
unsigned long long* totalHits,
|
||||
Hit* hits,
|
||||
int32_t maxHits
|
||||
) {
|
||||
const int64_t seedOffset = int64_t(blockIdx.x) * blockDim.x + threadIdx.x;
|
||||
if (seedOffset >= seedCount) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int64_t seed = startSeed + seedOffset;
|
||||
const uint64_t seedBits = static_cast<uint64_t>(seed);
|
||||
|
||||
for (int32_t x = 0; x < window; ++x) {
|
||||
const int32_t chunkX = x - radius;
|
||||
const uint64_t firstXTerm = coordXTerm(chunkX);
|
||||
for (int32_t z = 0; z < window; ++z) {
|
||||
const int32_t chunkZ = z - radius;
|
||||
if (!isSlimeFromSplitTerms(seedBits, firstXTerm, coordZTerm(chunkZ))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool found = true;
|
||||
for (int32_t dx = 0; dx < n && found; ++dx) {
|
||||
const uint64_t xTerm = dx == 0 ? firstXTerm : coordXTerm(chunkX + dx);
|
||||
const int32_t firstDz = dx == 0 ? 1 : 0;
|
||||
for (int32_t dz = firstDz; dz < n; ++dz) {
|
||||
if (!isSlimeFromSplitTerms(seedBits, xTerm, coordZTerm(chunkZ + dz))) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
const unsigned long long index = atomicAdd(totalHits, 1ULL);
|
||||
if (index < static_cast<unsigned long long>(maxHits)) {
|
||||
hits[index] = {seed, chunkX, chunkZ, n * n};
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void printHits(const std::vector<Hit>& hits) {
|
||||
for (const Hit& hit : hits) {
|
||||
std::cout << "hit seed=" << hit.seed
|
||||
<< " x=" << hit.x
|
||||
<< " z=" << hit.z
|
||||
<< " count=" << hit.count << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
try {
|
||||
const Args args = parseArgs(argc, argv);
|
||||
const int32_t window = args.radius * 2 + 1;
|
||||
const int32_t chunkWidth = window + args.n - 1;
|
||||
const int32_t tilesX = (window + kTileW - 1) / kTileW;
|
||||
const int32_t tilesZ = (window + kTileH - 1) / kTileH;
|
||||
const int32_t tilesPerSeed = tilesX * tilesZ;
|
||||
const size_t dynamicSharedBytes = size_t(kTileW + args.n - 1) * (kTileH + args.n - 1);
|
||||
const size_t staticSharedBytes = size_t(kTileH + kMaskFastPathMaxN - 1) * sizeof(unsigned long long);
|
||||
const bool useSeedKernel = !args.allLocations && args.required == args.n * args.n;
|
||||
const int32_t seedThreadsPerBlock = 64;
|
||||
|
||||
int device = 0;
|
||||
cudaDeviceProp prop{};
|
||||
CUDA_CHECK(cudaGetDevice(&device));
|
||||
CUDA_CHECK(cudaGetDeviceProperties(&prop, device));
|
||||
if (!useSeedKernel && dynamicSharedBytes + staticSharedBytes > static_cast<size_t>(prop.sharedMemPerBlock)) {
|
||||
throw std::runtime_error("requested N needs more shared memory per block than this GPU allows");
|
||||
}
|
||||
|
||||
uint64_t* deviceTerms = nullptr;
|
||||
if (!useSeedKernel) {
|
||||
std::vector<uint64_t> hostTerms = makeCoordTerms(args.radius, args.n);
|
||||
CUDA_CHECK(cudaMalloc(&deviceTerms, hostTerms.size() * sizeof(uint64_t)));
|
||||
CUDA_CHECK(cudaMemcpy(deviceTerms, hostTerms.data(), hostTerms.size() * sizeof(uint64_t), cudaMemcpyHostToDevice));
|
||||
}
|
||||
|
||||
const int64_t totalSeeds = args.endSeed - args.startSeed;
|
||||
const int64_t requestedBatch = args.seedBatch > 0
|
||||
? args.seedBatch
|
||||
: std::max(totalSeeds / 100, int64_t(1'000'000));
|
||||
const int64_t maxSeedsPerLaunch = useSeedKernel
|
||||
? int64_t(INT_MAX) * seedThreadsPerBlock
|
||||
: std::max<int64_t>(1, INT_MAX / std::max<int32_t>(1, tilesPerSeed));
|
||||
const int64_t maxBatchSeeds = std::min({requestedBatch, maxSeedsPerLaunch, totalSeeds});
|
||||
|
||||
int32_t* deviceSeedFound = nullptr;
|
||||
if (!args.allLocations && !useSeedKernel) {
|
||||
CUDA_CHECK(cudaMalloc(&deviceSeedFound, size_t(maxBatchSeeds) * sizeof(int32_t)));
|
||||
}
|
||||
|
||||
unsigned long long* deviceHitCount = nullptr;
|
||||
CUDA_CHECK(cudaMalloc(&deviceHitCount, sizeof(unsigned long long)));
|
||||
|
||||
Hit* deviceHits = nullptr;
|
||||
if (args.maxResults > 0) {
|
||||
CUDA_CHECK(cudaMalloc(&deviceHits, size_t(args.maxResults) * sizeof(Hit)));
|
||||
}
|
||||
|
||||
std::cout << "CUDA search on " << prop.name << ": seeds [" << args.startSeed << ", "
|
||||
<< args.endSeed << "), N=" << args.n << ", required=" << args.required
|
||||
<< ", radius=" << args.radius << ", tile=" << kTileW << "x" << kTileH
|
||||
<< ", mode=" << (useSeedKernel ? "seed-early-exit" : "tile-shared")
|
||||
<< (args.allLocations ? ", all locations\n" : ", first location per seed\n");
|
||||
|
||||
const auto started = std::chrono::steady_clock::now();
|
||||
uint64_t totalHits = 0;
|
||||
int32_t printed = 0;
|
||||
|
||||
for (int64_t seed = args.startSeed; seed < args.endSeed;) {
|
||||
const int64_t remainingSeeds = args.endSeed - seed;
|
||||
const int64_t batchSeeds = std::min(maxBatchSeeds, remainingSeeds);
|
||||
const int32_t remainingPrints = std::max<int32_t>(0, args.maxResults - printed);
|
||||
|
||||
CUDA_CHECK(cudaMemset(deviceHitCount, 0, sizeof(unsigned long long)));
|
||||
if (!args.allLocations && !useSeedKernel) {
|
||||
CUDA_CHECK(cudaMemset(deviceSeedFound, 0, size_t(batchSeeds) * sizeof(int32_t)));
|
||||
}
|
||||
|
||||
if (useSeedKernel) {
|
||||
const int64_t blocks = (batchSeeds + seedThreadsPerBlock - 1) / seedThreadsPerBlock;
|
||||
searchSeedsFullKernel<<<static_cast<unsigned int>(blocks), seedThreadsPerBlock>>>(
|
||||
seed,
|
||||
batchSeeds,
|
||||
window,
|
||||
args.n,
|
||||
args.radius,
|
||||
deviceHitCount,
|
||||
deviceHits,
|
||||
remainingPrints
|
||||
);
|
||||
} else {
|
||||
const int64_t blocks = batchSeeds * tilesPerSeed;
|
||||
searchTilesKernel<<<static_cast<unsigned int>(blocks), dim3(kTileW, kTileH), dynamicSharedBytes>>>(
|
||||
seed,
|
||||
batchSeeds,
|
||||
deviceTerms,
|
||||
chunkWidth,
|
||||
window,
|
||||
args.n,
|
||||
args.required,
|
||||
args.radius,
|
||||
tilesPerSeed,
|
||||
tilesX,
|
||||
args.allLocations ? 0 : 1,
|
||||
deviceSeedFound,
|
||||
deviceHitCount,
|
||||
deviceHits,
|
||||
remainingPrints
|
||||
);
|
||||
}
|
||||
CUDA_CHECK(cudaGetLastError());
|
||||
CUDA_CHECK(cudaDeviceSynchronize());
|
||||
|
||||
unsigned long long batchHits = 0;
|
||||
CUDA_CHECK(cudaMemcpy(&batchHits, deviceHitCount, sizeof(batchHits), cudaMemcpyDeviceToHost));
|
||||
totalHits += batchHits;
|
||||
|
||||
const int32_t copyHits = static_cast<int32_t>(
|
||||
std::min<unsigned long long>(batchHits, static_cast<unsigned long long>(remainingPrints))
|
||||
);
|
||||
if (copyHits > 0) {
|
||||
std::vector<Hit> hostHits(copyHits);
|
||||
CUDA_CHECK(cudaMemcpy(hostHits.data(), deviceHits, size_t(copyHits) * sizeof(Hit), cudaMemcpyDeviceToHost));
|
||||
printHits(hostHits);
|
||||
printed += copyHits;
|
||||
}
|
||||
|
||||
seed += batchSeeds;
|
||||
std::cerr << "\rchecked " << (seed - args.startSeed) << "/" << totalSeeds << " seeds" << std::flush;
|
||||
}
|
||||
std::cerr << '\n';
|
||||
|
||||
const auto finished = std::chrono::steady_clock::now();
|
||||
const std::chrono::duration<double> elapsed = finished - started;
|
||||
std::cout << "checked " << totalSeeds << " seeds, hits=" << totalHits
|
||||
<< ", elapsed=" << elapsed.count() << "s\n";
|
||||
|
||||
CUDA_CHECK(cudaFree(deviceHits));
|
||||
CUDA_CHECK(cudaFree(deviceHitCount));
|
||||
CUDA_CHECK(cudaFree(deviceSeedFound));
|
||||
if (deviceTerms != nullptr) {
|
||||
CUDA_CHECK(cudaFree(deviceTerms));
|
||||
}
|
||||
return 0;
|
||||
} catch (const std::exception& ex) {
|
||||
std::cerr << "error: " << ex.what() << '\n';
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue