diff options
Diffstat (limited to 'test/perf')
24 files changed, 0 insertions, 2742 deletions
diff --git a/test/perf/BUILD b/test/perf/BUILD deleted file mode 100644 index 0a0def6a3..000000000 --- a/test/perf/BUILD +++ /dev/null @@ -1,116 +0,0 @@ -load("//test/runner:defs.bzl", "syscall_test") - -package(licenses = ["notice"]) - -syscall_test( - test = "//test/perf/linux:clock_getres_benchmark", -) - -syscall_test( - test = "//test/perf/linux:clock_gettime_benchmark", -) - -syscall_test( - test = "//test/perf/linux:death_benchmark", -) - -syscall_test( - test = "//test/perf/linux:epoll_benchmark", -) - -syscall_test( - size = "large", - test = "//test/perf/linux:fork_benchmark", -) - -syscall_test( - size = "large", - test = "//test/perf/linux:futex_benchmark", -) - -syscall_test( - size = "enormous", - tags = ["nogotsan"], - test = "//test/perf/linux:getdents_benchmark", -) - -syscall_test( - size = "large", - test = "//test/perf/linux:getpid_benchmark", -) - -syscall_test( - size = "enormous", - tags = ["nogotsan"], - test = "//test/perf/linux:gettid_benchmark", -) - -syscall_test( - size = "large", - test = "//test/perf/linux:mapping_benchmark", -) - -syscall_test( - size = "large", - add_overlay = True, - test = "//test/perf/linux:open_benchmark", -) - -syscall_test( - test = "//test/perf/linux:pipe_benchmark", -) - -syscall_test( - size = "large", - add_overlay = True, - test = "//test/perf/linux:randread_benchmark", -) - -syscall_test( - size = "large", - add_overlay = True, - test = "//test/perf/linux:read_benchmark", -) - -syscall_test( - size = "large", - test = "//test/perf/linux:sched_yield_benchmark", -) - -syscall_test( - size = "large", - test = "//test/perf/linux:send_recv_benchmark", -) - -syscall_test( - size = "large", - add_overlay = True, - test = "//test/perf/linux:seqwrite_benchmark", -) - -syscall_test( - size = "enormous", - test = "//test/perf/linux:signal_benchmark", -) - -syscall_test( - test = "//test/perf/linux:sleep_benchmark", -) - -syscall_test( - size = "large", - add_overlay = True, - test = "//test/perf/linux:stat_benchmark", -) - -syscall_test( - size = "enormous", - add_overlay = True, - test = "//test/perf/linux:unlink_benchmark", -) - -syscall_test( - size = "large", - add_overlay = True, - test = "//test/perf/linux:write_benchmark", -) diff --git a/test/perf/linux/BUILD b/test/perf/linux/BUILD deleted file mode 100644 index b4e907826..000000000 --- a/test/perf/linux/BUILD +++ /dev/null @@ -1,356 +0,0 @@ -load("//tools:defs.bzl", "cc_binary", "gbenchmark", "gtest") - -package( - default_visibility = ["//:sandbox"], - licenses = ["notice"], -) - -cc_binary( - name = "getpid_benchmark", - testonly = 1, - srcs = [ - "getpid_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:test_main", - ], -) - -cc_binary( - name = "send_recv_benchmark", - testonly = 1, - srcs = [ - "send_recv_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/syscalls/linux:socket_test_util", - "//test/util:file_descriptor", - "//test/util:logging", - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "@com_google_absl//absl/synchronization", - ], -) - -cc_binary( - name = "gettid_benchmark", - testonly = 1, - srcs = [ - "gettid_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:test_main", - ], -) - -cc_binary( - name = "sched_yield_benchmark", - testonly = 1, - srcs = [ - "sched_yield_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "clock_getres_benchmark", - testonly = 1, - srcs = [ - "clock_getres_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:test_main", - ], -) - -cc_binary( - name = "clock_gettime_benchmark", - testonly = 1, - srcs = [ - "clock_gettime_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:test_main", - "@com_google_absl//absl/time", - ], -) - -cc_binary( - name = "open_benchmark", - testonly = 1, - srcs = [ - "open_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:fs_util", - "//test/util:logging", - "//test/util:temp_path", - "//test/util:test_main", - ], -) - -cc_binary( - name = "read_benchmark", - testonly = 1, - srcs = [ - "read_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:fs_util", - "//test/util:logging", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "randread_benchmark", - testonly = 1, - srcs = [ - "randread_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:file_descriptor", - "//test/util:logging", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "@com_google_absl//absl/random", - ], -) - -cc_binary( - name = "write_benchmark", - testonly = 1, - srcs = [ - "write_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:logging", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "seqwrite_benchmark", - testonly = 1, - srcs = [ - "seqwrite_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:logging", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "@com_google_absl//absl/random", - ], -) - -cc_binary( - name = "pipe_benchmark", - testonly = 1, - srcs = [ - "pipe_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:logging", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - ], -) - -cc_binary( - name = "fork_benchmark", - testonly = 1, - srcs = [ - "fork_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:cleanup", - "//test/util:file_descriptor", - "//test/util:logging", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "@com_google_absl//absl/synchronization", - ], -) - -cc_binary( - name = "futex_benchmark", - testonly = 1, - srcs = [ - "futex_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:logging", - "//test/util:test_main", - "//test/util:thread_util", - "@com_google_absl//absl/time", - ], -) - -cc_binary( - name = "epoll_benchmark", - testonly = 1, - srcs = [ - "epoll_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:epoll_util", - "//test/util:file_descriptor", - "//test/util:test_main", - "//test/util:test_util", - "//test/util:thread_util", - "@com_google_absl//absl/time", - ], -) - -cc_binary( - name = "death_benchmark", - testonly = 1, - srcs = [ - "death_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:logging", - "//test/util:test_main", - ], -) - -cc_binary( - name = "mapping_benchmark", - testonly = 1, - srcs = [ - "mapping_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:logging", - "//test/util:memory_util", - "//test/util:posix_error", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "signal_benchmark", - testonly = 1, - srcs = [ - "signal_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:logging", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "getdents_benchmark", - testonly = 1, - srcs = [ - "getdents_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:file_descriptor", - "//test/util:fs_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) - -cc_binary( - name = "sleep_benchmark", - testonly = 1, - srcs = [ - "sleep_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:logging", - "//test/util:test_main", - ], -) - -cc_binary( - name = "stat_benchmark", - testonly = 1, - srcs = [ - "stat_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:fs_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - "@com_google_absl//absl/strings", - ], -) - -cc_binary( - name = "unlink_benchmark", - testonly = 1, - srcs = [ - "unlink_benchmark.cc", - ], - deps = [ - gbenchmark, - gtest, - "//test/util:fs_util", - "//test/util:temp_path", - "//test/util:test_main", - "//test/util:test_util", - ], -) diff --git a/test/perf/linux/clock_getres_benchmark.cc b/test/perf/linux/clock_getres_benchmark.cc deleted file mode 100644 index b051293ad..000000000 --- a/test/perf/linux/clock_getres_benchmark.cc +++ /dev/null @@ -1,39 +0,0 @@ -// 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 <time.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" - -namespace gvisor { -namespace testing { - -namespace { - -// clock_getres(1) is very nearly a no-op syscall, but it does require copying -// out to a userspace struct. It thus provides a nice small copy-out benchmark. -void BM_ClockGetRes(benchmark::State& state) { - struct timespec ts; - for (auto _ : state) { - clock_getres(CLOCK_MONOTONIC, &ts); - } -} - -BENCHMARK(BM_ClockGetRes); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/clock_gettime_benchmark.cc b/test/perf/linux/clock_gettime_benchmark.cc deleted file mode 100644 index 6691bebd9..000000000 --- a/test/perf/linux/clock_gettime_benchmark.cc +++ /dev/null @@ -1,60 +0,0 @@ -// 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 <pthread.h> -#include <time.h> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "benchmark/benchmark.h" - -namespace gvisor { -namespace testing { - -namespace { - -void BM_ClockGettimeThreadCPUTime(benchmark::State& state) { - clockid_t clockid; - ASSERT_EQ(0, pthread_getcpuclockid(pthread_self(), &clockid)); - struct timespec tp; - - for (auto _ : state) { - clock_gettime(clockid, &tp); - } -} - -BENCHMARK(BM_ClockGettimeThreadCPUTime); - -void BM_VDSOClockGettime(benchmark::State& state) { - const clockid_t clock = state.range(0); - struct timespec tp; - absl::Time start = absl::Now(); - - // Don't benchmark the calibration phase. - while (absl::Now() < start + absl::Milliseconds(2100)) { - clock_gettime(clock, &tp); - } - - for (auto _ : state) { - clock_gettime(clock, &tp); - } -} - -BENCHMARK(BM_VDSOClockGettime)->Arg(CLOCK_MONOTONIC)->Arg(CLOCK_REALTIME); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/death_benchmark.cc b/test/perf/linux/death_benchmark.cc deleted file mode 100644 index cb2b6fd07..000000000 --- a/test/perf/linux/death_benchmark.cc +++ /dev/null @@ -1,36 +0,0 @@ -// 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 <signal.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" -#include "test/util/logging.h" - -namespace gvisor { -namespace testing { - -namespace { - -// DeathTest is not so much a microbenchmark as a macrobenchmark. It is testing -// the ability of gVisor (on whatever platform) to execute all the related -// stack-dumping routines associated with EXPECT_EXIT / EXPECT_DEATH. -TEST(DeathTest, ZeroEqualsOne) { - EXPECT_EXIT({ TEST_CHECK(0 == 1); }, ::testing::KilledBySignal(SIGABRT), ""); -} - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/epoll_benchmark.cc b/test/perf/linux/epoll_benchmark.cc deleted file mode 100644 index 0b121338a..000000000 --- a/test/perf/linux/epoll_benchmark.cc +++ /dev/null @@ -1,99 +0,0 @@ -// 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 <sys/epoll.h> -#include <sys/eventfd.h> - -#include <atomic> -#include <cerrno> -#include <cstdint> -#include <cstdlib> -#include <ctime> -#include <memory> - -#include "gtest/gtest.h" -#include "absl/time/time.h" -#include "benchmark/benchmark.h" -#include "test/util/epoll_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Returns a new eventfd. -PosixErrorOr<FileDescriptor> NewEventFD() { - int fd = eventfd(0, /* flags = */ 0); - MaybeSave(); - if (fd < 0) { - return PosixError(errno, "eventfd"); - } - return FileDescriptor(fd); -} - -// Also stolen from epoll.cc unit tests. -void BM_EpollTimeout(benchmark::State& state) { - constexpr int kFDsPerEpoll = 3; - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - - std::vector<FileDescriptor> eventfds; - for (int i = 0; i < kFDsPerEpoll; i++) { - eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); - ASSERT_NO_ERRNO( - RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN, 0)); - } - - struct epoll_event result[kFDsPerEpoll]; - int timeout_ms = state.range(0); - - for (auto _ : state) { - EXPECT_EQ(0, epoll_wait(epollfd.get(), result, kFDsPerEpoll, timeout_ms)); - } -} - -BENCHMARK(BM_EpollTimeout)->Range(0, 8); - -// Also stolen from epoll.cc unit tests. -void BM_EpollAllEvents(benchmark::State& state) { - auto epollfd = ASSERT_NO_ERRNO_AND_VALUE(NewEpollFD()); - const int fds_per_epoll = state.range(0); - constexpr uint64_t kEventVal = 5; - - std::vector<FileDescriptor> eventfds; - for (int i = 0; i < fds_per_epoll; i++) { - eventfds.push_back(ASSERT_NO_ERRNO_AND_VALUE(NewEventFD())); - ASSERT_NO_ERRNO( - RegisterEpollFD(epollfd.get(), eventfds[i].get(), EPOLLIN, 0)); - - ASSERT_THAT(WriteFd(eventfds[i].get(), &kEventVal, sizeof(kEventVal)), - SyscallSucceedsWithValue(sizeof(kEventVal))); - } - - std::vector<struct epoll_event> result(fds_per_epoll); - - for (auto _ : state) { - EXPECT_EQ(fds_per_epoll, - epoll_wait(epollfd.get(), result.data(), fds_per_epoll, 0)); - } -} - -BENCHMARK(BM_EpollAllEvents)->Range(2, 1024); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/fork_benchmark.cc b/test/perf/linux/fork_benchmark.cc deleted file mode 100644 index 84fdbc8a0..000000000 --- a/test/perf/linux/fork_benchmark.cc +++ /dev/null @@ -1,350 +0,0 @@ -// 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 <unistd.h> - -#include "gtest/gtest.h" -#include "absl/synchronization/barrier.h" -#include "benchmark/benchmark.h" -#include "test/util/cleanup.h" -#include "test/util/file_descriptor.h" -#include "test/util/logging.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -constexpr int kBusyMax = 250; - -// Do some CPU-bound busy-work. -int busy(int max) { - // Prevent the compiler from optimizing this work away, - volatile int count = 0; - - for (int i = 1; i < max; i++) { - for (int j = 2; j < i / 2; j++) { - if (i % j == 0) { - count++; - } - } - } - - return count; -} - -void BM_CPUBoundUniprocess(benchmark::State& state) { - for (auto _ : state) { - busy(kBusyMax); - } -} - -BENCHMARK(BM_CPUBoundUniprocess); - -void BM_CPUBoundAsymmetric(benchmark::State& state) { - const size_t max = state.max_iterations; - pid_t child = fork(); - if (child == 0) { - for (int i = 0; i < max; i++) { - busy(kBusyMax); - } - _exit(0); - } - ASSERT_THAT(child, SyscallSucceeds()); - ASSERT_TRUE(state.KeepRunningBatch(max)); - - int status; - EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status)); - EXPECT_EQ(0, WEXITSTATUS(status)); - ASSERT_FALSE(state.KeepRunning()); -} - -BENCHMARK(BM_CPUBoundAsymmetric)->UseRealTime(); - -void BM_CPUBoundSymmetric(benchmark::State& state) { - std::vector<pid_t> children; - auto child_cleanup = Cleanup([&] { - for (const pid_t child : children) { - int status; - EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status)); - EXPECT_EQ(0, WEXITSTATUS(status)); - } - ASSERT_FALSE(state.KeepRunning()); - }); - - const int processes = state.range(0); - for (int i = 0; i < processes; i++) { - size_t cur = (state.max_iterations + (processes - 1)) / processes; - if ((state.iterations() + cur) >= state.max_iterations) { - cur = state.max_iterations - state.iterations(); - } - pid_t child = fork(); - if (child == 0) { - for (int i = 0; i < cur; i++) { - busy(kBusyMax); - } - _exit(0); - } - ASSERT_THAT(child, SyscallSucceeds()); - if (cur > 0) { - // We can have a zero cur here, depending. - ASSERT_TRUE(state.KeepRunningBatch(cur)); - } - children.push_back(child); - } -} - -BENCHMARK(BM_CPUBoundSymmetric)->Range(2, 16)->UseRealTime(); - -// Child routine for ProcessSwitch/ThreadSwitch. -// Reads from readfd and writes the result to writefd. -void SwitchChild(int readfd, int writefd) { - while (1) { - char buf; - int ret = ReadFd(readfd, &buf, 1); - if (ret == 0) { - break; - } - TEST_CHECK_MSG(ret == 1, "read failed"); - - ret = WriteFd(writefd, &buf, 1); - if (ret == -1) { - TEST_CHECK_MSG(errno == EPIPE, "unexpected write failure"); - break; - } - TEST_CHECK_MSG(ret == 1, "write failed"); - } -} - -// Send bytes in a loop through a series of pipes, each passing through a -// different process. -// -// Proc 0 Proc 1 -// * ----------> * -// ^ Pipe 1 | -// | | -// | Pipe 0 | Pipe 2 -// | | -// | | -// | Pipe 3 v -// * <---------- * -// Proc 3 Proc 2 -// -// This exercises context switching through multiple processes. -void BM_ProcessSwitch(benchmark::State& state) { - // Code below assumes there are at least two processes. - const int num_processes = state.range(0); - ASSERT_GE(num_processes, 2); - - std::vector<pid_t> children; - auto child_cleanup = Cleanup([&] { - for (const pid_t child : children) { - int status; - EXPECT_THAT(RetryEINTR(waitpid)(child, &status, 0), SyscallSucceeds()); - EXPECT_TRUE(WIFEXITED(status)); - EXPECT_EQ(0, WEXITSTATUS(status)); - } - }); - - // Must come after children, as the FDs must be closed before the children - // will exit. - std::vector<FileDescriptor> read_fds; - std::vector<FileDescriptor> write_fds; - - for (int i = 0; i < num_processes; i++) { - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - read_fds.emplace_back(fds[0]); - write_fds.emplace_back(fds[1]); - } - - // This process is one of the processes in the loop. It will be considered - // index 0. - for (int i = 1; i < num_processes; i++) { - // Read from current pipe index, write to next. - const int read_index = i; - const int read_fd = read_fds[read_index].get(); - - const int write_index = (i + 1) % num_processes; - const int write_fd = write_fds[write_index].get(); - - // std::vector isn't safe to use from the fork child. - FileDescriptor* read_array = read_fds.data(); - FileDescriptor* write_array = write_fds.data(); - - pid_t child = fork(); - if (!child) { - // Close all other FDs. - for (int j = 0; j < num_processes; j++) { - if (j != read_index) { - read_array[j].reset(); - } - if (j != write_index) { - write_array[j].reset(); - } - } - - SwitchChild(read_fd, write_fd); - _exit(0); - } - ASSERT_THAT(child, SyscallSucceeds()); - children.push_back(child); - } - - // Read from current pipe index (0), write to next (1). - const int read_index = 0; - const int read_fd = read_fds[read_index].get(); - - const int write_index = 1; - const int write_fd = write_fds[write_index].get(); - - // Kick start the loop. - char buf = 'a'; - ASSERT_THAT(WriteFd(write_fd, &buf, 1), SyscallSucceedsWithValue(1)); - - for (auto _ : state) { - ASSERT_THAT(ReadFd(read_fd, &buf, 1), SyscallSucceedsWithValue(1)); - ASSERT_THAT(WriteFd(write_fd, &buf, 1), SyscallSucceedsWithValue(1)); - } -} - -BENCHMARK(BM_ProcessSwitch)->Range(2, 16)->UseRealTime(); - -// Equivalent to BM_ThreadSwitch using threads instead of processes. -void BM_ThreadSwitch(benchmark::State& state) { - // Code below assumes there are at least two threads. - const int num_threads = state.range(0); - ASSERT_GE(num_threads, 2); - - // Must come after threads, as the FDs must be closed before the children - // will exit. - std::vector<std::unique_ptr<ScopedThread>> threads; - std::vector<FileDescriptor> read_fds; - std::vector<FileDescriptor> write_fds; - - for (int i = 0; i < num_threads; i++) { - int fds[2]; - ASSERT_THAT(pipe(fds), SyscallSucceeds()); - read_fds.emplace_back(fds[0]); - write_fds.emplace_back(fds[1]); - } - - // This thread is one of the threads in the loop. It will be considered - // index 0. - for (int i = 1; i < num_threads; i++) { - // Read from current pipe index, write to next. - // - // Transfer ownership of the FDs to the thread. - const int read_index = i; - const int read_fd = read_fds[read_index].release(); - - const int write_index = (i + 1) % num_threads; - const int write_fd = write_fds[write_index].release(); - - threads.emplace_back(std::make_unique<ScopedThread>([read_fd, write_fd] { - FileDescriptor read(read_fd); - FileDescriptor write(write_fd); - SwitchChild(read.get(), write.get()); - })); - } - - // Read from current pipe index (0), write to next (1). - const int read_index = 0; - const int read_fd = read_fds[read_index].get(); - - const int write_index = 1; - const int write_fd = write_fds[write_index].get(); - - // Kick start the loop. - char buf = 'a'; - ASSERT_THAT(WriteFd(write_fd, &buf, 1), SyscallSucceedsWithValue(1)); - - for (auto _ : state) { - ASSERT_THAT(ReadFd(read_fd, &buf, 1), SyscallSucceedsWithValue(1)); - ASSERT_THAT(WriteFd(write_fd, &buf, 1), SyscallSucceedsWithValue(1)); - } - - // The two FDs still owned by this thread are closed, causing the next thread - // to exit its loop and close its FDs, and so on until all threads exit. -} - -BENCHMARK(BM_ThreadSwitch)->Range(2, 16)->UseRealTime(); - -void BM_ThreadStart(benchmark::State& state) { - const int num_threads = state.range(0); - - for (auto _ : state) { - state.PauseTiming(); - - auto barrier = new absl::Barrier(num_threads + 1); - std::vector<std::unique_ptr<ScopedThread>> threads; - - state.ResumeTiming(); - - for (size_t i = 0; i < num_threads; ++i) { - threads.emplace_back(std::make_unique<ScopedThread>([barrier] { - if (barrier->Block()) { - delete barrier; - } - })); - } - - if (barrier->Block()) { - delete barrier; - } - - state.PauseTiming(); - - for (const auto& thread : threads) { - thread->Join(); - } - - state.ResumeTiming(); - } -} - -BENCHMARK(BM_ThreadStart)->Range(1, 2048)->UseRealTime(); - -// Benchmark the complete fork + exit + wait. -void BM_ProcessLifecycle(benchmark::State& state) { - const int num_procs = state.range(0); - - std::vector<pid_t> pids(num_procs); - for (auto _ : state) { - for (size_t i = 0; i < num_procs; ++i) { - int pid = fork(); - if (pid == 0) { - _exit(0); - } - ASSERT_THAT(pid, SyscallSucceeds()); - pids[i] = pid; - } - - for (const int pid : pids) { - ASSERT_THAT(RetryEINTR(waitpid)(pid, nullptr, 0), - SyscallSucceedsWithValue(pid)); - } - } -} - -BENCHMARK(BM_ProcessLifecycle)->Range(1, 512)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/futex_benchmark.cc b/test/perf/linux/futex_benchmark.cc deleted file mode 100644 index b349d50bf..000000000 --- a/test/perf/linux/futex_benchmark.cc +++ /dev/null @@ -1,248 +0,0 @@ -// 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 <linux/futex.h> - -#include <atomic> -#include <cerrno> -#include <cstdint> -#include <cstdlib> -#include <ctime> - -#include "gtest/gtest.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" -#include "benchmark/benchmark.h" -#include "test/util/logging.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -inline int FutexWait(std::atomic<int32_t>* v, int32_t val) { - return syscall(SYS_futex, v, FUTEX_BITSET_MATCH_ANY, nullptr); -} - -inline int FutexWaitRelativeTimeout(std::atomic<int32_t>* v, int32_t val, - const struct timespec* reltime) { - return syscall(SYS_futex, v, FUTEX_WAIT_PRIVATE, reltime); -} - -inline int FutexWaitAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val, - const struct timespec* abstime) { - return syscall(SYS_futex, v, FUTEX_BITSET_MATCH_ANY, abstime); -} - -inline int FutexWaitBitsetAbsoluteTimeout(std::atomic<int32_t>* v, int32_t val, - int32_t bits, - const struct timespec* abstime) { - return syscall(SYS_futex, v, FUTEX_WAIT_BITSET_PRIVATE | FUTEX_CLOCK_REALTIME, - val, abstime, nullptr, bits); -} - -inline int FutexWake(std::atomic<int32_t>* v, int32_t count) { - return syscall(SYS_futex, v, FUTEX_WAKE_PRIVATE, count); -} - -// This just uses FUTEX_WAKE on an address with nothing waiting, very simple. -void BM_FutexWakeNop(benchmark::State& state) { - std::atomic<int32_t> v(0); - - for (auto _ : state) { - EXPECT_EQ(0, FutexWake(&v, 1)); - } -} - -BENCHMARK(BM_FutexWakeNop); - -// This just uses FUTEX_WAIT on an address whose value has changed, i.e., the -// syscall won't wait. -void BM_FutexWaitNop(benchmark::State& state) { - std::atomic<int32_t> v(0); - - for (auto _ : state) { - EXPECT_EQ(-EAGAIN, FutexWait(&v, 1)); - } -} - -BENCHMARK(BM_FutexWaitNop); - -// This uses FUTEX_WAIT with a timeout on an address whose value never -// changes, such that it always times out. Timeout overhead can be estimated by -// timer overruns for short timeouts. -void BM_FutexWaitTimeout(benchmark::State& state) { - const int timeout_ns = state.range(0); - std::atomic<int32_t> v(0); - auto ts = absl::ToTimespec(absl::Nanoseconds(timeout_ns)); - - for (auto _ : state) { - EXPECT_EQ(-ETIMEDOUT, FutexWaitRelativeTimeout(&v, 0, &ts)); - } -} - -BENCHMARK(BM_FutexWaitTimeout) - ->Arg(1) - ->Arg(10) - ->Arg(100) - ->Arg(1000) - ->Arg(10000); - -// This calls FUTEX_WAIT_BITSET with CLOCK_REALTIME. -void BM_FutexWaitBitset(benchmark::State& state) { - std::atomic<int32_t> v(0); - int timeout_ns = state.range(0); - auto ts = absl::ToTimespec(absl::Nanoseconds(timeout_ns)); - for (auto _ : state) { - EXPECT_EQ(-ETIMEDOUT, FutexWaitBitsetAbsoluteTimeout(&v, 0, 1, &ts)); - } -} - -BENCHMARK(BM_FutexWaitBitset)->Range(0, 100000); - -int64_t GetCurrentMonotonicTimeNanos() { - struct timespec ts; - TEST_CHECK(clock_gettime(CLOCK_MONOTONIC, &ts) != -1); - return ts.tv_sec * 1000000000ULL + ts.tv_nsec; -} - -void SpinNanos(int64_t delay_ns) { - if (delay_ns <= 0) { - return; - } - const int64_t end = GetCurrentMonotonicTimeNanos() + delay_ns; - while (GetCurrentMonotonicTimeNanos() < end) { - // spin - } -} - -// Each iteration of FutexRoundtripDelayed involves a thread sending a futex -// wakeup to another thread, which spins for delay_us and then sends a futex -// wakeup back. The time per iteration is 2* (delay_us + kBeforeWakeDelayNs + -// futex/scheduling overhead). -void BM_FutexRoundtripDelayed(benchmark::State& state) { - const int delay_us = state.range(0); - - const int64_t delay_ns = delay_us * 1000; - // Spin for an extra kBeforeWakeDelayNs before invoking FUTEX_WAKE to reduce - // the probability that the wakeup comes before the wait, preventing the wait - // from ever taking effect and causing the benchmark to underestimate the - // actual wakeup time. - constexpr int64_t kBeforeWakeDelayNs = 500; - std::atomic<int32_t> v(0); - ScopedThread t([&] { - for (int i = 0; i < state.max_iterations; i++) { - SpinNanos(delay_ns); - while (v.load(std::memory_order_acquire) == 0) { - FutexWait(&v, 0); - } - SpinNanos(kBeforeWakeDelayNs + delay_ns); - v.store(0, std::memory_order_release); - FutexWake(&v, 1); - } - }); - for (auto _ : state) { - SpinNanos(kBeforeWakeDelayNs + delay_ns); - v.store(1, std::memory_order_release); - FutexWake(&v, 1); - SpinNanos(delay_ns); - while (v.load(std::memory_order_acquire) == 1) { - FutexWait(&v, 1); - } - } -} - -BENCHMARK(BM_FutexRoundtripDelayed) - ->Arg(0) - ->Arg(10) - ->Arg(20) - ->Arg(50) - ->Arg(100); - -// FutexLock is a simple, dumb futex based lock implementation. -// It will try to acquire the lock by atomically incrementing the -// lock word. If it did not increment the lock from 0 to 1, someone -// else has the lock, so it will FUTEX_WAIT until it is woken in -// the unlock path. -class FutexLock { - public: - FutexLock() : lock_word_(0) {} - - void lock(struct timespec* deadline) { - int32_t val; - while ((val = lock_word_.fetch_add(1, std::memory_order_acquire) + 1) != - 1) { - // If we didn't get the lock by incrementing from 0 to 1, - // do a FUTEX_WAIT with the desired current value set to - // val. If val is no longer what the atomic increment returned, - // someone might have set it to 0 so we can try to acquire - // again. - int ret = FutexWaitAbsoluteTimeout(&lock_word_, val, deadline); - if (ret == 0 || ret == -EWOULDBLOCK || ret == -EINTR) { - continue; - } else { - FAIL() << "unexpected FUTEX_WAIT return: " << ret; - } - } - } - - void unlock() { - // Store 0 into the lock word and wake one waiter. We intentionally - // ignore the return value of the FUTEX_WAKE here, since there may be - // no waiters to wake anyway. - lock_word_.store(0, std::memory_order_release); - (void)FutexWake(&lock_word_, 1); - } - - private: - std::atomic<int32_t> lock_word_; -}; - -FutexLock* test_lock; // Used below. - -void FutexContend(benchmark::State& state, int thread_index, - struct timespec* deadline) { - int counter = 0; - if (thread_index == 0) { - test_lock = new FutexLock(); - } - for (auto _ : state) { - test_lock->lock(deadline); - counter++; - test_lock->unlock(); - } - if (thread_index == 0) { - delete test_lock; - } - state.SetItemsProcessed(state.iterations()); -} - -void BM_FutexContend(benchmark::State& state) { - FutexContend(state, state.thread_index, nullptr); -} - -BENCHMARK(BM_FutexContend)->ThreadRange(1, 1024)->UseRealTime(); - -void BM_FutexDeadlineContend(benchmark::State& state) { - auto deadline = absl::ToTimespec(absl::Now() + absl::Minutes(10)); - FutexContend(state, state.thread_index, &deadline); -} - -BENCHMARK(BM_FutexDeadlineContend)->ThreadRange(1, 1024)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/getdents_benchmark.cc b/test/perf/linux/getdents_benchmark.cc deleted file mode 100644 index afc599ad2..000000000 --- a/test/perf/linux/getdents_benchmark.cc +++ /dev/null @@ -1,149 +0,0 @@ -// 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 <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#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 = 16384; - -PosixErrorOr<TempPath> CreateDirectory(int count, - std::vector<std::string>* 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<std::string>* 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<std::string> 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<std::string> 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 diff --git a/test/perf/linux/getpid_benchmark.cc b/test/perf/linux/getpid_benchmark.cc deleted file mode 100644 index db74cb264..000000000 --- a/test/perf/linux/getpid_benchmark.cc +++ /dev/null @@ -1,37 +0,0 @@ -// 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 <sys/syscall.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" - -namespace gvisor { -namespace testing { - -namespace { - -void BM_Getpid(benchmark::State& state) { - for (auto _ : state) { - syscall(SYS_getpid); - } -} - -BENCHMARK(BM_Getpid); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/gettid_benchmark.cc b/test/perf/linux/gettid_benchmark.cc deleted file mode 100644 index 8f4961f5e..000000000 --- a/test/perf/linux/gettid_benchmark.cc +++ /dev/null @@ -1,38 +0,0 @@ -// 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 <sys/syscall.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" - -namespace gvisor { -namespace testing { - -namespace { - -void BM_Gettid(benchmark::State& state) { - for (auto _ : state) { - syscall(SYS_gettid); - } -} - -BENCHMARK(BM_Gettid)->ThreadRange(1, 4000)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/mapping_benchmark.cc b/test/perf/linux/mapping_benchmark.cc deleted file mode 100644 index 39c30fe69..000000000 --- a/test/perf/linux/mapping_benchmark.cc +++ /dev/null @@ -1,163 +0,0 @@ -// 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 <stdlib.h> -#include <sys/mman.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" -#include "test/util/logging.h" -#include "test/util/memory_util.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Conservative value for /proc/sys/vm/max_map_count, which limits the number of -// VMAs, minus a safety margin for VMAs that already exist for the test binary. -// The default value for max_map_count is -// include/linux/mm.h:DEFAULT_MAX_MAP_COUNT = 65530. -constexpr size_t kMaxVMAs = 64001; - -// Map then unmap pages without touching them. -void BM_MapUnmap(benchmark::State& state) { - // Number of pages to map. - const int pages = state.range(0); - - while (state.KeepRunning()) { - void* addr = mmap(0, pages * kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - TEST_CHECK_MSG(addr != MAP_FAILED, "mmap failed"); - - int ret = munmap(addr, pages * kPageSize); - TEST_CHECK_MSG(ret == 0, "munmap failed"); - } -} - -BENCHMARK(BM_MapUnmap)->Range(1, 1 << 17)->UseRealTime(); - -// Map, touch, then unmap pages. -void BM_MapTouchUnmap(benchmark::State& state) { - // Number of pages to map. - const int pages = state.range(0); - - while (state.KeepRunning()) { - void* addr = mmap(0, pages * kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - TEST_CHECK_MSG(addr != MAP_FAILED, "mmap failed"); - - char* c = reinterpret_cast<char*>(addr); - char* end = c + pages * kPageSize; - while (c < end) { - *c = 42; - c += kPageSize; - } - - int ret = munmap(addr, pages * kPageSize); - TEST_CHECK_MSG(ret == 0, "munmap failed"); - } -} - -BENCHMARK(BM_MapTouchUnmap)->Range(1, 1 << 17)->UseRealTime(); - -// Map and touch many pages, unmapping all at once. -// -// NOTE(b/111429208): This is a regression test to ensure performant mapping and -// allocation even with tons of mappings. -void BM_MapTouchMany(benchmark::State& state) { - // Number of pages to map. - const int page_count = state.range(0); - - while (state.KeepRunning()) { - std::vector<void*> pages; - - for (int i = 0; i < page_count; i++) { - void* addr = mmap(nullptr, kPageSize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - TEST_CHECK_MSG(addr != MAP_FAILED, "mmap failed"); - - char* c = reinterpret_cast<char*>(addr); - *c = 42; - - pages.push_back(addr); - } - - for (void* addr : pages) { - int ret = munmap(addr, kPageSize); - TEST_CHECK_MSG(ret == 0, "munmap failed"); - } - } - - state.SetBytesProcessed(kPageSize * page_count * state.iterations()); -} - -BENCHMARK(BM_MapTouchMany)->Range(1, 1 << 12)->UseRealTime(); - -void BM_PageFault(benchmark::State& state) { - // Map the region in which we will take page faults. To ensure that each page - // fault maps only a single page, each page we touch must correspond to a - // distinct VMA. Thus we need a 1-page gap between each 1-page VMA. However, - // each gap consists of a PROT_NONE VMA, instead of an unmapped hole, so that - // if there are background threads running, they can't inadvertently creating - // mappings in our gaps that are unmapped when the test ends. - size_t test_pages = kMaxVMAs; - // Ensure that test_pages is odd, since we want the test region to both - // begin and end with a mapped page. - if (test_pages % 2 == 0) { - test_pages--; - } - const size_t test_region_bytes = test_pages * kPageSize; - // Use MAP_SHARED here because madvise(MADV_DONTNEED) on private mappings on - // gVisor won't force future sentry page faults (by design). Use MAP_POPULATE - // so that Linux pre-allocates the shmem file used to back the mapping. - Mapping m = ASSERT_NO_ERRNO_AND_VALUE( - MmapAnon(test_region_bytes, PROT_READ, MAP_SHARED | MAP_POPULATE)); - for (size_t i = 0; i < test_pages / 2; i++) { - ASSERT_THAT( - mprotect(reinterpret_cast<void*>(m.addr() + ((2 * i + 1) * kPageSize)), - kPageSize, PROT_NONE), - SyscallSucceeds()); - } - - const size_t mapped_pages = test_pages / 2 + 1; - // "Start" at the end of the mapped region to force the mapped region to be - // reset, since we mapped it with MAP_POPULATE. - size_t cur_page = mapped_pages; - for (auto _ : state) { - if (cur_page >= mapped_pages) { - // We've reached the end of our mapped region and have to reset it to - // incur page faults again. - state.PauseTiming(); - ASSERT_THAT(madvise(m.ptr(), test_region_bytes, MADV_DONTNEED), - SyscallSucceeds()); - cur_page = 0; - state.ResumeTiming(); - } - const uintptr_t addr = m.addr() + (2 * cur_page * kPageSize); - const char c = *reinterpret_cast<volatile char*>(addr); - benchmark::DoNotOptimize(c); - cur_page++; - } -} - -BENCHMARK(BM_PageFault)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/open_benchmark.cc b/test/perf/linux/open_benchmark.cc deleted file mode 100644 index 68008f6d5..000000000 --- a/test/perf/linux/open_benchmark.cc +++ /dev/null @@ -1,56 +0,0 @@ -// 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 <fcntl.h> -#include <stdlib.h> -#include <unistd.h> - -#include <memory> -#include <string> -#include <vector> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" -#include "test/util/fs_util.h" -#include "test/util/logging.h" -#include "test/util/temp_path.h" - -namespace gvisor { -namespace testing { - -namespace { - -void BM_Open(benchmark::State& state) { - const int size = state.range(0); - std::vector<TempPath> cache; - for (int i = 0; i < size; i++) { - auto path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - cache.emplace_back(std::move(path)); - } - - unsigned int seed = 1; - for (auto _ : state) { - const int chosen = rand_r(&seed) % size; - int fd = open(cache[chosen].path().c_str(), O_RDONLY); - TEST_CHECK(fd != -1); - close(fd); - } -} - -BENCHMARK(BM_Open)->Range(1, 128)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/pipe_benchmark.cc b/test/perf/linux/pipe_benchmark.cc deleted file mode 100644 index 8f5f6a2a3..000000000 --- a/test/perf/linux/pipe_benchmark.cc +++ /dev/null @@ -1,66 +0,0 @@ -// 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 <stdlib.h> -#include <sys/stat.h> -#include <unistd.h> - -#include <cerrno> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" -#include "test/util/logging.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -void BM_Pipe(benchmark::State& state) { - int fds[2]; - TEST_CHECK(pipe(fds) == 0); - - const int size = state.range(0); - std::vector<char> wbuf(size); - std::vector<char> rbuf(size); - RandomizeBuffer(wbuf.data(), size); - - ScopedThread t([&] { - auto const fd = fds[1]; - for (int i = 0; i < state.max_iterations; i++) { - TEST_CHECK(WriteFd(fd, wbuf.data(), wbuf.size()) == size); - } - }); - - for (auto _ : state) { - TEST_CHECK(ReadFd(fds[0], rbuf.data(), rbuf.size()) == size); - } - - t.Join(); - - close(fds[0]); - close(fds[1]); - - state.SetBytesProcessed(static_cast<int64_t>(size) * - static_cast<int64_t>(state.iterations())); -} - -BENCHMARK(BM_Pipe)->Range(1, 1 << 20)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/randread_benchmark.cc b/test/perf/linux/randread_benchmark.cc deleted file mode 100644 index b0eb8c24e..000000000 --- a/test/perf/linux/randread_benchmark.cc +++ /dev/null @@ -1,100 +0,0 @@ -// 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 <fcntl.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <sys/uio.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" -#include "test/util/file_descriptor.h" -#include "test/util/logging.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Create a 1GB file that will be read from at random positions. This should -// invalid any performance gains from caching. -const uint64_t kFileSize = 1ULL << 30; - -// How many bytes to write at once to initialize the file used to read from. -const uint32_t kWriteSize = 65536; - -// Largest benchmarked read unit. -const uint32_t kMaxRead = 1UL << 26; - -TempPath CreateFile(uint64_t file_size) { - auto path = TempPath::CreateFile().ValueOrDie(); - FileDescriptor fd = Open(path.path(), O_WRONLY).ValueOrDie(); - - // Try to minimize syscalls by using maximum size writev() requests. - std::vector<char> buffer(kWriteSize); - RandomizeBuffer(buffer.data(), buffer.size()); - const std::vector<std::vector<struct iovec>> iovecs_list = - GenerateIovecs(file_size, buffer.data(), buffer.size()); - for (const auto& iovecs : iovecs_list) { - TEST_CHECK(writev(fd.get(), iovecs.data(), iovecs.size()) >= 0); - } - - return path; -} - -// Global test state, initialized once per process lifetime. -struct GlobalState { - const TempPath tmpfile; - explicit GlobalState(TempPath tfile) : tmpfile(std::move(tfile)) {} -}; - -GlobalState& GetGlobalState() { - // This gets created only once throughout the lifetime of the process. - // Use a dynamically allocated object (that is never deleted) to avoid order - // of destruction of static storage variables issues. - static GlobalState* const state = - // The actual file size is the maximum random seek range (kFileSize) + the - // maximum read size so we can read that number of bytes at the end of the - // file. - new GlobalState(CreateFile(kFileSize + kMaxRead)); - return *state; -} - -void BM_RandRead(benchmark::State& state) { - const int size = state.range(0); - - GlobalState& global_state = GetGlobalState(); - FileDescriptor fd = - ASSERT_NO_ERRNO_AND_VALUE(Open(global_state.tmpfile.path(), O_RDONLY)); - std::vector<char> buf(size); - - unsigned int seed = 1; - for (auto _ : state) { - TEST_CHECK(PreadFd(fd.get(), buf.data(), buf.size(), - rand_r(&seed) % kFileSize) == size); - } - - state.SetBytesProcessed(static_cast<int64_t>(size) * - static_cast<int64_t>(state.iterations())); -} - -BENCHMARK(BM_RandRead)->Range(1, kMaxRead)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/read_benchmark.cc b/test/perf/linux/read_benchmark.cc deleted file mode 100644 index 62445867d..000000000 --- a/test/perf/linux/read_benchmark.cc +++ /dev/null @@ -1,53 +0,0 @@ -// 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 <fcntl.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" -#include "test/util/fs_util.h" -#include "test/util/logging.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -void BM_Read(benchmark::State& state) { - const int size = state.range(0); - const std::string contents(size, 0); - auto path = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileWith( - GetAbsoluteTestTmpdir(), contents, TempPath::kDefaultFileMode)); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(path.path(), O_RDONLY)); - - std::vector<char> buf(size); - for (auto _ : state) { - TEST_CHECK(PreadFd(fd.get(), buf.data(), buf.size(), 0) == size); - } - - state.SetBytesProcessed(static_cast<int64_t>(size) * - static_cast<int64_t>(state.iterations())); -} - -BENCHMARK(BM_Read)->Range(1, 1 << 26)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/sched_yield_benchmark.cc b/test/perf/linux/sched_yield_benchmark.cc deleted file mode 100644 index 6756b5575..000000000 --- a/test/perf/linux/sched_yield_benchmark.cc +++ /dev/null @@ -1,37 +0,0 @@ -// 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 <sched.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -void BM_Sched_yield(benchmark::State& state) { - for (auto ignored : state) { - TEST_CHECK(sched_yield() == 0); - } -} - -BENCHMARK(BM_Sched_yield)->ThreadRange(1, 2000)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/send_recv_benchmark.cc b/test/perf/linux/send_recv_benchmark.cc deleted file mode 100644 index d73e49523..000000000 --- a/test/perf/linux/send_recv_benchmark.cc +++ /dev/null @@ -1,372 +0,0 @@ -// 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 <netinet/in.h> -#include <netinet/tcp.h> -#include <poll.h> -#include <sys/ioctl.h> -#include <sys/socket.h> - -#include <cstring> - -#include "gtest/gtest.h" -#include "absl/synchronization/notification.h" -#include "benchmark/benchmark.h" -#include "test/syscalls/linux/socket_test_util.h" -#include "test/util/file_descriptor.h" -#include "test/util/logging.h" -#include "test/util/posix_error.h" -#include "test/util/test_util.h" -#include "test/util/thread_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -constexpr ssize_t kMessageSize = 1024; - -class Message { - public: - explicit Message(int byte = 0) : Message(byte, kMessageSize, 0) {} - - explicit Message(int byte, int sz) : Message(byte, sz, 0) {} - - explicit Message(int byte, int sz, int cmsg_sz) - : buffer_(sz, byte), cmsg_buffer_(cmsg_sz, 0) { - iov_.iov_base = buffer_.data(); - iov_.iov_len = sz; - hdr_.msg_iov = &iov_; - hdr_.msg_iovlen = 1; - hdr_.msg_control = cmsg_buffer_.data(); - hdr_.msg_controllen = cmsg_sz; - } - - struct msghdr* header() { - return &hdr_; - } - - private: - std::vector<char> buffer_; - std::vector<char> cmsg_buffer_; - struct iovec iov_ = {}; - struct msghdr hdr_ = {}; -}; - -void BM_Recvmsg(benchmark::State& state) { - int sockets[2]; - TEST_CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0); - FileDescriptor send_socket(sockets[0]), recv_socket(sockets[1]); - absl::Notification notification; - Message send_msg('a'), recv_msg; - - ScopedThread t([&send_msg, &send_socket, ¬ification] { - while (!notification.HasBeenNotified()) { - sendmsg(send_socket.get(), send_msg.header(), 0); - } - }); - - int64_t bytes_received = 0; - for (auto ignored : state) { - int n = recvmsg(recv_socket.get(), recv_msg.header(), 0); - TEST_CHECK(n > 0); - bytes_received += n; - } - - notification.Notify(); - recv_socket.reset(); - - state.SetBytesProcessed(bytes_received); -} - -BENCHMARK(BM_Recvmsg)->UseRealTime(); - -void BM_Sendmsg(benchmark::State& state) { - int sockets[2]; - TEST_CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0); - FileDescriptor send_socket(sockets[0]), recv_socket(sockets[1]); - absl::Notification notification; - Message send_msg('a'), recv_msg; - - ScopedThread t([&recv_msg, &recv_socket, ¬ification] { - while (!notification.HasBeenNotified()) { - recvmsg(recv_socket.get(), recv_msg.header(), 0); - } - }); - - int64_t bytes_sent = 0; - for (auto ignored : state) { - int n = sendmsg(send_socket.get(), send_msg.header(), 0); - TEST_CHECK(n > 0); - bytes_sent += n; - } - - notification.Notify(); - send_socket.reset(); - - state.SetBytesProcessed(bytes_sent); -} - -BENCHMARK(BM_Sendmsg)->UseRealTime(); - -void BM_Recvfrom(benchmark::State& state) { - int sockets[2]; - TEST_CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0); - FileDescriptor send_socket(sockets[0]), recv_socket(sockets[1]); - absl::Notification notification; - char send_buffer[kMessageSize], recv_buffer[kMessageSize]; - - ScopedThread t([&send_socket, &send_buffer, ¬ification] { - while (!notification.HasBeenNotified()) { - sendto(send_socket.get(), send_buffer, kMessageSize, 0, nullptr, 0); - } - }); - - int bytes_received = 0; - for (auto ignored : state) { - int n = recvfrom(recv_socket.get(), recv_buffer, kMessageSize, 0, nullptr, - nullptr); - TEST_CHECK(n > 0); - bytes_received += n; - } - - notification.Notify(); - recv_socket.reset(); - - state.SetBytesProcessed(bytes_received); -} - -BENCHMARK(BM_Recvfrom)->UseRealTime(); - -void BM_Sendto(benchmark::State& state) { - int sockets[2]; - TEST_CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == 0); - FileDescriptor send_socket(sockets[0]), recv_socket(sockets[1]); - absl::Notification notification; - char send_buffer[kMessageSize], recv_buffer[kMessageSize]; - - ScopedThread t([&recv_socket, &recv_buffer, ¬ification] { - while (!notification.HasBeenNotified()) { - recvfrom(recv_socket.get(), recv_buffer, kMessageSize, 0, nullptr, - nullptr); - } - }); - - int64_t bytes_sent = 0; - for (auto ignored : state) { - int n = sendto(send_socket.get(), send_buffer, kMessageSize, 0, nullptr, 0); - TEST_CHECK(n > 0); - bytes_sent += n; - } - - notification.Notify(); - send_socket.reset(); - - state.SetBytesProcessed(bytes_sent); -} - -BENCHMARK(BM_Sendto)->UseRealTime(); - -PosixErrorOr<sockaddr_storage> InetLoopbackAddr(int family) { - struct sockaddr_storage addr; - memset(&addr, 0, sizeof(addr)); - addr.ss_family = family; - switch (family) { - case AF_INET: - reinterpret_cast<struct sockaddr_in*>(&addr)->sin_addr.s_addr = - htonl(INADDR_LOOPBACK); - break; - case AF_INET6: - reinterpret_cast<struct sockaddr_in6*>(&addr)->sin6_addr = - in6addr_loopback; - break; - default: - return PosixError(EINVAL, - absl::StrCat("unknown socket family: ", family)); - } - return addr; -} - -// BM_RecvmsgWithControlBuf measures the performance of recvmsg when we allocate -// space for control messages. Note that we do not expect to receive any. -void BM_RecvmsgWithControlBuf(benchmark::State& state) { - auto listen_socket = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)); - - // Initialize address to the loopback one. - sockaddr_storage addr = ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(AF_INET6)); - socklen_t addrlen = sizeof(addr); - - // Bind to some port then start listening. - ASSERT_THAT(bind(listen_socket.get(), - reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - ASSERT_THAT(listen(listen_socket.get(), SOMAXCONN), SyscallSucceeds()); - - // Get the address we're listening on, then connect to it. We need to do this - // because we're allowing the stack to pick a port for us. - ASSERT_THAT(getsockname(listen_socket.get(), - reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - - auto send_socket = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)); - - ASSERT_THAT( - RetryEINTR(connect)(send_socket.get(), - reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - // Accept the connection. - auto recv_socket = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_socket.get(), nullptr, nullptr)); - - absl::Notification notification; - Message send_msg('a'); - // Create a msghdr with a buffer allocated for control messages. - Message recv_msg(0, kMessageSize, /*cmsg_sz=*/24); - - ScopedThread t([&send_msg, &send_socket, ¬ification] { - while (!notification.HasBeenNotified()) { - sendmsg(send_socket.get(), send_msg.header(), 0); - } - }); - - int64_t bytes_received = 0; - for (auto ignored : state) { - int n = recvmsg(recv_socket.get(), recv_msg.header(), 0); - TEST_CHECK(n > 0); - bytes_received += n; - } - - notification.Notify(); - recv_socket.reset(); - - state.SetBytesProcessed(bytes_received); -} - -BENCHMARK(BM_RecvmsgWithControlBuf)->UseRealTime(); - -// BM_SendmsgTCP measures the sendmsg throughput with varying payload sizes. -// -// state.Args[0] indicates whether the underlying socket should be blocking or -// non-blocking w/ 0 indicating non-blocking and 1 to indicate blocking. -// state.Args[1] is the size of the payload to be used per sendmsg call. -void BM_SendmsgTCP(benchmark::State& state) { - auto listen_socket = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); - - // Initialize address to the loopback one. - sockaddr_storage addr = ASSERT_NO_ERRNO_AND_VALUE(InetLoopbackAddr(AF_INET)); - socklen_t addrlen = sizeof(addr); - - // Bind to some port then start listening. - ASSERT_THAT(bind(listen_socket.get(), - reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - ASSERT_THAT(listen(listen_socket.get(), SOMAXCONN), SyscallSucceeds()); - - // Get the address we're listening on, then connect to it. We need to do this - // because we're allowing the stack to pick a port for us. - ASSERT_THAT(getsockname(listen_socket.get(), - reinterpret_cast<struct sockaddr*>(&addr), &addrlen), - SyscallSucceeds()); - - auto send_socket = - ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); - - ASSERT_THAT( - RetryEINTR(connect)(send_socket.get(), - reinterpret_cast<struct sockaddr*>(&addr), addrlen), - SyscallSucceeds()); - - // Accept the connection. - auto recv_socket = - ASSERT_NO_ERRNO_AND_VALUE(Accept(listen_socket.get(), nullptr, nullptr)); - - // Check if we want to run the test w/ a blocking send socket - // or non-blocking. - const int blocking = state.range(0); - if (!blocking) { - // Set the send FD to O_NONBLOCK. - int opts; - ASSERT_THAT(opts = fcntl(send_socket.get(), F_GETFL), SyscallSucceeds()); - opts |= O_NONBLOCK; - ASSERT_THAT(fcntl(send_socket.get(), F_SETFL, opts), SyscallSucceeds()); - } - - absl::Notification notification; - - // Get the buffer size we should use for this iteration of the test. - const int buf_size = state.range(1); - Message send_msg('a', buf_size), recv_msg(0, buf_size); - - ScopedThread t([&recv_msg, &recv_socket, ¬ification] { - while (!notification.HasBeenNotified()) { - TEST_CHECK(recvmsg(recv_socket.get(), recv_msg.header(), 0) >= 0); - } - }); - - int64_t bytes_sent = 0; - int ncalls = 0; - for (auto ignored : state) { - int sent = 0; - while (true) { - struct msghdr hdr = {}; - struct iovec iov = {}; - struct msghdr* snd_header = send_msg.header(); - iov.iov_base = static_cast<char*>(snd_header->msg_iov->iov_base) + sent; - iov.iov_len = snd_header->msg_iov->iov_len - sent; - hdr.msg_iov = &iov; - hdr.msg_iovlen = 1; - int n = RetryEINTR(sendmsg)(send_socket.get(), &hdr, 0); - ncalls++; - if (n > 0) { - sent += n; - if (sent == buf_size) { - break; - } - // n can be > 0 but less than requested size. In which case we don't - // poll. - continue; - } - // Poll the fd for it to become writable. - struct pollfd poll_fd = {send_socket.get(), POLL_OUT, 0}; - EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, 10), - SyscallSucceedsWithValue(0)); - } - bytes_sent += static_cast<int64_t>(sent); - } - - notification.Notify(); - send_socket.reset(); - state.SetBytesProcessed(bytes_sent); -} - -void Args(benchmark::internal::Benchmark* benchmark) { - for (int blocking = 0; blocking < 2; blocking++) { - for (int buf_size = 1024; buf_size <= 256 << 20; buf_size *= 2) { - benchmark->Args({blocking, buf_size}); - } - } -} - -BENCHMARK(BM_SendmsgTCP)->Apply(&Args)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/seqwrite_benchmark.cc b/test/perf/linux/seqwrite_benchmark.cc deleted file mode 100644 index af49e4477..000000000 --- a/test/perf/linux/seqwrite_benchmark.cc +++ /dev/null @@ -1,66 +0,0 @@ -// 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 <fcntl.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" -#include "test/util/logging.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// The maximum file size of the test file, when writes get beyond this point -// they wrap around. This should be large enough to blow away caches. -const uint64_t kMaxFile = 1 << 30; - -// Perform writes of various sizes sequentially to one file. Wraps around if it -// goes above a certain maximum file size. -void BM_SeqWrite(benchmark::State& state) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_WRONLY)); - - const int size = state.range(0); - std::vector<char> buf(size); - RandomizeBuffer(buf.data(), buf.size()); - - // Start writes at offset 0. - uint64_t offset = 0; - for (auto _ : state) { - TEST_CHECK(PwriteFd(fd.get(), buf.data(), buf.size(), offset) == - buf.size()); - offset += buf.size(); - // Wrap around if going above the maximum file size. - if (offset >= kMaxFile) { - offset = 0; - } - } - - state.SetBytesProcessed(static_cast<int64_t>(size) * - static_cast<int64_t>(state.iterations())); -} - -BENCHMARK(BM_SeqWrite)->Range(1, 1 << 26)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/signal_benchmark.cc b/test/perf/linux/signal_benchmark.cc deleted file mode 100644 index cec679191..000000000 --- a/test/perf/linux/signal_benchmark.cc +++ /dev/null @@ -1,61 +0,0 @@ -// 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 <signal.h> -#include <string.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" -#include "test/util/logging.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -void FixupHandler(int sig, siginfo_t* si, void* void_ctx) { - static unsigned int dataval = 0; - - // Skip the offending instruction. - ucontext_t* ctx = reinterpret_cast<ucontext_t*>(void_ctx); - ctx->uc_mcontext.gregs[REG_RAX] = reinterpret_cast<greg_t>(&dataval); -} - -void BM_FaultSignalFixup(benchmark::State& state) { - // Set up the signal handler. - struct sigaction sa = {}; - sigemptyset(&sa.sa_mask); - sa.sa_sigaction = FixupHandler; - sa.sa_flags = SA_SIGINFO; - TEST_CHECK(sigaction(SIGSEGV, &sa, nullptr) == 0); - - // Fault, fault, fault. - for (auto _ : state) { - // Trigger the segfault. - asm volatile( - "movq $0, %%rax\n" - "movq $0x77777777, (%%rax)\n" - : - : - : "rax"); - } -} - -BENCHMARK(BM_FaultSignalFixup)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/sleep_benchmark.cc b/test/perf/linux/sleep_benchmark.cc deleted file mode 100644 index 99ef05117..000000000 --- a/test/perf/linux/sleep_benchmark.cc +++ /dev/null @@ -1,60 +0,0 @@ -// 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 <errno.h> -#include <sys/syscall.h> -#include <time.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" -#include "test/util/logging.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Sleep for 'param' nanoseconds. -void BM_Sleep(benchmark::State& state) { - const int nanoseconds = state.range(0); - - for (auto _ : state) { - struct timespec ts; - ts.tv_sec = 0; - ts.tv_nsec = nanoseconds; - - int ret; - do { - ret = syscall(SYS_nanosleep, &ts, &ts); - if (ret < 0) { - TEST_CHECK(errno == EINTR); - } - } while (ret < 0); - } -} - -BENCHMARK(BM_Sleep) - ->Arg(0) - ->Arg(1) - ->Arg(1000) // 1us - ->Arg(1000 * 1000) // 1ms - ->Arg(10 * 1000 * 1000) // 10ms - ->Arg(50 * 1000 * 1000) // 50ms - ->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/stat_benchmark.cc b/test/perf/linux/stat_benchmark.cc deleted file mode 100644 index f15424482..000000000 --- a/test/perf/linux/stat_benchmark.cc +++ /dev/null @@ -1,62 +0,0 @@ -// 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 <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "absl/strings/str_cat.h" -#include "benchmark/benchmark.h" -#include "test/util/fs_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Creates a file in a nested directory hierarchy at least `depth` directories -// deep, and stats that file multiple times. -void BM_Stat(benchmark::State& state) { - // Create nested directories with given depth. - int depth = state.range(0); - const TempPath top_dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - std::string dir_path = top_dir.path(); - - while (depth-- > 0) { - // Don't use TempPath because it will make paths too long to use. - // - // The top_dir destructor will clean up this whole tree. - dir_path = JoinPath(dir_path, absl::StrCat(depth)); - ASSERT_NO_ERRNO(Mkdir(dir_path, 0755)); - } - - // Create the file that will be stat'd. - const TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir_path)); - - struct stat st; - for (auto _ : state) { - ASSERT_THAT(stat(file.path().c_str(), &st), SyscallSucceeds()); - } -} - -BENCHMARK(BM_Stat)->Range(1, 100)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/unlink_benchmark.cc b/test/perf/linux/unlink_benchmark.cc deleted file mode 100644 index 92243a042..000000000 --- a/test/perf/linux/unlink_benchmark.cc +++ /dev/null @@ -1,66 +0,0 @@ -// 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 <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" -#include "test/util/fs_util.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -// Creates a directory containing `files` files, and unlinks all the files. -void BM_Unlink(benchmark::State& state) { - // Create directory with given files. - const int file_count = state.range(0); - - // We unlink all files on each iteration, but report this as a "batch" - // iteration so that reported times are per file. - TempPath dir; - while (state.KeepRunningBatch(file_count)) { - state.PauseTiming(); - // N.B. dir is declared outside the loop so that destruction of the previous - // iteration's directory occurs here, inside of PauseTiming. - dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir()); - - std::vector<TempPath> files; - for (int i = 0; i < file_count; i++) { - TempPath file = - ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFileIn(dir.path())); - files.push_back(std::move(file)); - } - state.ResumeTiming(); - - while (!files.empty()) { - // Destructor unlinks. - files.pop_back(); - } - } - - state.SetItemsProcessed(state.iterations()); -} - -BENCHMARK(BM_Unlink)->Range(1, 100 * 1000)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor diff --git a/test/perf/linux/write_benchmark.cc b/test/perf/linux/write_benchmark.cc deleted file mode 100644 index 7b060c70e..000000000 --- a/test/perf/linux/write_benchmark.cc +++ /dev/null @@ -1,52 +0,0 @@ -// 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 <fcntl.h> -#include <stdlib.h> -#include <sys/stat.h> -#include <unistd.h> - -#include "gtest/gtest.h" -#include "benchmark/benchmark.h" -#include "test/util/logging.h" -#include "test/util/temp_path.h" -#include "test/util/test_util.h" - -namespace gvisor { -namespace testing { - -namespace { - -void BM_Write(benchmark::State& state) { - auto f = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile()); - FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(f.path(), O_WRONLY)); - - const int size = state.range(0); - std::vector<char> buf(size); - RandomizeBuffer(buf.data(), size); - - for (auto _ : state) { - TEST_CHECK(PwriteFd(fd.get(), buf.data(), size, 0) == size); - } - - state.SetBytesProcessed(static_cast<int64_t>(size) * - static_cast<int64_t>(state.iterations())); -} - -BENCHMARK(BM_Write)->Range(1, 1 << 26)->UseRealTime(); - -} // namespace - -} // namespace testing -} // namespace gvisor |