Created
June 19, 2017 11:28
-
-
Save fmorgner/93f2e1e55ae6a73d227258eb1e1d3990 to your computer and use it in GitHub Desktop.
Benchmark for filtering files in C++
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// COMPILE WITH: g++ -std=c++1z filter.cpp -o filter -O3 -flto -lhayai_main -lstdc++fs | |
#include <algorithm> | |
#include <boost/iterator/filter_iterator.hpp> | |
#include <experimental/filesystem> | |
#include <hayai/hayai.hpp> | |
#include <iterator> | |
#include <set> | |
#include <string> | |
#include <vector> | |
#include <functional> | |
namespace fs = std::experimental::filesystem; | |
auto constexpr test_root = "/home/sophia/test_root"; /*!< The root folder containing the files */ | |
/** | |
* A filter that accepts everything | |
*/ | |
auto constexpr true_filter = [](auto const &) { | |
return true; | |
}; | |
/** | |
* A filter that accepts only files | |
* | |
* @param entry A 'directory_entry' | |
*/ | |
auto constexpr file_filter = [](auto const & entry) { | |
return fs::is_regular_file(entry); | |
}; | |
/** | |
* A filter that accepts only files with specific extensions | |
* | |
* @param entry A 'directory_entry' | |
*/ | |
auto constexpr file_extension_filter = [](auto const & entry) { | |
auto static const kExtensions = std::set<std::string>{".jpg", ".png", ".tga"}; | |
return fs::is_regular_file(entry) && kExtensions.count(entry.path().extension()); | |
}; | |
/** | |
* Get the number of 'directory_entry' objects in @p root satisfying the @p predicate | |
* | |
* @param root The root folder to search in | |
* @param predicate A predicate to check on the 'directory_entry' objects in @p root | |
*/ | |
std::size_t get_num_entries(fs::path const & root, std::function<bool (fs::directory_entry const &)> predicate = true_filter) | |
{ | |
auto && iterator = fs::recursive_directory_iterator{root}; | |
return std::count_if(begin(iterator), end(iterator), predicate); | |
} | |
/** | |
* Get all directory entries in @p root | |
* | |
* @param root The root directory | |
*/ | |
auto no_filter(fs::path const & root) | |
{ | |
auto && iterator = fs::recursive_directory_iterator{root}; | |
return std::vector<fs::directory_entry>{begin(iterator), end(iterator)}; | |
} | |
/** | |
* Get all directory entries in @p root | |
* | |
* @note This version preallocates the result vector to the required number of elements | |
* | |
* @param root The root directory | |
*/ | |
auto no_filter_prealloc(fs::path const & root) | |
{ | |
auto && iterator = fs::recursive_directory_iterator{root}; | |
auto && result = std::vector<fs::directory_entry>{}; | |
result.reserve(get_num_entries(root)); | |
result.insert(result.end(), begin(iterator), end(iterator)); | |
return result; | |
} | |
/** | |
* Get all 'file' directory entries in @p root | |
* | |
* @note This version uses the Boost.Iterator library | |
* | |
* @param root The root directory | |
*/ | |
auto boost_file_entries(fs::path const & root) | |
{ | |
auto && iterator = fs::recursive_directory_iterator{root}; | |
auto && begin = boost::make_filter_iterator(file_filter, | |
fs::begin(iterator), fs::end(iterator)); | |
auto && end = boost::make_filter_iterator(file_filter, | |
fs::end(iterator), fs::end(iterator)); | |
return std::vector<fs::directory_entry>{begin, end}; | |
} | |
/** | |
* Get all 'file' directory entries in @p root | |
* | |
* @note This version uses the Boost.Iterator library | |
* @note This version preallocates the result vector to the required number of elements | |
* | |
* @param root The root directory | |
*/ | |
auto boost_file_entries_prealloc(fs::path const & root) | |
{ | |
auto && iterator = fs::recursive_directory_iterator{root}; | |
auto && begin = boost::make_filter_iterator(file_filter, | |
fs::begin(iterator), fs::end(iterator)); | |
auto && end = boost::make_filter_iterator(file_filter, | |
fs::end(iterator), fs::end(iterator)); | |
auto && result = std::vector<fs::directory_entry>{}; | |
result.reserve(get_num_entries(root, file_filter)); | |
result.insert(result.end(), begin, end); | |
return result; | |
} | |
/** | |
* Get all 'file' directory entries with 'image' file extensions in @p root | |
* | |
* @note This version uses the Boost.Iterator library | |
* | |
* @param root The root directory | |
*/ | |
auto boost_file_extension_entries(fs::path const & root) | |
{ | |
auto && iterator = fs::recursive_directory_iterator{root}; | |
auto && begin = boost::make_filter_iterator(file_extension_filter, | |
fs::begin(iterator), fs::end(iterator)); | |
auto && end = boost::make_filter_iterator(file_extension_filter, | |
fs::end(iterator), fs::end(iterator)); | |
return std::vector<fs::directory_entry>{begin, end}; | |
} | |
/** | |
* Get all 'file' directory entries with 'image' file extensions in @p root | |
* | |
* @note This version uses the Boost.Iterator library | |
* @note This version preallocates the result vector to the required number of elements | |
* | |
* @param root The root directory | |
*/ | |
auto boost_file_extension_entries_prealloc(fs::path const & root) | |
{ | |
auto && iterator = fs::recursive_directory_iterator{root}; | |
auto && begin = boost::make_filter_iterator(file_extension_filter, | |
fs::begin(iterator), fs::end(iterator)); | |
auto && end = boost::make_filter_iterator(file_extension_filter, | |
fs::end(iterator), fs::end(iterator)); | |
auto && result = std::vector<fs::directory_entry>{}; | |
result.reserve(get_num_entries(root, file_extension_filter)); | |
result.insert(result.end(), begin, end); | |
return result; | |
} | |
/** | |
* Get all 'file' directory entries in @p root | |
* | |
* @note This version uses only standard functions | |
* | |
* @param root The root directory | |
*/ | |
auto std_file_entries(fs::path const & root) | |
{ | |
auto && iterator = fs::recursive_directory_iterator{root}; | |
auto && result = std::vector<fs::directory_entry>{}; | |
copy_if(begin(iterator), end(iterator), back_inserter(result), file_filter); | |
return result; | |
} | |
/** | |
* Get all 'file' directory entries in @p root | |
* | |
* @note This version uses only standard functions | |
* @note This version preallocates the result vector to the required number of elements | |
* | |
* @param root The root directory | |
*/ | |
auto std_file_entries_prealloc(fs::path const & root) | |
{ | |
auto && iterator = fs::recursive_directory_iterator{root}; | |
auto && result = std::vector<fs::directory_entry>{}; | |
result.reserve(get_num_entries(root, file_filter)); | |
copy_if(fs::begin(iterator), fs::end(iterator), back_inserter(result), file_filter); | |
return result; | |
} | |
/** | |
* Get all 'file' directory entries with 'image' file extensions in @p root | |
* | |
* @note This version uses only standard functions | |
* | |
* @param root The root directory | |
*/ | |
auto std_file_extension_entries(fs::path const & root) | |
{ | |
auto && result = std::vector<fs::directory_entry>{}; | |
auto && iterator = fs::recursive_directory_iterator{root}; | |
copy_if(begin(iterator), end(iterator), back_inserter(result), file_extension_filter); | |
return result; | |
} | |
/** | |
* Get all 'file' directory entries with 'image' file extensions in @p root | |
* | |
* @note This version uses only standard functions | |
* @note This version preallocates the result vector to the required number of elements | |
* | |
* @param root The root directory | |
*/ | |
auto std_file_extension_entries_prealloc(fs::path const & root) | |
{ | |
auto && iterator = fs::recursive_directory_iterator{root}; | |
auto && result = std::vector<fs::directory_entry>{}; | |
result.reserve(get_num_entries(root, file_extension_filter)); | |
copy_if(begin(iterator), end(iterator), back_inserter(result), file_extension_filter); | |
return result; | |
} | |
auto constexpr kRuns{10}; | |
auto constexpr kIterations{25}; | |
BENCHMARK(FileFilter, NoFilter, kRuns, kIterations) { no_filter(test_root); } | |
BENCHMARK(FileFilter, NoFilterPrealloc, kRuns, kIterations) { no_filter_prealloc(test_root); } | |
BENCHMARK(FileFilter, BoostFileFilter, kRuns, kIterations) { boost_file_entries(test_root); } | |
BENCHMARK(FileFilter, StdFileFilter, kRuns, kIterations) { std_file_entries(test_root); } | |
BENCHMARK(FileFilter, BoostFileFilterPrealloc, kRuns, kIterations) { boost_file_entries_prealloc(test_root); } | |
BENCHMARK(FileFilter, StdFileFilterPrealloc, kRuns, kIterations) { std_file_entries_prealloc(test_root); } | |
BENCHMARK(FileFilter, BoostFileExtensionFilter, kRuns, kIterations) { boost_file_extension_entries(test_root); } | |
BENCHMARK(FileFilter, StdFileExtensionFilter, kRuns, kIterations) { std_file_extension_entries(test_root); } | |
BENCHMARK(FileFilter, BoostFileExtensionFilterPrealloc, kRuns, kIterations) { boost_file_extension_entries_prealloc(test_root); } | |
BENCHMARK(FileFilter, StdFileExtensionFilterPrealloc, kRuns, kIterations) { std_file_extension_entries_prealloc(test_root); } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment