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