Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/llfio.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2018-04-25 20:37:12 +0300
committerNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spamtrap@nedprod.com>2018-04-25 20:37:12 +0300
commit217ee5c65b5487f5c97f285e090298d516068170 (patch)
treeefd4a8d420868ba28ff9f61ce22b8e2884e32096 /programs
parent4080a74f79fed027b7fc965a220ba3b7d8578c1d (diff)
upd
Diffstat (limited to 'programs')
-rw-r--r--programs/benchmark-iostreams/main.cpp421
1 files changed, 421 insertions, 0 deletions
diff --git a/programs/benchmark-iostreams/main.cpp b/programs/benchmark-iostreams/main.cpp
new file mode 100644
index 00000000..16d7bbb9
--- /dev/null
+++ b/programs/benchmark-iostreams/main.cpp
@@ -0,0 +1,421 @@
+/* Test the performance of various file locking mechanisms
+(C) 2016-2017 Niall Douglas <http://www.nedproductions.biz/> (6 commits)
+File Created: Mar 2016
+
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License in the accompanying file
+Licence.txt or at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+
+Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file Licence.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+*/
+
+//! On exit dumps a CSV file of the AFIO log, one per child worker
+#define DEBUG_CSV 1
+
+//! Seconds to run the benchmark
+#define BENCHMARK_DURATION 10
+
+#define _CRT_SECURE_NO_WARNINGS 1
+
+#include "../../include/afio/afio.hpp"
+#include "kerneltest/include/kerneltest/v1.0/child_process.hpp"
+
+#include <fstream>
+#include <iostream>
+#include <vector>
+
+#ifdef _WIN32
+#undef _CRT_NONSTDC_DEPRECATE
+#define _CRT_NONSTDC_DEPRECATE(a)
+#include <conio.h> // for kbhit()
+#else
+#include <sys/ioctl.h>
+#include <termios.h>
+
+bool kbhit()
+{
+ termios term;
+ tcgetattr(0, &term);
+
+ termios term2 = term;
+ term2.c_lflag &= ~ICANON;
+ tcsetattr(0, TCSANOW, &term2);
+
+ int byteswaiting;
+ ioctl(0, FIONREAD, &byteswaiting);
+
+ tcsetattr(0, TCSANOW, &term);
+
+ return byteswaiting > 0;
+}
+#endif
+
+namespace afio = AFIO_V2_NAMESPACE;
+namespace child_process = KERNELTEST_V1_NAMESPACE::child_process;
+
+static volatile size_t *shared_memory;
+static void initialise_shared_memory()
+{
+ auto fh = afio::file_handle::file({}, "shared_memory", afio::file_handle::mode::write, afio::file_handle::creation::if_needed, afio::file_handle::caching::temporary).value();
+ auto sh = afio::section_handle::section(fh, 8, afio::section_handle::flag::write).value();
+ auto mp = afio::map_handle::map(sh).value();
+ shared_memory = (size_t *) mp.address();
+ if(!shared_memory)
+ abort();
+ *shared_memory = (size_t) -1;
+}
+static void child_locks(size_t id)
+{
+ size_t current = *shared_memory;
+ if(current != (size_t) -1)
+ {
+ std::cerr << "FATAL: Lock algorithm is broken! " << current << " still holds the lock!" << std::endl;
+ std::terminate();
+ }
+ *shared_memory = id;
+}
+static void child_unlocks(size_t id)
+{
+ size_t current = *shared_memory;
+ if(current != id)
+ {
+ std::cerr << "FATAL: Lock algorithm is broken! " << current << " has stolen the lock!" << std::endl;
+ std::terminate();
+ }
+ *shared_memory = (size_t) -1;
+}
+
+int main(int argc, char *argv[])
+{
+ if(argc < 4)
+ {
+ std::cerr << "Usage: " << argv[0] << " [!]<atomic_append|byte_ranges|lock_files|memory_map> <entities> <no of waiters>" << std::endl;
+ return 1;
+ }
+ initialise_shared_memory();
+
+
+ // ******** MASTER PROCESS BEGINS HERE ********
+ if(strcmp(argv[1], "spawned") && strcmp(argv[1], "!spawned"))
+ {
+ size_t waiters = atoi(argv[3]);
+ if(!waiters || !atoi(argv[2]))
+ {
+ std::cerr << "Usage: " << argv[0] << " [!]<atomic_append|byte_ranges|lock_files|memory_map> <entities> <no of waiters>" << std::endl;
+ return 1;
+ }
+
+ std::vector<child_process::child_process> children;
+ auto mypath = child_process::current_process_path();
+#ifdef UNICODE
+ std::vector<afio::filesystem::path::string_type> args = {L"spawned", L"", L"", L"", L"00"};
+ args[1].resize(strlen(argv[1]));
+ for(size_t n = 0; n < args[1].size(); n++)
+ args[1][n] = argv[1][n];
+ args[2].resize(strlen(argv[2]));
+ for(size_t n = 0; n < args[2].size(); n++)
+ args[2][n] = argv[2][n];
+ args[3].resize(strlen(argv[3]));
+ for(size_t n = 0; n < args[3].size(); n++)
+ args[3][n] = argv[3][n];
+#else
+ std::vector<afio::filesystem::path::string_type> args = {"spawned", argv[1], argv[2], argv[3], "00"};
+#endif
+ auto env = child_process::current_process_env();
+ std::cout << "Launching " << waiters << " copies of myself as a child process ..." << std::endl;
+ for(size_t n = 0; n < waiters; n++)
+ {
+ if(n >= 10)
+ {
+ args[4][0] = (char) ('0' + (n / 10));
+ args[4][1] = (char) ('0' + (n % 10));
+ }
+ else
+ {
+ args[4][0] = (char) ('0' + n);
+ args[4][1] = 0;
+ }
+ auto child = child_process::child_process::launch(mypath, args, env, true);
+ if(child.has_error())
+ {
+ std::cerr << "FATAL: Child " << n << " could not be launched due to " << child.error().message() << std::endl;
+ return 1;
+ }
+ children.push_back(std::move(child.value()));
+ }
+ // Wait for all children to tell me they are ready
+ char buffer[1024];
+ std::cout << "Waiting for all children to become ready ..." << std::endl;
+ for(auto &child : children)
+ {
+ auto &i = child.cout();
+ if(!i.getline(buffer, sizeof(buffer)))
+ {
+ std::cerr << "ERROR: Child seems to have vanished!" << std::endl;
+ return 1;
+ }
+ if(0 != strncmp(buffer, "READY", 5))
+ {
+ std::cerr << "ERROR: Child wrote unexpected output '" << buffer << "'" << std::endl;
+ return 1;
+ }
+ }
+#if 0
+ std::cout << "Attach your debugger now and press Return" << std::endl;
+ getchar();
+#endif
+#if 0
+ auto begin = std::chrono::steady_clock::now();
+ while(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - begin).count() < 2)
+ ;
+#endif
+ std::cout << "Benchmarking for " << BENCHMARK_DURATION << " seconds ..." << std::endl;
+ // Issue go command to all children
+ for(auto &child : children)
+ child.cin() << "GO" << std::endl;
+ // Wait for benchmark to complete
+ std::this_thread::sleep_for(std::chrono::seconds(BENCHMARK_DURATION));
+ std::cout << "Stopping benchmark and telling children to report results ..." << std::endl;
+ // Tell children to quit
+ for(auto &child : children)
+ child.cin() << "STOP" << std::endl;
+ unsigned long long results = 0, result;
+ std::cout << std::endl;
+ std::ofstream oh("benchmark_locking.csv");
+ for(size_t n = 0; n < children.size(); n++)
+ {
+ auto &child = children[n];
+ if(!child.cout().getline(buffer, sizeof(buffer)))
+ {
+ std::cerr << "ERROR: Child seems to have vanished!" << std::endl;
+ return 1;
+ }
+ if(0 != strncmp(buffer, "RESULTS(", 8))
+ {
+ std::cerr << "ERROR: Child wrote unexpected output '" << buffer << "'." << std::endl;
+ return 1;
+ }
+ result = atol(&buffer[8]);
+ std::cout << "Child " << n << " reports result " << result << std::endl;
+ results += result;
+ if(n)
+ oh << ",";
+ oh << result;
+ }
+ results /= BENCHMARK_DURATION;
+ std::cout << "Total result: " << results << " ops/sec" << std::endl;
+ oh << "\n" << results << std::endl;
+ return 0;
+ }
+
+
+ // ******** CHILD PROCESS BEGINS HERE ********
+ if(argc < 6)
+ {
+ std::cerr << "ERROR: args too short" << std::endl;
+ return 1;
+ }
+ enum class lock_algorithm
+ {
+ unknown,
+ atomic_append,
+ byte_ranges,
+ lock_files,
+ memory_map
+ } test = lock_algorithm::unknown;
+ bool contended = true;
+ if(!strcmp(argv[2], "atomic_append"))
+ test = lock_algorithm::atomic_append;
+ else if(!strcmp(argv[2], "byte_ranges"))
+ test = lock_algorithm::byte_ranges;
+ else if(!strcmp(argv[2], "lock_files"))
+ test = lock_algorithm::lock_files;
+ else if(!strcmp(argv[2], "memory_map"))
+ test = lock_algorithm::memory_map;
+ else if(!strcmp(argv[2], "!atomic_append"))
+ {
+ test = lock_algorithm::atomic_append;
+ contended = false;
+ }
+ else if(!strcmp(argv[2], "!byte_ranges"))
+ {
+ test = lock_algorithm::byte_ranges;
+ contended = false;
+ }
+ else if(!strcmp(argv[2], "!lock_files"))
+ {
+ test = lock_algorithm::lock_files;
+ contended = false;
+ }
+ else if(!strcmp(argv[2], "!memory_map"))
+ {
+ test = lock_algorithm::memory_map;
+ contended = false;
+ }
+ if(test == lock_algorithm::unknown)
+ {
+ std::cerr << "ERROR: unknown test requested" << std::endl;
+ return 1;
+ }
+ size_t total_locks = atoi(argv[3]), waiters = atoi(argv[4]), this_child = atoi(argv[5]), count = 0;
+ (void) waiters;
+ if(!total_locks)
+ {
+ std::cerr << "ERROR: unknown total locks requested" << std::endl;
+ return 1;
+ }
+ // I am a spawned child. Tell parent I am ready.
+ std::cout << "READY(" << this_child << ")" << std::endl;
+ // Wait for parent to let me proceed
+ std::atomic<int> done(-1);
+ std::thread worker([test, contended, total_locks, this_child, &done, &count] {
+ std::unique_ptr<afio::algorithm::shared_fs_mutex::shared_fs_mutex> algorithm;
+ auto base = afio::path_handle::path(".").value();
+ switch(test)
+ {
+ case lock_algorithm::atomic_append:
+ {
+ auto v = afio::algorithm::shared_fs_mutex::atomic_append::fs_mutex_append({}, "lockfile");
+ if(v.has_error())
+ {
+ std::cerr << "ERROR: Creation of lock algorithm returns " << v.error().message() << std::endl;
+ return;
+ }
+ algorithm = std::make_unique<afio::algorithm::shared_fs_mutex::atomic_append>(std::move(v.value()));
+ break;
+ }
+ case lock_algorithm::byte_ranges:
+ {
+ auto v = afio::algorithm::shared_fs_mutex::byte_ranges::fs_mutex_byte_ranges({}, "lockfile");
+ if(v.has_error())
+ {
+ std::cerr << "ERROR: Creation of lock algorithm returns " << v.error().message() << std::endl;
+ return;
+ }
+ algorithm = std::make_unique<afio::algorithm::shared_fs_mutex::byte_ranges>(std::move(v.value()));
+ break;
+ }
+ case lock_algorithm::lock_files:
+ {
+ auto v = afio::algorithm::shared_fs_mutex::lock_files::fs_mutex_lock_files(base);
+ if(v.has_error())
+ {
+ std::cerr << "ERROR: Creation of lock algorithm returns " << v.error().message() << std::endl;
+ return;
+ }
+ algorithm = std::make_unique<afio::algorithm::shared_fs_mutex::lock_files>(std::move(v.value()));
+ break;
+ }
+ case lock_algorithm::memory_map:
+ {
+ auto v = afio::algorithm::shared_fs_mutex::memory_map<QUICKCPPLIB_NAMESPACE::algorithm::hash::passthru_hash>::fs_mutex_map({}, "lockfile");
+ if(v.has_error())
+ {
+ std::cerr << "ERROR: Creation of lock algorithm returns " << v.error().message() << std::endl;
+ return;
+ }
+ algorithm = std::make_unique<afio::algorithm::shared_fs_mutex::memory_map<QUICKCPPLIB_NAMESPACE::algorithm::hash::passthru_hash>>(std::move(v.value()));
+ break;
+ }
+ case lock_algorithm::unknown:
+ break;
+ }
+ // Create entities named 0 to total_locks
+ std::vector<afio::algorithm::shared_fs_mutex::shared_fs_mutex::entity_type> entities(total_locks);
+ for(size_t n = 0; n < total_locks; n++)
+ {
+ if(contended)
+ {
+ entities[n].value = n;
+ entities[n].exclusive = true;
+ }
+ else
+ {
+ entities[n].value = (this_child << 4) + n; // guaranteed unique
+ entities[n].exclusive = true;
+ }
+ }
+ while(done == -1)
+ std::this_thread::yield();
+ while(!done)
+ {
+ auto result = algorithm->lock(entities, afio::deadline(), false);
+ if(result.has_error())
+ {
+ std::cerr << "ERROR: Algorithm lock returns " << result.error().message() << std::endl;
+ return;
+ }
+ if(contended)
+ child_locks(this_child);
+ ++count;
+ auto guard = std::move(result.value());
+ if(contended)
+ child_unlocks(this_child);
+ guard.unlock();
+ }
+ });
+ if(!strcmp(argv[1], "!spawned"))
+ {
+ auto lastcount = count;
+ size_t secs = 0;
+ done = 0;
+ while(!kbhit())
+ {
+ std::this_thread::sleep_for(std::chrono::seconds(1));
+ ++secs;
+ std::cout << "\ncount=" << count << " (+" << (count - lastcount) << "), average=" << (count / secs) << std::endl;
+ lastcount = count;
+#if 1
+ auto it = afio::log().cbegin();
+ for(size_t n = 0; n < 10; n++)
+ {
+ if(it == afio::log().cend())
+ break;
+ std::cout << " " << *it;
+ ++it;
+ }
+#endif
+ }
+ done = 1;
+ worker.join();
+ }
+ else
+ for(;;)
+ {
+ char buffer[1024];
+ // This blocks
+ if(!std::cin.getline(buffer, sizeof(buffer)))
+ {
+ return 1;
+ }
+ if(0 == strcmp(buffer, "GO"))
+ {
+ // Launch worker thread
+ done = 0;
+ }
+ else if(0 == strcmp(buffer, "STOP"))
+ {
+ done = 1;
+ worker.join();
+ std::cout << "RESULTS(" << count << ")" << std::endl;
+#if DEBUG_CSV
+ std::ofstream s("benchmark_locking_afio_log" + std::to_string(this_child) + ".csv");
+ s << csv(afio::log());
+#endif
+ return 0;
+ }
+ }
+}