github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/perf/linux/getdents_benchmark.cc (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include <sys/stat.h> 16 #include <sys/types.h> 17 #include <unistd.h> 18 19 #include "gtest/gtest.h" 20 #include "benchmark/benchmark.h" 21 #include "test/util/file_descriptor.h" 22 #include "test/util/fs_util.h" 23 #include "test/util/temp_path.h" 24 #include "test/util/test_util.h" 25 26 #ifndef SYS_getdents64 27 #if defined(__x86_64__) 28 #define SYS_getdents64 217 29 #elif defined(__aarch64__) 30 #define SYS_getdents64 217 31 #else 32 #error "Unknown architecture" 33 #endif 34 #endif // SYS_getdents64 35 36 namespace gvisor { 37 namespace testing { 38 39 namespace { 40 41 constexpr int kBufferSize = 65536; 42 43 PosixErrorOr<TempPath> CreateDirectory(int count, 44 std::vector<std::string>* files) { 45 ASSIGN_OR_RETURN_ERRNO(TempPath dir, TempPath::CreateDir()); 46 47 ASSIGN_OR_RETURN_ERRNO(FileDescriptor dfd, 48 Open(dir.path(), O_RDONLY | O_DIRECTORY)); 49 50 for (int i = 0; i < count; i++) { 51 auto file = NewTempRelPath(); 52 auto res = MknodAt(dfd, file, S_IFREG | 0644, 0); 53 RETURN_IF_ERRNO(res); 54 files->push_back(file); 55 } 56 57 return std::move(dir); 58 } 59 60 PosixError CleanupDirectory(const TempPath& dir, 61 std::vector<std::string>* files) { 62 ASSIGN_OR_RETURN_ERRNO(FileDescriptor dfd, 63 Open(dir.path(), O_RDONLY | O_DIRECTORY)); 64 65 for (auto it = files->begin(); it != files->end(); ++it) { 66 auto res = UnlinkAt(dfd, *it, 0); 67 RETURN_IF_ERRNO(res); 68 } 69 return NoError(); 70 } 71 72 // Creates a directory containing `files` files, and reads all the directory 73 // entries from the directory using a single FD. 74 void BM_GetdentsSameFD(benchmark::State& state) { 75 // Create directory with given files. 76 const int count = state.range(0); 77 78 // Keep a vector of all of the file TempPaths that is destroyed before dir. 79 // 80 // Normally, we'd simply allow dir to recursively clean up the contained 81 // files, but that recursive cleanup uses getdents, which may be very slow in 82 // extreme benchmarks. 83 TempPath dir; 84 std::vector<std::string> files; 85 dir = ASSERT_NO_ERRNO_AND_VALUE(CreateDirectory(count, &files)); 86 87 FileDescriptor fd = 88 ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY)); 89 char buffer[kBufferSize]; 90 91 // We read all directory entries on each iteration, but report this as a 92 // "batch" iteration so that reported times are per file. 93 while (state.KeepRunningBatch(count)) { 94 ASSERT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceeds()); 95 96 int ret; 97 do { 98 ASSERT_THAT(ret = syscall(SYS_getdents64, fd.get(), buffer, kBufferSize), 99 SyscallSucceeds()); 100 } while (ret > 0); 101 } 102 103 ASSERT_NO_ERRNO(CleanupDirectory(dir, &files)); 104 105 state.SetItemsProcessed(state.iterations()); 106 } 107 108 BENCHMARK(BM_GetdentsSameFD)->Range(1, 1 << 12)->UseRealTime(); 109 110 // Creates a directory containing `files` files, and reads all the directory 111 // entries from the directory using a new FD each time. 112 void BM_GetdentsNewFD(benchmark::State& state) { 113 // Create directory with given files. 114 const int count = state.range(0); 115 116 // Keep a vector of all of the file TempPaths that is destroyed before dir. 117 // 118 // Normally, we'd simply allow dir to recursively clean up the contained 119 // files, but that recursive cleanup uses getdents, which may be very slow in 120 // extreme benchmarks. 121 TempPath dir; 122 std::vector<std::string> files; 123 dir = ASSERT_NO_ERRNO_AND_VALUE(CreateDirectory(count, &files)); 124 char buffer[kBufferSize]; 125 126 // We read all directory entries on each iteration, but report this as a 127 // "batch" iteration so that reported times are per file. 128 while (state.KeepRunningBatch(count)) { 129 FileDescriptor fd = 130 ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY)); 131 132 int ret; 133 do { 134 ASSERT_THAT(ret = syscall(SYS_getdents64, fd.get(), buffer, kBufferSize), 135 SyscallSucceeds()); 136 } while (ret > 0); 137 } 138 139 ASSERT_NO_ERRNO(CleanupDirectory(dir, &files)); 140 141 state.SetItemsProcessed(state.iterations()); 142 } 143 144 BENCHMARK(BM_GetdentsNewFD)->Range(1, 1 << 12)->UseRealTime(); 145 146 } // namespace 147 148 } // namespace testing 149 } // namespace gvisor