gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 61
    31  #elif defined(__riscv)
    32  #define SYS_getdents64 61
    33  #else
    34  #error "Unknown architecture"
    35  #endif
    36  #endif  // SYS_getdents64
    37  
    38  namespace gvisor {
    39  namespace testing {
    40  
    41  namespace {
    42  
    43  constexpr int kBufferSize = 65536;
    44  
    45  PosixErrorOr<TempPath> CreateDirectory(int count,
    46                                         std::vector<std::string>* files) {
    47    ASSIGN_OR_RETURN_ERRNO(TempPath dir, TempPath::CreateDir());
    48  
    49    ASSIGN_OR_RETURN_ERRNO(FileDescriptor dfd,
    50                           Open(dir.path(), O_RDONLY | O_DIRECTORY));
    51  
    52    for (int i = 0; i < count; i++) {
    53      auto file = NewTempRelPath();
    54      auto res = MknodAt(dfd, file, S_IFREG | 0644, 0);
    55      RETURN_IF_ERRNO(res);
    56      files->push_back(file);
    57    }
    58  
    59    return std::move(dir);
    60  }
    61  
    62  PosixError CleanupDirectory(const TempPath& dir,
    63                              std::vector<std::string>* files) {
    64    ASSIGN_OR_RETURN_ERRNO(FileDescriptor dfd,
    65                           Open(dir.path(), O_RDONLY | O_DIRECTORY));
    66  
    67    for (auto it = files->begin(); it != files->end(); ++it) {
    68      auto res = UnlinkAt(dfd, *it, 0);
    69      RETURN_IF_ERRNO(res);
    70    }
    71    return NoError();
    72  }
    73  
    74  // Creates a directory containing `files` files, and reads all the directory
    75  // entries from the directory using a single FD.
    76  void BM_GetdentsSameFD(benchmark::State& state) {
    77    // Create directory with given files.
    78    const int count = state.range(0);
    79  
    80    // Keep a vector of all of the file TempPaths that is destroyed before dir.
    81    //
    82    // Normally, we'd simply allow dir to recursively clean up the contained
    83    // files, but that recursive cleanup uses getdents, which may be very slow in
    84    // extreme benchmarks.
    85    TempPath dir;
    86    std::vector<std::string> files;
    87    dir = ASSERT_NO_ERRNO_AND_VALUE(CreateDirectory(count, &files));
    88  
    89    FileDescriptor fd =
    90        ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY));
    91    char buffer[kBufferSize];
    92  
    93    // We read all directory entries on each iteration, but report this as a
    94    // "batch" iteration so that reported times are per file.
    95    while (state.KeepRunningBatch(count)) {
    96      ASSERT_THAT(lseek(fd.get(), 0, SEEK_SET), SyscallSucceeds());
    97  
    98      int ret;
    99      do {
   100        ASSERT_THAT(ret = syscall(SYS_getdents64, fd.get(), buffer, kBufferSize),
   101                    SyscallSucceeds());
   102      } while (ret > 0);
   103    }
   104  
   105    ASSERT_NO_ERRNO(CleanupDirectory(dir, &files));
   106  
   107    state.SetItemsProcessed(state.iterations());
   108  }
   109  
   110  BENCHMARK(BM_GetdentsSameFD)->Range(1, 1 << 12)->UseRealTime();
   111  
   112  // Creates a directory containing `files` files, and reads all the directory
   113  // entries from the directory using a new FD each time.
   114  void BM_GetdentsNewFD(benchmark::State& state) {
   115    // Create directory with given files.
   116    const int count = state.range(0);
   117  
   118    // Keep a vector of all of the file TempPaths that is destroyed before dir.
   119    //
   120    // Normally, we'd simply allow dir to recursively clean up the contained
   121    // files, but that recursive cleanup uses getdents, which may be very slow in
   122    // extreme benchmarks.
   123    TempPath dir;
   124    std::vector<std::string> files;
   125    dir = ASSERT_NO_ERRNO_AND_VALUE(CreateDirectory(count, &files));
   126    char buffer[kBufferSize];
   127  
   128    // We read all directory entries on each iteration, but report this as a
   129    // "batch" iteration so that reported times are per file.
   130    while (state.KeepRunningBatch(count)) {
   131      FileDescriptor fd =
   132          ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY | O_DIRECTORY));
   133  
   134      int ret;
   135      do {
   136        ASSERT_THAT(ret = syscall(SYS_getdents64, fd.get(), buffer, kBufferSize),
   137                    SyscallSucceeds());
   138      } while (ret > 0);
   139    }
   140  
   141    ASSERT_NO_ERRNO(CleanupDirectory(dir, &files));
   142  
   143    state.SetItemsProcessed(state.iterations());
   144  }
   145  
   146  BENCHMARK(BM_GetdentsNewFD)->Range(1, 1 << 12)->UseRealTime();
   147  
   148  }  // namespace
   149  
   150  }  // namespace testing
   151  }  // namespace gvisor