// Copyright 2020 The gVisor Authors. // // 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 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. #include #include #include #include "gtest/gtest.h" #include "benchmark/benchmark.h" #include "test/util/file_descriptor.h" #include "test/util/fs_util.h" #include "test/util/temp_path.h" #include "test/util/test_util.h" #ifndef SYS_getdents64 #if defined(__x86_64__) #define SYS_getdents64 217 #elif defined(__aarch64__) #define SYS_getdents64 217 #else #error "Unknown architecture" #endif #endif // SYS_getdents64 namespace gvisor { namespace testing { namespace { constexpr int kBufferSize = 65536; PosixErrorOr CreateDirectory(int count, std::vector* files) { ASSIGN_OR_RETURN_ERRNO(TempPath dir, TempPath::CreateDir()); ASSIGN_OR_RETURN_ERRNO(FileDescriptor dfd, Open(dir.path(), O_RDONLY | O_DIRECTORY)); for (int i = 0; i < count; i++) { auto file = NewTempRelPath(); auto res = MknodAt(dfd, file, S_IFREG | 0644, 0); RETURN_IF_ERRNO(res); files->push_back(file); } return std::move(dir); } PosixError CleanupDirectory(const TempPath& dir, std::vector* files) { ASSIGN_OR_RETURN_ERRNO(FileDescriptor dfd, Open(dir.path(), O_RDONLY | O_DIRECTORY)); for (auto it = files->begin(); it != files->end(); ++it) { auto res = UnlinkAt(dfd, *it, 0); RETURN_IF_ERRNO(res); } return NoError(); } // Creates a directory containing `files` files, and reads all the directory // entries from the directory using a single FD. void BM_GetdentsSameFD(benchmark::State& state) { // Create directory with given files. const int count = state.range(0); // Keep a vector of all of the file TempPaths that is destroyed before dir. // // Normally, we'd simply allow dir to recursively clean up the contained // files, but that recursive cleanup uses getdents, which may be very slow in // extreme benchmarks. TempPath dir; std::vector files; dir = ASSERT_NO_ERRNO_AND_VALUE(CreateDirectory(count, &files)); FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY)); char buffer[kBufferSize]; // We read all directory entries on each iteration, but report this as a // "batch" iteration so that reported times are per file. while (state.KeepRunningBatch(count)) { ASSERT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceeds()); int ret; do { ASSERT_THAT(ret = syscall(SYS_getdents64, fd.get(), buffer, kBufferSize), SyscallSucceeds()); } while (ret > 0); } ASSERT_NO_ERRNO(CleanupDirectory(dir, &files)); state.SetItemsProcessed(state.iterations()); } BENCHMARK(BM_GetdentsSameFD)->Range(1, 1 << 16)->UseRealTime(); // Creates a directory containing `files` files, and reads all the directory // entries from the directory using a new FD each time. void BM_GetdentsNewFD(benchmark::State& state) { // Create directory with given files. const int count = state.range(0); // Keep a vector of all of the file TempPaths that is destroyed before dir. // // Normally, we'd simply allow dir to recursively clean up the contained // files, but that recursive cleanup uses getdents, which may be very slow in // extreme benchmarks. TempPath dir; std::vector files; dir = ASSERT_NO_ERRNO_AND_VALUE(CreateDirectory(count, &files)); char buffer[kBufferSize]; // We read all directory entries on each iteration, but report this as a // "batch" iteration so that reported times are per file. while (state.KeepRunningBatch(count)) { FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY)); int ret; do { ASSERT_THAT(ret = syscall(SYS_getdents64, fd.get(), buffer, kBufferSize), SyscallSucceeds()); } while (ret > 0); } ASSERT_NO_ERRNO(CleanupDirectory(dir, &files)); state.SetItemsProcessed(state.iterations()); } BENCHMARK(BM_GetdentsNewFD)->Range(1, 1 << 12)->UseRealTime(); } // namespace } // namespace testing } // namespace gvisor