Implement deterministic shuffle with configurable g++/libstdc++ compatibility. (#369)
This commit is contained in:
@@ -27,6 +27,32 @@ if(ENABLE_ASSERTIONS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(SHUFFLE_METHODS "g++10" "g++11" "stdshuffle")
|
||||
set(SHUFFLE_METHOD "" CACHE STRING "Choose the shuffle method (REQUIRED)")
|
||||
set_property(CACHE SHUFFLE_METHOD PROPERTY STRINGS ${SHUFFLE_METHODS})
|
||||
|
||||
if("${SHUFFLE_METHOD}" STREQUAL "")
|
||||
message(FATAL_ERROR
|
||||
"\n [ERROR]: SHUFFLE_METHOD is not set!\n"
|
||||
" You must specify it using: cmake -DSHUFFLE_METHOD=<value>\n"
|
||||
" Valid options are: ${SHUFFLE_METHODS}"
|
||||
)
|
||||
elseif(NOT SHUFFLE_METHOD IN_LIST SHUFFLE_METHODS)
|
||||
message(FATAL_ERROR
|
||||
"\n [ERROR]: Invalid SHUFFLE_METHOD: '${SHUFFLE_METHOD}'\n"
|
||||
" Valid options are: ${SHUFFLE_METHODS}"
|
||||
)
|
||||
endif()
|
||||
|
||||
message(STATUS "Use Shuffle Method: ${SHUFFLE_METHOD}")
|
||||
if(SHUFFLE_METHOD STREQUAL "stdshuffle")
|
||||
add_compile_definitions(USE_STD_SHUFFLE)
|
||||
elseif(SHUFFLE_METHOD STREQUAL "g++10")
|
||||
add_compile_definitions(USE_GCC10_SHUFFLE)
|
||||
elseif(SHUFFLE_METHOD STREQUAL "g++11")
|
||||
add_compile_definitions(USE_GCC11_SHUFFLE)
|
||||
endif()
|
||||
|
||||
option(OVERRIDE_CXX_NEW_DELETE "Override C++ new/delete operator" OFF)
|
||||
option(SAVE_ALLOCATE_SIZE "Use more memory to save allocate size" OFF)
|
||||
|
||||
|
||||
@@ -100,15 +100,25 @@ Install other build prerequisites:
|
||||
|
||||
## Build 3FS
|
||||
|
||||
- Build 3FS in `build` folder:
|
||||
Build 3FS in `build` folder:
|
||||
|
||||
```
|
||||
cmake -S . -B build -DCMAKE_CXX_COMPILER=clang++-14 -DCMAKE_C_COMPILER=clang-14 -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
|
||||
cmake --build build -j 32
|
||||
```
|
||||
- Build 3FS use Docker
|
||||
- For TencentOS-4: `docker pull docker.io/tencentos/tencentos4-deepseek3fs-build:latest`
|
||||
- For OpenCloudOS-9: `docker pull docker.io/opencloudos/opencloudos9-deepseek3fs-build:latest`
|
||||
```bash
|
||||
# Replace <method> with 'g++10' or 'g++11' based on your environment
|
||||
cmake -S . -B build \
|
||||
-DCMAKE_CXX_COMPILER=clang++-14 -DCMAKE_C_COMPILER=clang-14 \
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
|
||||
-DSHUFFLE_METHOD=<method>
|
||||
cmake --build build -j 32
|
||||
```
|
||||
|
||||
Due to the historical use of `std::shuffle`, binaries compiled with different compiler versions (e.g., `g++10` vs. `g++11 +`) may be incompatible ([issue](https://github.com/deepseek-ai/3FS/issues/368)). To resolve this, you must explicitly specify `-DSHUFFLE_METHOD` during compilation to lock in a consistent shuffle algorithm:
|
||||
|
||||
- Existing Clusters: Use the method corresponding to the compiler version previously used to deploy the cluster (`g++10` or `g++11`).
|
||||
- New Clusters: You can choose either `g++10` or `g++11`. However, once the cluster is deployed, you must stay with the same configuration for all future builds to maintain compatibility.
|
||||
|
||||
### Build 3FS use Docker
|
||||
- For TencentOS-4: `docker pull docker.io/tencentos/tencentos4-deepseek3fs-build:latest`
|
||||
- For OpenCloudOS-9: `docker pull docker.io/opencloudos/opencloudos9-deepseek3fs-build:latest`
|
||||
|
||||
## Run a test cluster
|
||||
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <folly/Random.h>
|
||||
#include <folly/logging/xlog.h>
|
||||
#include <glog/logging.h>
|
||||
#include <numeric>
|
||||
#include <optional>
|
||||
#include <random>
|
||||
#include <vector>
|
||||
|
||||
#include "common/utils/Int128.h"
|
||||
|
||||
namespace hf3fs {
|
||||
|
||||
#if defined __UINT64_TYPE__ && defined __UINT32_TYPE__ && __SIZEOF_INT128__
|
||||
#else
|
||||
#error "error"
|
||||
#endif
|
||||
|
||||
#if (defined(USE_STD_SHUFFLE) + defined(USE_GCC10_SHUFFLE) + defined(USE_GCC11_SHUFFLE)) > 1
|
||||
#error "multiple shuffle method defined"
|
||||
#endif
|
||||
|
||||
#if defined(USE_STD_SHUFFLE)
|
||||
__attribute__((used)) inline constexpr char HF3FS_SHUFFLE_METHOD[] = "HF3FS_SHUFFLE_METHOD=STD_SHUFFLE";
|
||||
#elif defined(USE_GCC10_SHUFFLE)
|
||||
__attribute__((used)) inline constexpr char HF3FS_SHUFFLE_METHOD[] = "HF3FS_SHUFFLE_METHOD=GCC10_SHUFFLE";
|
||||
#elif defined(USE_GCC11_SHUFFLE)
|
||||
__attribute__((used)) inline constexpr char HF3FS_SHUFFLE_METHOD[] = "HF3FS_SHUFFLE_METHOD=GCC11_SHUFFLE";
|
||||
#else
|
||||
#error "shuffle method not defined"
|
||||
#endif
|
||||
|
||||
#if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE)
|
||||
#else
|
||||
#error "not libstdc++"
|
||||
#endif
|
||||
|
||||
// Lemire's nearly divisionless algorithm.
|
||||
// Returns an unbiased random number from __g downscaled to [0,__range)
|
||||
// using an unsigned type _Wp twice as wide as unsigned type _Up.
|
||||
// https://github.com/gcc-mirror/gcc/blob/a9c5f33f3a88fb9f6324beced1ccf30a6740f094/libstdc%2B%2B-v3/include/bits/uniform_int_dist.h#L252
|
||||
inline uint64_t fast_range(std::mt19937_64 &urng, uint64_t range) {
|
||||
// reference: Fast Random Integer Generation in an Interval
|
||||
// ACM Transactions on Modeling and Computer Simulation 29 (1), 2019
|
||||
// https://arxiv.org/abs/1805.10941
|
||||
uint128_t product = uint128_t(urng()) * uint128_t(range);
|
||||
uint64_t low = uint64_t(product);
|
||||
if (low < range) {
|
||||
uint64_t threshold = -range % range;
|
||||
while (low < threshold) {
|
||||
product = uint128_t(urng()) * uint128_t(range);
|
||||
low = uint64_t(product);
|
||||
}
|
||||
}
|
||||
|
||||
return product >> 64;
|
||||
}
|
||||
|
||||
// https://github.com/gcc-mirror/gcc/blob/e10dc8fa17ac633dfeca38cadfe0ba974af7e5a3/libstdc%2B%2B-v3/include/bits/uniform_int_dist.h#L288
|
||||
template <const bool GCC11>
|
||||
uint64_t gcc_uniform_int(uint64_t a, uint64_t b, std::mt19937_64 &urng) {
|
||||
static_assert(std::is_same_v<std::mt19937_64::result_type, uint64_t>);
|
||||
|
||||
constexpr uint64_t urngrange = std::mt19937_64::max() - std::mt19937_64::min();
|
||||
static_assert(std::mt19937_64::min() == 0);
|
||||
static_assert(std::mt19937_64::max() == std::numeric_limits<uint64_t>::max());
|
||||
static_assert(urngrange == std::numeric_limits<uint64_t>::max());
|
||||
|
||||
const uint64_t urange = b - a;
|
||||
assert(urngrange >= urange);
|
||||
|
||||
uint64_t ret;
|
||||
if (urngrange > urange) {
|
||||
if constexpr (GCC11) {
|
||||
// downscaling after g++11
|
||||
const uint64_t uerange = urange + 1; // urange can be zero
|
||||
ret = fast_range(urng, uerange);
|
||||
} else {
|
||||
// downscaling for g++10
|
||||
const uint64_t uerange = urange + 1; // __urange can be zero
|
||||
const uint64_t scaling = urngrange / uerange;
|
||||
const uint64_t past = uerange * scaling;
|
||||
do {
|
||||
ret = uint64_t(urng());
|
||||
} while (ret >= past);
|
||||
ret /= scaling;
|
||||
}
|
||||
} else {
|
||||
ret = urng();
|
||||
}
|
||||
|
||||
return ret + a;
|
||||
}
|
||||
|
||||
// https://github.com/gcc-mirror/gcc/blob/e10dc8fa17ac633dfeca38cadfe0ba974af7e5a3/libstdc%2B%2B-v3/include/bits/stl_algo.h#L3646
|
||||
template <const bool GCC11>
|
||||
std::pair<uint64_t, uint64_t> gen_two_uniform_ints(uint64_t b0, uint64_t b1, std::mt19937_64 &urng) {
|
||||
const uint64_t x = gcc_uniform_int<GCC11>(0, (b0 * b1) - 1, urng);
|
||||
return std::make_pair(x / b1, x % b1);
|
||||
}
|
||||
|
||||
// https://github.com/gcc-mirror/gcc/blob/e10dc8fa17ac633dfeca38cadfe0ba974af7e5a3/libstdc%2B%2B-v3/include/bits/stl_algo.h#L3688
|
||||
template <typename T, const bool GCC11>
|
||||
void gcc_shuffle(std::vector<T> &vec, uint64_t mt19937_64_seed) {
|
||||
if (vec.empty()) return;
|
||||
|
||||
std::mt19937_64 urng(mt19937_64_seed);
|
||||
constexpr uint64_t urngrange = urng.max() - urng.min();
|
||||
static_assert(urngrange == std::numeric_limits<uint64_t>::max());
|
||||
|
||||
const uint64_t urange = vec.size();
|
||||
auto first = vec.begin();
|
||||
auto last = vec.end();
|
||||
if (urngrange / urange >= urange) {
|
||||
auto i = first + 1;
|
||||
|
||||
// Since we know the vector isn't empty, an even number of elements
|
||||
// means an uneven number of elements /to swap/, in which case we
|
||||
// do the first one up front:
|
||||
if ((urange % 2) == 0) {
|
||||
std::iter_swap(i++, first + gcc_uniform_int<GCC11>(0, 1, urng));
|
||||
}
|
||||
|
||||
// Now we know that last - i is even, so we do the rest in pairs,
|
||||
// using a single distribution invocation to produce swap positions
|
||||
// for two successive elements at a time:
|
||||
while (i != last) {
|
||||
const uint64_t swap_range = uint64_t(i - first) + 1;
|
||||
const std::pair<uint64_t, uint64_t> pospos = gen_two_uniform_ints<GCC11>(swap_range, swap_range + 1, urng);
|
||||
std::iter_swap(i++, first + pospos.first);
|
||||
std::iter_swap(i++, first + pospos.second);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto i = first + 1; i != last; ++i) {
|
||||
std::iter_swap(i, first + gcc_uniform_int<GCC11>(0, i - first, urng));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void gcc11_shuffle(std::vector<T> &vec, uint64_t mt19937_64_seed) {
|
||||
gcc_shuffle<T, true>(vec, mt19937_64_seed);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void gcc10_shuffle(std::vector<T> &vec, uint64_t mt19937_64_seed) {
|
||||
gcc_shuffle<T, false>(vec, mt19937_64_seed);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void std_shuffle(std::vector<T> &vec, uint64_t mt19937_64_seed) {
|
||||
std::mt19937_64 gen(mt19937_64_seed);
|
||||
std::shuffle(vec.begin(), vec.end(), gen);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void hf3fs_shuffle(std::vector<T> &vec, uint64_t mt19937_64_seed) {
|
||||
#if defined(USE_STD_SHUFFLE)
|
||||
std_shuffle(vec, mt19937_64_seed);
|
||||
#elif defined(USE_GCC10_SHUFFLE)
|
||||
gcc10_shuffle(vec, mt19937_64_seed);
|
||||
#elif defined(USE_GCC11_SHUFFLE)
|
||||
gcc11_shuffle(vec, mt19937_64_seed);
|
||||
#else
|
||||
#error "shuffle method not defined"
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool safe_shuffle_seed(uint32_t vec_len, uint64_t mt19937_64_seed) {
|
||||
std::vector<uint32_t> vec1(vec_len);
|
||||
std::iota(vec1.begin(), vec1.end(), 0);
|
||||
std::vector<uint32_t> vec2 = vec1;
|
||||
std::vector<uint32_t> vec3 = vec1;
|
||||
assert(vec1 == vec2 && vec1 == vec3);
|
||||
|
||||
std_shuffle(vec1, mt19937_64_seed);
|
||||
gcc_shuffle<uint32_t, true>(vec2, mt19937_64_seed);
|
||||
gcc_shuffle<uint32_t, false>(vec3, mt19937_64_seed);
|
||||
|
||||
#if (_GLIBCXX_RELEASE >= 11)
|
||||
XLOGF_IF(DFATAL, vec1 != vec2, "vector length {}, shuffle seed {}", vec_len, mt19937_64_seed);
|
||||
#elif (_GLIBCXX_RELEASE >= 10)
|
||||
XLOGF_IF(DFATAL, vec1 != vec3, "vector length {}, shuffle seed {}", vec_len, mt19937_64_seed);
|
||||
#else
|
||||
#error "g++ version < 10"
|
||||
#endif
|
||||
|
||||
return vec1 == vec2 && vec1 == vec3;
|
||||
}
|
||||
|
||||
inline std::optional<uint64_t> find_safe_seed(uint32_t vec_len) {
|
||||
for (size_t i = 0; i < 1000; i++) {
|
||||
auto seed = folly::Random::rand64();
|
||||
if (safe_shuffle_seed(vec_len, seed)) {
|
||||
return seed;
|
||||
}
|
||||
}
|
||||
// probability should be very low
|
||||
XLOGF(DFATAL, "can't find safe shuffle seed for vec size {}", vec_len);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace hf3fs
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common/utils/Result.h"
|
||||
#include "common/utils/Shuffle.h"
|
||||
#include "fbs/core/user/User.h"
|
||||
#include "fbs/meta/Common.h"
|
||||
#include "fbs/mgmtd/MgmtdTypes.h"
|
||||
@@ -118,7 +119,10 @@ Layout Layout::newChainRange(ChainTableId table,
|
||||
uint32_t chunk,
|
||||
uint32_t stripe,
|
||||
uint32_t baseChainIndex) {
|
||||
auto chains = ChainRange(baseChainIndex, ChainRange::Shuffle::STD_SHUFFLE_MT19937, folly::Random::rand64());
|
||||
auto seed = find_safe_seed(stripe);
|
||||
auto chains = ChainRange(baseChainIndex,
|
||||
seed ? ChainRange::Shuffle::STD_SHUFFLE_MT19937 : ChainRange::Shuffle::NO_SHUFFLE,
|
||||
seed ? *seed : 0);
|
||||
return Layout{table, tableVer, chunk, stripe, chains};
|
||||
}
|
||||
|
||||
@@ -164,8 +168,7 @@ std::span<const uint32_t> Layout::ChainRange::getChainIndexList(size_t stripe) c
|
||||
case NO_SHUFFLE:
|
||||
break;
|
||||
case STD_SHUFFLE_MT19937: {
|
||||
auto rng = std::mt19937_64(seed);
|
||||
std::shuffle(chains.begin(), chains.end(), rng);
|
||||
hf3fs_shuffle(chains, seed);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "client/mgmtd/ICommonMgmtdClient.h"
|
||||
#include "common/utils/Coroutine.h"
|
||||
#include "common/utils/Result.h"
|
||||
#include "common/utils/Shuffle.h"
|
||||
#include "fbs/meta/Schema.h"
|
||||
#include "fbs/mgmtd/MgmtdTypes.h"
|
||||
|
||||
@@ -110,8 +111,13 @@ class ChainAllocator {
|
||||
fmt::format("try to allocate {} chains from {}, found {}", layout.stripeSize, tableId, chainCnt));
|
||||
}
|
||||
auto chainBegin = roundRobin(chainCnt);
|
||||
// pick a safe shuffle seed
|
||||
auto seed = find_safe_seed(layout.stripeSize);
|
||||
if (!seed) {
|
||||
co_return makeError(StatusCode::kInvalidArg, "Failed to find safe shuffle seed");
|
||||
}
|
||||
layout.tableVersion = table->chainTableVersion;
|
||||
layout.chains = Layout::ChainRange(chainBegin, Layout::ChainRange::STD_SHUFFLE_MT19937, folly::Random::rand64());
|
||||
layout.chains = Layout::ChainRange(chainBegin, Layout::ChainRange::STD_SHUFFLE_MT19937, *seed);
|
||||
if (auto valid = layout.valid(false); valid.hasError()) {
|
||||
XLOGF(DFATAL, "Layout is not valid after alloc {}, error {}", layout, valid.error());
|
||||
CO_RETURN_ERROR(valid);
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
#include <cstdint>
|
||||
#include <folly/Random.h>
|
||||
#include <folly/experimental/TestUtil.h>
|
||||
#include <folly/logging/xlog.h>
|
||||
#include <gflags/gflags.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "common/utils/Shuffle.h"
|
||||
#include "fmt/core.h"
|
||||
|
||||
DEFINE_int32(test_shuffle, 10000, "shuffle times");
|
||||
DEFINE_string(test_dump_path, "", "3fs meta dump path");
|
||||
|
||||
namespace hf3fs::test {
|
||||
namespace {
|
||||
|
||||
TEST(TestShuffle, Basic) {
|
||||
// most random seed should be safe if vector size is small
|
||||
for (int i = 0; i < FLAGS_test_shuffle; i++) {
|
||||
auto seed = folly::Random::rand64();
|
||||
auto vecsize = folly::Random::rand32(200);
|
||||
ASSERT_TRUE(safe_shuffle_seed(vecsize, seed)) << " " << seed << " " << vecsize;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestShuffle, FindUnsafeSeed) {
|
||||
// for large vector size, we can find mismatch seed
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
auto seed = folly::Random::rand64();
|
||||
auto vecsize = 10000;
|
||||
if (!safe_shuffle_seed(vecsize, seed)) {
|
||||
fmt::println("find unsafe seed {} for vecsize {}", seed, vecsize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ASSERT_TRUE(false);
|
||||
}
|
||||
|
||||
TEST(TestShuffle, KnownUnsafeSeed) {
|
||||
// we know some seed is not safe
|
||||
std::vector<uint64_t> unsafeSeeds = {6853942027173882469ull,
|
||||
12595889032228965821ull,
|
||||
8619779154399616190ull,
|
||||
12360005796445515182ull,
|
||||
14687818431154681368ull,
|
||||
17026414701752999916ull,
|
||||
12928634241705049562ull,
|
||||
5961001693986322440ull,
|
||||
8264875872430788387ull,
|
||||
8715369310580772031ull,
|
||||
17199606764515290853ull,
|
||||
15088078859152357184ull,
|
||||
13543500868882399137ull,
|
||||
472380080658457497ull};
|
||||
for (auto seed : unsafeSeeds) {
|
||||
ASSERT_FALSE(safe_shuffle_seed(200, seed)) << seed;
|
||||
#if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE)
|
||||
std::vector<uint32_t> vec1(200);
|
||||
std::iota(vec1.begin(), vec1.end(), 0);
|
||||
std::vector<uint32_t> vec2 = vec1;
|
||||
std_shuffle(vec1, seed);
|
||||
#if (_GLIBCXX_RELEASE >= 11)
|
||||
gcc11_shuffle(vec2, seed);
|
||||
#elif (_GLIBCXX_RELEASE >= 10)
|
||||
gcc10_shuffle(vec2, seed);
|
||||
#else
|
||||
static_assert(false);
|
||||
#endif
|
||||
ASSERT_EQ(vec1, vec2) << "seed: " << seed;
|
||||
#else
|
||||
static_assert(false);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestShuffle, CheckDump) {
|
||||
if (FLAGS_test_dump_path.empty()) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
std::ifstream file(FLAGS_test_dump_path);
|
||||
ASSERT_TRUE(file.is_open()) << "failed to open: " << FLAGS_test_dump_path;
|
||||
|
||||
std::string line;
|
||||
std::getline(file, line); // skip header
|
||||
|
||||
std::cerr << "id,seed,size,match_gcc10,match_gcc11" << std::endl;
|
||||
|
||||
uint64_t total = 0;
|
||||
uint64_t mismatch_g10 = 0, mismatch_g11 = 0, mismatch_both = 0;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
std::istringstream iss(line);
|
||||
std::string id_str, seed_str, size_str;
|
||||
|
||||
std::getline(iss, id_str, ',');
|
||||
std::getline(iss, seed_str, ',');
|
||||
std::getline(iss, size_str, ',');
|
||||
|
||||
uint64_t id = std::stoull(id_str, nullptr, 16);
|
||||
uint64_t seed = std::stoull(seed_str);
|
||||
uint64_t size = std::stoull(size_str);
|
||||
|
||||
std::vector<uint64_t> vec_std(size), vec_g10(size), vec_g11(size);
|
||||
std::iota(vec_std.begin(), vec_std.end(), 0);
|
||||
std::iota(vec_g10.begin(), vec_g10.end(), 0);
|
||||
std::iota(vec_g11.begin(), vec_g11.end(), 0);
|
||||
|
||||
std_shuffle(vec_std, seed);
|
||||
gcc10_shuffle(vec_g10, seed);
|
||||
gcc11_shuffle(vec_g11, seed);
|
||||
|
||||
bool match_g10 = (vec_std == vec_g10);
|
||||
bool match_g11 = (vec_std == vec_g11);
|
||||
|
||||
if (!match_g10 || !match_g11) {
|
||||
std::cerr << std::hex << id << std::dec << ","
|
||||
<< seed << ","
|
||||
<< size << ","
|
||||
<< (match_g10 ? "true" : "false") << ","
|
||||
<< (match_g11 ? "true" : "false") << std::endl;
|
||||
}
|
||||
|
||||
if (!match_g10 && !match_g11) {
|
||||
mismatch_both++;
|
||||
} else if (!match_g10) {
|
||||
mismatch_g10++;
|
||||
} else if (!match_g11) {
|
||||
mismatch_g11++;
|
||||
}
|
||||
|
||||
total++;
|
||||
if (total % 10000 == 0) {
|
||||
std::cout << "Progress: " << total
|
||||
<< ", g++10_only_mismatch: " << mismatch_g10
|
||||
<< ", g++11_only_mismatch: " << mismatch_g11
|
||||
<< ", both_mismatch: " << mismatch_both << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Done. Total: " << total
|
||||
<< ", g++10_only_mismatch: " << mismatch_g10
|
||||
<< ", g++11_only_mismatch: " << mismatch_g11
|
||||
<< ", both_mismatch: " << mismatch_both << std::endl;
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace hf3fs::test
|
||||
Reference in New Issue
Block a user