mirror of
https://github.com/deepseek-ai/3FS
synced 2025-05-16 18:33:14 +00:00
292 lines
11 KiB
C++
292 lines
11 KiB
C++
#include "StorageBench.h"
|
|
|
|
#include <folly/init/Init.h>
|
|
|
|
#include "common/monitor/Monitor.h"
|
|
#include "memory/common/OverrideCppNewDelete.h"
|
|
|
|
DEFINE_bool(benchmarkNetwork, false, "Run in network benchmark mode");
|
|
DEFINE_bool(benchmarkStorage, false, "Run in storage benchmark mode");
|
|
DEFINE_bool(ignoreIOError, false, "Ignore all IO errors");
|
|
DEFINE_bool(injectRandomServerError, false, "Inject random server errors");
|
|
DEFINE_bool(injectRandomClientError, false, "Inject random client errors");
|
|
DEFINE_bool(retryPermanentError, false, "Retry requests with permanent errors");
|
|
DEFINE_bool(verifyReadData, false, "Check if the read data are correct");
|
|
DEFINE_bool(verifyReadChecksum, false, "Verify the checksum of read IOs");
|
|
DEFINE_bool(verifyWriteChecksum, true, "Verify the checksum of write IOs");
|
|
DEFINE_bool(randomShuffleChunkIds, false, "Random shuffle generated chunk IDs");
|
|
DEFINE_bool(generateTestData, true, "Generate test data for read test");
|
|
DEFINE_bool(sparseChunkIds, false, "Generate sparse chunk IDs");
|
|
DEFINE_bool(truncateChunks, false, "Truncate chunks");
|
|
DEFINE_bool(cleanupChunks, false, "Clean up (remove) chunks after benchmark");
|
|
DEFINE_bool(cleanupChunksBeforeBench, false, "Clean up (remove) chunks before benchmark");
|
|
DEFINE_bool(serverMode, false, "Run in server mode");
|
|
DEFINE_bool(clientMode, false, "Run in client mode");
|
|
DEFINE_bool(clusterMode, false, "Run in cluster mode (get routing info from mgmtd)");
|
|
DEFINE_bool(printMetrics, false, "Enable printing metrics in logs");
|
|
DEFINE_bool(reportMetrics, false, "Enable reporting metrics to ClickHouse");
|
|
DEFINE_uint32(metaStoreType, 0, "Metadata store type (0=LevelDB, 1=RocksDB, 2=MemDB)");
|
|
DEFINE_uint32(chunkSizeKB, 512, "Chunk size (KB)");
|
|
DEFINE_uint32(chainTableId, 0, "Chain table id");
|
|
DEFINE_uint32(chainTableVersion, 0, "Chain table version");
|
|
DEFINE_string(chainIds, "", "List of chain ids");
|
|
DEFINE_string(storageNodeIds, "", "List of storage node ids");
|
|
DEFINE_uint32(numChains, 1, "Number of chains");
|
|
DEFINE_uint32(numReplicas, 1, "Number of replicas");
|
|
DEFINE_uint32(numStorageNodes, 1, "Number of storage nodes");
|
|
DEFINE_uint32(numChunks, 1, "Number of chunks");
|
|
DEFINE_uint32(readSize, 4096, "Read IO size");
|
|
DEFINE_uint32(writeSize, 131072, "Write IO size");
|
|
DEFINE_uint32(memoryAlignment, 1, "Alignment size of each IO buffer");
|
|
DEFINE_uint32(readOffAlignment, 0, "Alignment size of each read IO offset");
|
|
DEFINE_uint32(batchSize, 1, "Read/write batch size");
|
|
DEFINE_uint32(readBatchSize, 0, "Read batch size");
|
|
DEFINE_uint32(writeBatchSize, 0, "Write batch size");
|
|
DEFINE_uint32(removeBatchSize, 0, "Remove batch size");
|
|
DEFINE_uint32(numReadSecs, 0, "Read test duration");
|
|
DEFINE_uint32(numWriteSecs, 0, "Write test duration");
|
|
DEFINE_uint32(numCoroutines, 1, "Number of coroutines");
|
|
DEFINE_uint32(numTestThreads, 1, "Number of test threads");
|
|
DEFINE_uint32(randSeed, 0, "Random seed for chunk id generation");
|
|
DEFINE_uint32(chunkIdPrefix, 0xFFFF, "The most significant 2 bytes of chunk ids");
|
|
DEFINE_uint32(serviceLevel, 0, "Service level");
|
|
DEFINE_uint32(listenPort, 0, "Listen port");
|
|
DEFINE_uint32(clientTimeoutMS, 30000, "Client timeout (milliseconds)");
|
|
DEFINE_string(dataPaths, folly::fs::temp_directory_path().string(), "Comma or space separated list of paths");
|
|
DEFINE_string(clientConfig, "", "Path of client config");
|
|
DEFINE_string(serverConfig, "", "Path of server config");
|
|
DEFINE_string(statsFilePath, "./perfstats.csv", "Path of performance stats file");
|
|
DEFINE_string(ibvDevices, "mlx5_0,mlx5_1", "Comma or space separated list of ibv devices");
|
|
DEFINE_string(ibnetZones, "", "Comma or space separated list of IB network zones");
|
|
DEFINE_string(clusterId, "stage", "Cluster id used to connect to mgmtd");
|
|
DEFINE_string(mgmtdEndpoints,
|
|
"",
|
|
"Comma or space separated list of mgmtd endpoints, "
|
|
"e.g. 'RDMA://10.1.1.1:1234,RDMA://10.1.1.2:1234'");
|
|
DEFINE_string(storageEndpoints,
|
|
"",
|
|
"Comma or space separated list of storage ids and endpoints, "
|
|
"e.g. '1@RDMA://10.1.1.1:1234,2@RDMA://10.1.1.2:1234'");
|
|
DEFINE_string(monitorEndpoint, "", "Monitor endpoint");
|
|
DEFINE_uint32(defaultPKeyIndex, 1, "IB default pkey index");
|
|
|
|
namespace hf3fs::storage::benchmark {
|
|
|
|
using namespace hf3fs::storage::client;
|
|
|
|
std::vector<uint32_t> stringToIntVec(const std::string &str) {
|
|
std::vector<uint32_t> vec;
|
|
std::vector<std::string> substrs;
|
|
boost::split(substrs, str, boost::is_any_of(", "));
|
|
|
|
for (auto s : substrs) {
|
|
boost::trim(s);
|
|
if (s.empty()) continue;
|
|
|
|
uint32_t n = std::stoul(s);
|
|
vec.push_back(n);
|
|
}
|
|
|
|
return vec;
|
|
}
|
|
|
|
bool runBenchmarks() {
|
|
std::vector<std::string> dataPathStrs;
|
|
boost::split(dataPathStrs, FLAGS_dataPaths, boost::is_any_of(", "));
|
|
|
|
std::vector<hf3fs::Path> dataPaths;
|
|
dataPaths.reserve(dataPathStrs.size());
|
|
|
|
for (auto str : dataPathStrs) {
|
|
boost::trim(str);
|
|
if (str.empty()) continue;
|
|
|
|
dataPaths.emplace_back(str);
|
|
}
|
|
|
|
std::vector<std::string> endpointRawStrs;
|
|
boost::split(endpointRawStrs, FLAGS_storageEndpoints, boost::is_any_of(", "));
|
|
|
|
std::map<NodeId, net::Address> storageEndpoints;
|
|
|
|
for (auto str : endpointRawStrs) {
|
|
boost::trim(str);
|
|
if (str.empty()) continue;
|
|
|
|
std::vector<std::string> nodeEndpointStrs;
|
|
boost::split(nodeEndpointStrs, str, boost::is_any_of("@"));
|
|
|
|
if (nodeEndpointStrs.size() != 2) {
|
|
XLOGF(ERR, "Wrong node endpoint string: {}", str);
|
|
return false;
|
|
}
|
|
|
|
auto nodeIdStr = nodeEndpointStrs[0];
|
|
auto endpointStr = nodeEndpointStrs[1];
|
|
|
|
NodeId nodeId{std::stoul(nodeIdStr)};
|
|
auto endpoint = net::Address::fromString(endpointStr);
|
|
storageEndpoints[nodeId] = endpoint;
|
|
XLOGF(WARN, "Add storage endpoint: {} @ {}", nodeId, endpoint);
|
|
}
|
|
|
|
if (FLAGS_clientMode && storageEndpoints.empty()) {
|
|
XLOGF(ERR, "No storage endpoint specified for client mode");
|
|
return false;
|
|
}
|
|
|
|
if (FLAGS_readSize > FLAGS_chunkSizeKB * 1024) {
|
|
XLOGF(ERR, "Read size {} is greater than chunk size {}", FLAGS_readSize, FLAGS_chunkSizeKB * 1024);
|
|
return false;
|
|
}
|
|
|
|
auto metaStoreType = static_cast<kv::KVStore::Type>(FLAGS_metaStoreType);
|
|
|
|
test::SystemSetupConfig setupConfig = {
|
|
FLAGS_chunkSizeKB * 1024 /*chunkSize*/,
|
|
FLAGS_numChains /*numChains*/,
|
|
FLAGS_numReplicas /*numReplicas*/,
|
|
FLAGS_numStorageNodes /*numStorageNodes*/,
|
|
dataPaths /*dataPaths*/,
|
|
FLAGS_clientConfig,
|
|
FLAGS_serverConfig,
|
|
storageEndpoints,
|
|
FLAGS_serviceLevel,
|
|
FLAGS_listenPort,
|
|
StorageClient::ImplementationType::RPC /*clientImplType*/,
|
|
metaStoreType,
|
|
true /*useFakeMgmtdClient*/,
|
|
!FLAGS_clientMode /*startStorageServer*/,
|
|
false,
|
|
};
|
|
|
|
std::vector<std::string> ibvDevices;
|
|
boost::split(ibvDevices, FLAGS_ibvDevices, boost::is_any_of(", "));
|
|
|
|
std::vector<std::string> ibnetZones;
|
|
boost::split(ibnetZones, FLAGS_ibnetZones, boost::is_any_of(", "));
|
|
|
|
endpointRawStrs.clear();
|
|
boost::split(endpointRawStrs, FLAGS_mgmtdEndpoints, boost::is_any_of(", "));
|
|
|
|
std::vector<net::Address> mgmtdEndpoints;
|
|
|
|
for (auto str : endpointRawStrs) {
|
|
boost::trim(str);
|
|
if (str.empty()) continue;
|
|
|
|
auto endpoint = net::Address::fromString(str);
|
|
mgmtdEndpoints.push_back(endpoint);
|
|
XLOGF(WARN, "Add mgmtd endpoint: {}", endpoint);
|
|
}
|
|
|
|
StorageBench::Options benchOptions{FLAGS_numChunks,
|
|
FLAGS_readSize,
|
|
FLAGS_writeSize,
|
|
FLAGS_batchSize,
|
|
FLAGS_numReadSecs,
|
|
FLAGS_numWriteSecs,
|
|
FLAGS_clientTimeoutMS,
|
|
FLAGS_numCoroutines,
|
|
FLAGS_numTestThreads,
|
|
FLAGS_randSeed,
|
|
(uint16_t)FLAGS_chunkIdPrefix,
|
|
FLAGS_benchmarkNetwork,
|
|
FLAGS_benchmarkStorage,
|
|
FLAGS_ignoreIOError,
|
|
FLAGS_injectRandomServerError,
|
|
FLAGS_injectRandomClientError,
|
|
FLAGS_retryPermanentError,
|
|
FLAGS_verifyReadData,
|
|
FLAGS_verifyReadChecksum,
|
|
FLAGS_verifyWriteChecksum,
|
|
FLAGS_randomShuffleChunkIds,
|
|
FLAGS_generateTestData,
|
|
FLAGS_sparseChunkIds,
|
|
FLAGS_statsFilePath,
|
|
ibvDevices,
|
|
ibnetZones,
|
|
mgmtdEndpoints,
|
|
FLAGS_clusterId,
|
|
FLAGS_chainTableId,
|
|
FLAGS_chainTableVersion,
|
|
stringToIntVec(FLAGS_chainIds),
|
|
stringToIntVec(FLAGS_storageNodeIds),
|
|
FLAGS_memoryAlignment,
|
|
FLAGS_readOffAlignment,
|
|
FLAGS_defaultPKeyIndex,
|
|
FLAGS_readBatchSize,
|
|
FLAGS_writeBatchSize,
|
|
FLAGS_removeBatchSize};
|
|
|
|
StorageBench bench(setupConfig, benchOptions);
|
|
|
|
if (FLAGS_clusterMode) {
|
|
if (!bench.connect()) {
|
|
XLOGF(WARN, "Failed to connect to cluster");
|
|
return false;
|
|
}
|
|
} else {
|
|
if (!bench.setup()) {
|
|
XLOGF(WARN, "Failed to set up benchmark");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bench.generateChunkIds();
|
|
|
|
if (FLAGS_cleanupChunksBeforeBench) {
|
|
bench.cleanup();
|
|
}
|
|
|
|
bool runOK = true;
|
|
|
|
if (FLAGS_serverMode) {
|
|
XLOGF(WARN, "Waiting...");
|
|
while (true) {
|
|
::sleep(1);
|
|
}
|
|
} else {
|
|
runOK = bench.run();
|
|
}
|
|
|
|
if (FLAGS_truncateChunks) {
|
|
bench.truncate();
|
|
}
|
|
|
|
if (FLAGS_cleanupChunks) {
|
|
bench.cleanup();
|
|
}
|
|
|
|
bench.teardown();
|
|
|
|
return runOK;
|
|
}
|
|
|
|
} // namespace hf3fs::storage::benchmark
|
|
|
|
int main(int argc, char **argv) {
|
|
folly::init(&argc, &argv, true);
|
|
hf3fs::monitor::Monitor::Config monitorConfig;
|
|
|
|
if (FLAGS_printMetrics || FLAGS_reportMetrics) {
|
|
if (FLAGS_printMetrics) {
|
|
monitorConfig.reporters(0).set_type("log");
|
|
} else if (FLAGS_reportMetrics) {
|
|
monitorConfig.reporters(0).set_type("monitor_collector");
|
|
monitorConfig.reporters(0).monitor_collector().set_remote_ip(FLAGS_monitorEndpoint);
|
|
monitorConfig.set_reporters_length(1);
|
|
}
|
|
|
|
auto monitorResult = hf3fs::monitor::Monitor::start(monitorConfig);
|
|
XLOGF_IF(FATAL, !monitorResult, "Failed to start monitor: {}", monitorResult.error());
|
|
}
|
|
|
|
bool ok = hf3fs::storage::benchmark::runBenchmarks();
|
|
|
|
hf3fs::monitor::Monitor::stop();
|
|
hf3fs::memory::shutdown();
|
|
|
|
return ok ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
}
|