text: /*
   * Copyright 2012 Facebook, Inc.
   *
   * 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 "folly/test/function_benchmark/benchmark_impl.h"
  #include "folly/test/function_benchmark/test_functions.h"
  
  #include "folly/Benchmark.h"
  #include "folly/ScopeGuard.h"
  #include <gflags/gflags.h>
  #include <glog/logging.h>
  
  using folly::ScopeGuard;
  using folly::makeGuard;
  
  // Declare the bm_max_iters flag from folly/Benchmark.cpp
  DECLARE_int32(bm_max_iters);
  
  // Directly invoking a function
  BENCHMARK(fn_invoke, iters) {
    for (int n = 0; n < iters; ++n) {
      doNothing();
    }
  }
  
  // Invoking a function through a function pointer
  BENCHMARK(fn_ptr_invoke, iters) {
    BM_fn_ptr_invoke_impl(iters, doNothing);
  }
  
  // Invoking a function through a std::function object
  BENCHMARK(std_function_invoke, iters) {
    BM_std_function_invoke_impl(iters, doNothing);
  }
  
  // Invoking a member function through a member function pointer
  BENCHMARK(mem_fn_invoke, iters) {
    TestClass tc;
    BM_mem_fn_invoke_impl(iters, &tc, &TestClass::doNothing);
  }
  
  // Invoke a function pointer through an inlined wrapper function
  BENCHMARK(fn_ptr_invoke_through_inline, iters) {
    BM_fn_ptr_invoke_inlined_impl(iters, doNothing);
  }
  
  // Invoke a lambda that calls doNothing() through an inlined wrapper function
  BENCHMARK(lambda_invoke_fn, iters) {
    BM_invoke_fn_template_impl(iters, [] { doNothing(); });
  }
  
  // Invoke a lambda that does nothing
  BENCHMARK(lambda_noop, iters) {
    BM_invoke_fn_template_impl(iters, [] {});
  }
  
  // Invoke a lambda that modifies a local variable
  BENCHMARK(lambda_local_var, iters) {
    uint32_t count1 = 0;
    uint32_t count2 = 0;
    BM_invoke_fn_template_impl(iters, [&] {
      // Do something slightly more complicated than just incrementing a
      // variable.  Otherwise gcc is smart enough to optimize the loop away.
      if (count1 & 0x1) {
        ++count2;
      }
      ++count1;
    });
  
    // Use the values we computed, so gcc won't optimize the loop away
    CHECK_EQ(iters, count1);
    CHECK_EQ(iters / 2, count2);
  }
  
  // Invoke a function pointer through the same wrapper used for lambdas
  BENCHMARK(fn_ptr_invoke_through_template, iters) {
    BM_invoke_fn_template_impl(iters, doNothing);
  }
  
  // Invoking a virtual method
  BENCHMARK(virtual_fn_invoke, iters) {
    VirtualClass vc;
    BM_virtual_fn_invoke_impl(iters, &vc);
  }
  
  // Creating a function pointer and invoking it
  BENCHMARK(fn_ptr_create_invoke, iters) {
    for (int n = 0; n < iters; ++n) {
      void (*fn)() = doNothing;
      fn();
    }
  }
  
  // Creating a std::function object from a function pointer, and invoking it
  BENCHMARK(std_function_create_invoke, iters) {
    for (int n = 0; n < iters; ++n) {
      std::function<void()> fn = doNothing;
      fn();
    }
  }
  
  // Creating a pointer-to-member and invoking it
  BENCHMARK(mem_fn_create_invoke, iters) {
    TestClass tc;
    for (int n = 0; n < iters; ++n) {
      void (TestClass::*memfn)() = &TestClass::doNothing;
      (tc.*memfn)();
    }
  }
  
  // Using std::bind to create a std::function from a member function,
  // and invoking it
  BENCHMARK(std_bind_create_invoke, iters) {
    TestClass tc;
    for (int n = 0; n < iters; ++n) {
      std::function<void()> fn = std::bind(&TestClass::doNothing, &tc);
      fn();
    }
  }
  
  // Using std::bind directly to invoke a member function
  BENCHMARK(std_bind_direct_invoke, iters) {
    TestClass tc;
    for (int n = 0; n < iters; ++n) {
      auto fn = std::bind(&TestClass::doNothing, &tc);
      fn();
    }
  }
  
  // Using ScopeGuard to invoke a std::function
  BENCHMARK(scope_guard_std_function, iters) {
    std::function<void()> fn(doNothing);
    for (int n = 0; n < iters; ++n) {
      ScopeGuard g = makeGuard(fn);
    }
  }
  
  // Using ScopeGuard to invoke a std::function,
  // but create the ScopeGuard with an rvalue to a std::function
  BENCHMARK(scope_guard_std_function_rvalue, iters) {
    for (int n = 0; n < iters; ++n) {
      ScopeGuard g = makeGuard(std::function<void()>(doNothing));
    }
  }
  
  // Using ScopeGuard to invoke a function pointer
  BENCHMARK(scope_guard_fn_ptr, iters) {
    for (int n = 0; n < iters; ++n) {
      ScopeGuard g = makeGuard(doNothing);
    }
  }
  
  // Using ScopeGuard to invoke a lambda that does nothing
  BENCHMARK(scope_guard_lambda_noop, iters) {
    for (int n = 0; n < iters; ++n) {
      ScopeGuard g = makeGuard([] {});
    }
  }
  
  // Using ScopeGuard to invoke a lambda that invokes a function
  BENCHMARK(scope_guard_lambda_function, iters) {
    for (int n = 0; n < iters; ++n) {
      ScopeGuard g = makeGuard([] { doNothing(); });
    }
  }
  
  // Using ScopeGuard to invoke a lambda that modifies a local variable
  BENCHMARK(scope_guard_lambda_local_var, iters) {
    uint32_t count = 0;
    for (int n = 0; n < iters; ++n) {
      ScopeGuard g = makeGuard([&] {
        // Increment count if n is odd.  Without this conditional check
        // (i.e., if we just increment count each time through the loop),
        // gcc is smart enough to optimize the entire loop away, and just set
        // count = iters.
        if (n & 0x1) {
          ++count;
        }
      });
    }
  
    // Check that the value of count is what we expect.
    // This check is necessary: if we don't use count, gcc detects that count is
    // unused and optimizes the entire loop away.
    CHECK_EQ(iters / 2, count);
  }
  
  BENCHMARK_DRAW_LINE()
  
  BENCHMARK(throw_exception, iters) {
    for (int n = 0; n < iters; ++n) {
      try {
        throwException();
      } catch (const std::exception& ex) {
      }
    }
  }
  
  BENCHMARK(catch_no_exception, iters) {
    for (int n = 0; n < iters; ++n) {
      try {
        doNothing();
      } catch (const std::exception& ex) {
      }
    }
  }
  
  BENCHMARK(return_exc_ptr, iters) {
    for (int n = 0; n < iters; ++n) {
      returnExceptionPtr();
    }
  }
  
  BENCHMARK(exc_ptr_param_return, iters) {
    for (int n = 0; n < iters; ++n) {
      std::exception_ptr ex;
      exceptionPtrReturnParam(&ex);
    }
  }
  
  BENCHMARK(exc_ptr_param_return_null, iters) {
    for (int n = 0; n < iters; ++n) {
      exceptionPtrReturnParam(nullptr);
    }
  }
  
  BENCHMARK(return_string, iters) {
    for (int n = 0; n < iters; ++n) {
      returnString();
    }
  }
  
  BENCHMARK(return_string_noexcept, iters) {
    for (int n = 0; n < iters; ++n) {
      returnStringNoExcept();
    }
  }
  
  BENCHMARK(return_code, iters) {
    for (int n = 0; n < iters; ++n) {
      returnCode(false);
    }
  }
  
  BENCHMARK(return_code_noexcept, iters) {
    for (int n = 0; n < iters; ++n) {
      returnCodeNoExcept(false);
    }
  }
  
  // main()
  
  int main(int argc, char** argv) {
    google::ParseCommandLineFlags(&argc, &argv, true);
    folly::runBenchmarks();
  }
  