29# if !defined(NOMINMAX)
35# include <sys/resource.h>
40constexpr std::int32_t columns = 20;
41constexpr std::int32_t pages = 4;
43std::vector<char> mutablePath(
const std::filesystem::path &path) {
44 const std::string text = path.string();
45 std::vector<char> result(text.begin(), text.end());
46 result.push_back(
'\0');
50std::uint64_t peakMemoryKib() {
52 PROCESS_MEMORY_COUNTERS counters{};
53 if (!GetProcessMemoryInfo(GetCurrentProcess(), &counters,
sizeof(counters)))
return 0;
54 return static_cast<std::uint64_t
>(counters.PeakWorkingSetSize / 1024U);
56 struct rusage usage{};
57 if (getrusage(RUSAGE_SELF, &usage) != 0)
return 0;
58# if defined(__APPLE__)
59 return static_cast<std::uint64_t
>(usage.ru_maxrss / 1024);
61 return static_cast<std::uint64_t
>(usage.ru_maxrss);
66sdds::Layout numericLayout() {
67 sdds::DataOptions data;
68 data.mode = sdds::DataMode::Binary;
69 data.majorOrder = sdds::MajorOrder::Row;
70 data.byteOrder = sdds::ByteOrder::Little;
71 sdds::LayoutBuilder builder;
72 builder.setDataOptions(data);
73 for (std::int32_t column = 0; column < columns; ++column)
74 builder.addColumn({{
"c" + std::to_string(column), {}, {}, {}, {}, sdds::Type::Double}, 0});
75 return builder.build();
78sdds::Layout mixedLayout() {
79 sdds::LayoutBuilder builder;
80 builder.addArray({{
"array", {}, {}, {}, {}, sdds::Type::Double}, 0, 1, {}})
81 .addColumn({{
"c0", {}, {}, {}, {}, sdds::Type::Double}, 0})
82 .addColumn({{
"text", {}, {}, {}, {}, sdds::Type::String}, 0});
83 return builder.build();
86std::vector<double> columnValues(std::int64_t rows, std::int32_t column,
88 std::vector<double> values(
static_cast<std::size_t
>(rows));
89 for (std::int64_t row = 0; row < rows; ++row)
90 values[
static_cast<std::size_t
>(row)] = page * 1000.0 + column + row * 0.001;
94void generateCpp(
const std::filesystem::path &path, std::int64_t rows,
95 sdds::Compression compression = sdds::Compression::None) {
96 auto layout = numericLayout();
97 sdds::WriterOptions options;
98 options.compression = compression;
99 options.gzipLevel = 1;
100 options.lzmaPreset = 1;
101 auto writer = sdds::Writer::create(path, layout, options);
102 for (std::int32_t page = 0; page < pages; ++page) {
103 writer.beginPage(rows);
104 for (std::int32_t column = 0; column < columns; ++column)
105 writer.setColumn(
"c" + std::to_string(column), columnValues(rows, column, page));
111void generateMixed(
const std::filesystem::path &path, std::int64_t rows) {
112 const auto layout = mixedLayout();
113 auto writer = sdds::Writer::create(path, layout);
114 std::vector<double> array(10000, 1.25);
115 std::vector<double> numeric(
static_cast<std::size_t
>(rows), 2.5);
116 std::vector<std::string> strings(
static_cast<std::size_t
>(rows));
117 for (std::int64_t row = 0; row < rows; ++row)
118 strings[
static_cast<std::size_t
>(row)] =
"row-" + std::to_string(row % 1000);
119 for (std::int32_t page = 0; page < pages; ++page) {
120 writer.beginPage(rows);
121 writer.setArray(
"array", {{
static_cast<std::int32_t
>(array.size())}, array});
122 writer.setColumn(
"c0", numeric);
123 writer.setColumn(
"text", strings);
129double cppRead(
const std::filesystem::path &path, std::string_view mode) {
130 auto reader = sdds::Reader::open(path);
131 sdds::ReadRequest request;
132 if (mode ==
"project") {
133 request.parameters = sdds::FieldSelection::noFields();
134 request.arrays = sdds::FieldSelection::noFields();
135 request.columns = sdds::FieldSelection::only({
"c0",
"c1"});
136 }
else if (mode ==
"sparse") {
137 request.rows.stride = 10;
140 while (
auto page = reader.next(request)) {
141 const auto &values = page->columnAs<
double>(
"c0");
142 for (
const double value : values) checksum += value;
143 if (mode ==
"project")
144 for (
const double value : page->columnAs<
double>(
"c1")) checksum += value;
150double cppSeek(
const std::filesystem::path &path) {
151 auto reader = sdds::Reader::open(path);
152 reader.buildPageIndex();
153 sdds::ReadRequest request;
154 request.parameters = sdds::FieldSelection::noFields();
155 request.arrays = sdds::FieldSelection::noFields();
156 request.columns = sdds::FieldSelection::only({
"c0"});
157 request.rows.first = 0;
158 request.rows.count = 1;
160 for (std::int32_t iteration = 0; iteration < 20; ++iteration) {
161 reader.gotoPage(iteration % 2 ? pages : 1);
162 checksum += reader.next(request)->columnAs<
double>(
"c0")[0];
168double cppMixedRead(
const std::filesystem::path &path,
bool strings) {
169 auto reader = sdds::Reader::open(path);
170 sdds::ReadRequest request;
171 request.parameters = sdds::FieldSelection::noFields();
172 request.arrays = strings ? sdds::FieldSelection::noFields()
173 : sdds::FieldSelection::only({
"array"});
174 request.columns = strings ? sdds::FieldSelection::only({
"text"})
175 : sdds::FieldSelection::noFields();
177 while (
auto page = reader.next(request)) {
179 for (const auto &value : page->columnAs<std::string>(
"text")) checksum += value.size();
181 for (const double value : page->arrayAs<double>(
"array")) checksum += value;
187double cRead(
const std::filesystem::path &path, std::string_view mode) {
189 auto filename = mutablePath(path);
191 if (mode ==
"project") {
193 char second[] =
"c1";
194 char *names[] = {first, second};
199 std::int32_t page = 0;
204 for (std::int64_t row = 0; row < rows; ++row) checksum += values[row];
206 if (mode ==
"project") {
208 for (std::int64_t row = 0; row < rows; ++row) checksum += values[row];
216double cSeek(
const std::filesystem::path &path) {
218 auto filename = mutablePath(path);
226 for (std::int32_t iteration = 0; iteration < 20; ++iteration) {
236 checksum += values[0];
243double cMixedRead(
const std::filesystem::path &path,
bool strings) {
245 auto filename = mutablePath(path);
249 char text[] =
"text";
250 char *names[] = {text};
258 for (std::int64_t row = 0; row < rows; ++row)
259 checksum += std::string(values[row]).size();
264 if (!array)
return NAN;
265 const auto *values =
static_cast<const double *
>(array->data);
266 for (std::int32_t index = 0; index < array->elements; ++index) checksum += values[index];
274double cppWrite(
const std::filesystem::path &path, std::int64_t rows) {
275 generateCpp(path, rows);
276 return static_cast<double>(std::filesystem::file_size(path));
279double cWrite(
const std::filesystem::path &path, std::int64_t rows) {
281 auto filename = mutablePath(path);
284 for (std::int32_t column = 0; column < columns; ++column) {
285 const std::string name =
"c" + std::to_string(column);
289 for (std::int32_t page = 0; page < pages; ++page) {
291 for (std::int32_t column = 0; column < columns; ++column) {
292 const std::string name =
"c" + std::to_string(column);
293 auto values = columnValues(rows, column, page);
294 if (!
SDDS_SetColumn(&dataset, SDDS_SET_BY_NAME, values.data(), rows, name.c_str()))
300 return static_cast<double>(std::filesystem::file_size(path));
303double cppAppend(
const std::filesystem::path &path) {
304 auto writer = sdds::Writer::append(path);
306 for (std::int32_t column = 0; column < columns; ++column)
307 writer.setColumn(
"c" + std::to_string(column), std::vector<double>{column * 1.0});
310 return static_cast<double>(std::filesystem::file_size(path));
313double cAppend(
const std::filesystem::path &path) {
317 for (std::int32_t column = 0; column < columns; ++column) {
318 const std::string name =
"c" + std::to_string(column);
319 double value = column;
320 if (!
SDDS_SetColumn(&dataset, SDDS_SET_BY_NAME, &value, 1, name.c_str()))
return NAN;
323 return static_cast<double>(std::filesystem::file_size(path));
326double cppUpdate(
const std::filesystem::path &path) {
327 auto writer = sdds::Writer::appendToLastPage(path, 1);
328 const std::int64_t firstRow = writer.rowsPresent();
329 for (std::int32_t column = 0; column < columns; ++column)
330 writer.setColumn(
"c" + std::to_string(column), std::vector<double>{column * 1.0}, firstRow);
331 writer.updatePage(
true);
333 return static_cast<double>(std::filesystem::file_size(path));
336double cUpdate(
const std::filesystem::path &path) {
338 std::int64_t rowsPresent = 0;
341 for (std::int32_t column = 0; column < columns; ++column) {
342 const std::string name =
"c" + std::to_string(column);
343 if (!
SDDS_SetRowValues(&dataset, SDDS_SET_BY_NAME | SDDS_PASS_BY_VALUE, rowsPresent,
344 name.c_str(),
static_cast<double>(column),
nullptr))
348 return static_cast<double>(std::filesystem::file_size(path));
351void result(
const std::string &engine,
const std::string &mode,
double seconds,
353 std::cout <<
"{\"engine\":\"" << engine <<
"\",\"mode\":\"" << mode
354 <<
"\",\"seconds\":" << seconds <<
",\"peak_kib\":" << peakMemoryKib()
355 <<
",\"checksum\":" << checksum <<
"}\n";
360int main(
int argc,
char **argv) {
362 std::cerr <<
"usage: benchmark_sddspp MODE PATH [ROWS]\n";
365 const std::string mode(argv[1]);
366 const std::filesystem::path path(argv[2]);
367 const std::int64_t rows = argc > 3 ? std::stoll(argv[3]) : 100000;
368 if (!path.parent_path().empty()) std::filesystem::create_directories(path.parent_path());
369 if (mode ==
"generate") {
370 generateCpp(path, rows);
373 if (mode ==
"generate-gzip") {
374 generateCpp(path, rows, sdds::Compression::Gzip);
377 if (mode ==
"generate-xz") {
378 generateCpp(path, rows, sdds::Compression::Xz);
381 if (mode ==
"generate-mixed") {
382 generateMixed(path, rows);
385 const auto start = std::chrono::steady_clock::now();
386 double checksum = NAN;
388 std::string workload;
389 if (mode.rfind(
"cpp-", 0) == 0) {
391 workload = mode.substr(4);
392 if (workload ==
"full" || workload ==
"project" || workload ==
"sparse" ||
393 workload ==
"compression")
394 checksum = cppRead(path, workload ==
"compression" ?
"full" : workload);
395 else if (workload ==
"strings" || workload ==
"arrays")
396 checksum = cppMixedRead(path, workload ==
"strings");
397 else if (workload ==
"seek") checksum = cppSeek(path);
398 else if (workload ==
"write") checksum = cppWrite(path, rows);
399 else if (workload ==
"append") checksum = cppAppend(path);
400 else if (workload ==
"update") checksum = cppUpdate(path);
401 }
else if (mode.rfind(
"c-", 0) == 0) {
403 workload = mode.substr(2);
404 if (workload ==
"full" || workload ==
"project" || workload ==
"sparse" ||
405 workload ==
"compression")
406 checksum = cRead(path, workload ==
"compression" ?
"full" : workload);
407 else if (workload ==
"strings" || workload ==
"arrays")
408 checksum = cMixedRead(path, workload ==
"strings");
409 else if (workload ==
"seek") checksum = cSeek(path);
410 else if (workload ==
"write") checksum = cWrite(path, rows);
411 else if (workload ==
"append") checksum = cAppend(path);
412 else if (workload ==
"update") checksum = cUpdate(path);
414 if (engine.empty() || std::isnan(checksum))
return 2;
415 const double seconds = std::chrono::duration<double>(
416 std::chrono::steady_clock::now() - start).count();
417 result(engine, workload, seconds, checksum);
SDDS (Self Describing Data Set) Data Types Definitions and Function Prototypes.
int32_t SDDS_SetDefaultIOBufferSize(int32_t newValue)
int32_t SDDS_LengthenTable(SDDS_DATASET *SDDS_dataset, int64_t n_additional_rows)
int32_t SDDS_SetRowValues(SDDS_DATASET *SDDS_dataset, int32_t mode, int64_t row,...)
int32_t SDDS_StartPage(SDDS_DATASET *SDDS_dataset, int64_t expected_n_rows)
int32_t SDDS_SetColumn(SDDS_DATASET *SDDS_dataset, int32_t mode, void *data, int64_t rows,...)
Sets the values for one data column in the current data table of an SDDS dataset.
int32_t SDDS_InitializeAppend(SDDS_DATASET *SDDS_dataset, const char *filename)
Initializes the SDDS dataset for appending data by adding a new page to an existing file.
int32_t SDDS_InitializeOutput(SDDS_DATASET *SDDS_dataset, int32_t data_mode, int32_t lines_per_row, const char *description, const char *contents, const char *filename)
Initializes the SDDS output dataset.
int32_t SDDS_InitializeAppendToPage(SDDS_DATASET *SDDS_dataset, const char *filename, int64_t updateInterval, int64_t *rowsPresentReturn)
Initializes the SDDS dataset for appending data to the last page of an existing file.
int32_t SDDS_DefineSimpleColumn(SDDS_DATASET *SDDS_dataset, const char *name, const char *unit, int32_t type)
Defines a simple data column within the SDDS dataset.
int32_t SDDS_UpdatePage(SDDS_DATASET *SDDS_dataset, uint32_t mode)
Updates the current page of the SDDS dataset.
int32_t SDDS_WritePage(SDDS_DATASET *SDDS_dataset)
Writes the current data table to the output file.
int32_t SDDS_WriteLayout(SDDS_DATASET *SDDS_dataset)
Writes the SDDS layout header to the output file.
void SDDS_FreeArray(SDDS_ARRAY *array)
Frees memory allocated for an SDDS array structure.
int32_t SDDS_FreeStringArray(char **string, int64_t strings)
Frees an array of strings by deallocating each individual string.
void SDDS_PrintErrors(FILE *fp, int32_t mode)
Prints recorded error messages to a specified file stream.
void SDDS_Free(void *mem)
Free memory previously allocated by SDDS_Malloc.
#define SDDS_DOUBLE
Identifier for the double data type.