]> https://gitweb.dealii.org/ - dealii.git/commitdiff
performance tests: add two valgrind instrumentation tests
authorMatthias Maier <tamiko@43-1.org>
Sun, 20 Feb 2022 20:15:34 +0000 (14:15 -0600)
committerMatthias Maier <tamiko@43-1.org>
Fri, 11 Mar 2022 21:46:34 +0000 (15:46 -0600)
tests/performance/CMakeLists.txt
tests/performance/instrumentation_step_22.cc [new file with mode: 0644]
tests/performance/instrumentation_step_22.with_valgrind=true.threads=1.release.run_only [new file with mode: 0644]
tests/performance/instrumentation_step_3.cc [new file with mode: 0644]
tests/performance/instrumentation_step_3.with_valgrind=true.threads=1.release.run_only [new file with mode: 0644]

index 2619be7aef876cba99aace5fcb1318dc094cf8fe..18479a025bb9a0715fff201e5b485a8d88ebde15 100644 (file)
@@ -21,9 +21,17 @@ IF( NOT VALGRIND_EXECUTABLE MATCHES "-NOTFOUND" AND
     NOT VALGRIND_INCLUDE_DIR MATCHES "-NOTFOUND" )
   MESSAGE(STATUS "Found valgrind at ${VALGRIND_EXECUTABLE}")
   SET(DEAL_II_WITH_VALGRIND TRUE)
+  SET(_command_line
+    "${VALGRIND_EXECUTABLE}"
+    --tool=callgrind -q --combine-dumps=yes --instr-atstart=no
+    --callgrind-out-file=callgrind.out
+    )
 ELSE()
-  MESSAGE(STATUS "COuld not find valgrind")
+  MESSAGE(STATUS "Could not find valgrind")
   SET(DEAL_II_WITH_VALGRIND FALSE)
 ENDIF()
 
+SET(performance_instrumentation_step_3_RUNARGS_PREFIX "${_command_line}")
+SET(performance_instrumentation_step_22_RUNARGS_PREFIX "${_command_line}")
+
 DEAL_II_PICKUP_TESTS()
diff --git a/tests/performance/instrumentation_step_22.cc b/tests/performance/instrumentation_step_22.cc
new file mode 100644 (file)
index 0000000..c177b7e
--- /dev/null
@@ -0,0 +1,677 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2022 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+//
+// Description:
+//
+// A performance benchmark based on step 22 that measures the number of
+// instruction cycles for system setup, assembly, solve and postprocessing
+// for a Stokes problem.
+//
+// Status: experimental
+//
+
+#include <deal.II/base/function.h>
+#include <deal.II/base/logstream.h>
+#include <deal.II/base/quadrature_lib.h>
+#include <deal.II/base/tensor_function.h>
+#include <deal.II/base/timer.h>
+#include <deal.II/base/utilities.h>
+
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/dofs/dof_renumbering.h>
+#include <deal.II/dofs/dof_tools.h>
+
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_system.h>
+#include <deal.II/fe/fe_values.h>
+
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/grid_refinement.h>
+#include <deal.II/grid/grid_tools.h>
+#include <deal.II/grid/tria.h>
+
+#include <deal.II/lac/affine_constraints.h>
+#include <deal.II/lac/block_sparse_matrix.h>
+#include <deal.II/lac/block_vector.h>
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/precondition.h>
+#include <deal.II/lac/solver_cg.h>
+#include <deal.II/lac/sparse_direct.h>
+#include <deal.II/lac/sparse_ilu.h>
+
+#include <deal.II/numerics/data_out.h>
+#include <deal.II/numerics/error_estimator.h>
+#include <deal.II/numerics/matrix_tools.h>
+#include <deal.II/numerics/vector_tools.h>
+
+#include <fstream>
+#include <iostream>
+#include <memory>
+
+#include "performance_test_driver.h"
+#include "valgrind_instrumentation.h"
+
+using namespace dealii;
+
+dealii::ConditionalOStream debug_output(std::cout, false);
+
+template <int dim>
+struct InnerPreconditioner;
+
+template <>
+struct InnerPreconditioner<2>
+{
+  using type = SparseDirectUMFPACK;
+};
+
+template <>
+struct InnerPreconditioner<3>
+{
+  using type = SparseILU<double>;
+};
+
+template <int dim>
+class StokesProblem
+{
+public:
+  StokesProblem(const unsigned int degree);
+  Measurement
+  run();
+
+private:
+  void
+  setup_dofs();
+  void
+  assemble_system();
+  void
+  solve();
+  void
+  output_results(const unsigned int refinement_cycle) const;
+  void
+  refine_mesh();
+
+  const unsigned int degree;
+
+  Triangulation<dim> triangulation;
+  FESystem<dim>      fe;
+  DoFHandler<dim>    dof_handler;
+
+  AffineConstraints<double> constraints;
+
+  BlockSparsityPattern      sparsity_pattern;
+  BlockSparseMatrix<double> system_matrix;
+
+  BlockSparsityPattern      preconditioner_sparsity_pattern;
+  BlockSparseMatrix<double> preconditioner_matrix;
+
+  BlockVector<double> solution;
+  BlockVector<double> system_rhs;
+
+  std::shared_ptr<typename InnerPreconditioner<dim>::type> A_preconditioner;
+};
+
+
+template <int dim>
+class BoundaryValues : public Function<dim>
+{
+public:
+  BoundaryValues()
+    : Function<dim>(dim + 1)
+  {}
+
+  virtual double
+  value(const Point<dim> &p, const unsigned int component = 0) const override;
+
+  virtual void
+  vector_value(const Point<dim> &p, Vector<double> &value) const override;
+};
+
+
+template <int dim>
+double
+BoundaryValues<dim>::value(const Point<dim> & p,
+                           const unsigned int component) const
+{
+  Assert(component < this->n_components,
+         ExcIndexRange(component, 0, this->n_components));
+
+  if (component == 0)
+    return (p[0] < 0 ? -1 : (p[0] > 0 ? 1 : 0));
+  return 0;
+}
+
+
+template <int dim>
+void
+BoundaryValues<dim>::vector_value(const Point<dim> &p,
+                                  Vector<double> &  values) const
+{
+  for (unsigned int c = 0; c < this->n_components; ++c)
+    values(c) = BoundaryValues<dim>::value(p, c);
+}
+
+
+template <int dim>
+class RightHandSide : public TensorFunction<1, dim>
+{
+public:
+  RightHandSide()
+    : TensorFunction<1, dim>()
+  {}
+
+  virtual Tensor<1, dim>
+  value(const Point<dim> &p) const override;
+
+  virtual void
+  value_list(const std::vector<Point<dim>> &p,
+             std::vector<Tensor<1, dim>> &  value) const override;
+};
+
+
+template <int dim>
+Tensor<1, dim>
+RightHandSide<dim>::value(const Point<dim> & /*p*/) const
+{
+  return Tensor<1, dim>();
+}
+
+
+template <int dim>
+void
+RightHandSide<dim>::value_list(const std::vector<Point<dim>> &vp,
+                               std::vector<Tensor<1, dim>> &  values) const
+{
+  for (unsigned int c = 0; c < vp.size(); ++c)
+    {
+      values[c] = RightHandSide<dim>::value(vp[c]);
+    }
+}
+
+
+template <class MatrixType, class PreconditionerType>
+class InverseMatrix : public Subscriptor
+{
+public:
+  InverseMatrix(const MatrixType &m, const PreconditionerType &preconditioner);
+
+  void
+  vmult(Vector<double> &dst, const Vector<double> &src) const;
+
+private:
+  const SmartPointer<const MatrixType>         matrix;
+  const SmartPointer<const PreconditionerType> preconditioner;
+};
+
+
+template <class MatrixType, class PreconditionerType>
+InverseMatrix<MatrixType, PreconditionerType>::InverseMatrix(
+  const MatrixType &        m,
+  const PreconditionerType &preconditioner)
+  : matrix(&m)
+  , preconditioner(&preconditioner)
+{}
+
+
+template <class MatrixType, class PreconditionerType>
+void
+InverseMatrix<MatrixType, PreconditionerType>::vmult(
+  Vector<double> &      dst,
+  const Vector<double> &src) const
+{
+  SolverControl            solver_control(src.size(), 1e-6 * src.l2_norm());
+  SolverCG<Vector<double>> cg(solver_control);
+
+  dst = 0;
+
+  cg.solve(*matrix, dst, src, *preconditioner);
+}
+
+
+template <class PreconditionerType>
+class SchurComplement : public Subscriptor
+{
+public:
+  SchurComplement(
+    const BlockSparseMatrix<double> &system_matrix,
+    const InverseMatrix<SparseMatrix<double>, PreconditionerType> &A_inverse);
+
+  void
+  vmult(Vector<double> &dst, const Vector<double> &src) const;
+
+private:
+  const SmartPointer<const BlockSparseMatrix<double>> system_matrix;
+  const SmartPointer<
+    const InverseMatrix<SparseMatrix<double>, PreconditionerType>>
+    A_inverse;
+
+  mutable Vector<double> tmp1, tmp2;
+};
+
+
+template <class PreconditionerType>
+SchurComplement<PreconditionerType>::SchurComplement(
+  const BlockSparseMatrix<double> &                              system_matrix,
+  const InverseMatrix<SparseMatrix<double>, PreconditionerType> &A_inverse)
+  : system_matrix(&system_matrix)
+  , A_inverse(&A_inverse)
+  , tmp1(system_matrix.block(0, 0).m())
+  , tmp2(system_matrix.block(0, 0).m())
+{}
+
+
+template <class PreconditionerType>
+void
+SchurComplement<PreconditionerType>::vmult(Vector<double> &      dst,
+                                           const Vector<double> &src) const
+{
+  system_matrix->block(0, 1).vmult(tmp1, src);
+  A_inverse->vmult(tmp2, tmp1);
+  system_matrix->block(1, 0).vmult(dst, tmp2);
+}
+
+
+template <int dim>
+StokesProblem<dim>::StokesProblem(const unsigned int degree)
+  : degree(degree)
+  , triangulation(Triangulation<dim>::maximum_smoothing)
+  , fe(FE_Q<dim>(degree + 1), dim, FE_Q<dim>(degree), 1)
+  , dof_handler(triangulation)
+{}
+
+
+template <int dim>
+void
+StokesProblem<dim>::setup_dofs()
+{
+  A_preconditioner.reset();
+  system_matrix.clear();
+  preconditioner_matrix.clear();
+
+  dof_handler.distribute_dofs(fe);
+  DoFRenumbering::Cuthill_McKee(dof_handler);
+
+  std::vector<unsigned int> block_component(dim + 1, 0);
+  block_component[dim] = 1;
+  DoFRenumbering::component_wise(dof_handler, block_component);
+
+  {
+    constraints.clear();
+
+    const FEValuesExtractors::Vector velocities(0);
+    DoFTools::make_hanging_node_constraints(dof_handler, constraints);
+    VectorTools::interpolate_boundary_values(dof_handler,
+                                             1,
+                                             BoundaryValues<dim>(),
+                                             constraints,
+                                             fe.component_mask(velocities));
+  }
+
+  constraints.close();
+
+  const std::vector<types::global_dof_index> dofs_per_block =
+    DoFTools::count_dofs_per_fe_block(dof_handler, block_component);
+  const types::global_dof_index n_u = dofs_per_block[0];
+  const types::global_dof_index n_p = dofs_per_block[1];
+
+  debug_output << "   Number of active cells: "
+               << triangulation.n_active_cells() << std::endl
+               << "   Number of degrees of freedom: " << dof_handler.n_dofs()
+               << " (" << n_u << '+' << n_p << ')' << std::endl;
+
+  {
+    BlockDynamicSparsityPattern dsp(dofs_per_block, dofs_per_block);
+
+    Table<2, DoFTools::Coupling> coupling(dim + 1, dim + 1);
+    for (unsigned int c = 0; c < dim + 1; ++c)
+      for (unsigned int d = 0; d < dim + 1; ++d)
+        if (!((c == dim) && (d == dim)))
+          coupling[c][d] = DoFTools::always;
+        else
+          coupling[c][d] = DoFTools::none;
+
+    DoFTools::make_sparsity_pattern(
+      dof_handler, coupling, dsp, constraints, false);
+
+    sparsity_pattern.copy_from(dsp);
+  }
+
+  {
+    BlockDynamicSparsityPattern preconditioner_dsp(dofs_per_block,
+                                                   dofs_per_block);
+
+    Table<2, DoFTools::Coupling> preconditioner_coupling(dim + 1, dim + 1);
+    for (unsigned int c = 0; c < dim + 1; ++c)
+      for (unsigned int d = 0; d < dim + 1; ++d)
+        if (((c == dim) && (d == dim)))
+          preconditioner_coupling[c][d] = DoFTools::always;
+        else
+          preconditioner_coupling[c][d] = DoFTools::none;
+
+    DoFTools::make_sparsity_pattern(dof_handler,
+                                    preconditioner_coupling,
+                                    preconditioner_dsp,
+                                    constraints,
+                                    false);
+
+    preconditioner_sparsity_pattern.copy_from(preconditioner_dsp);
+  }
+
+  system_matrix.reinit(sparsity_pattern);
+  preconditioner_matrix.reinit(preconditioner_sparsity_pattern);
+
+  solution.reinit(dofs_per_block);
+  system_rhs.reinit(dofs_per_block);
+}
+
+
+template <int dim>
+void
+StokesProblem<dim>::assemble_system()
+{
+  system_matrix         = 0;
+  system_rhs            = 0;
+  preconditioner_matrix = 0;
+
+  QGauss<dim> quadrature_formula(degree + 2);
+
+  FEValues<dim> fe_values(fe,
+                          quadrature_formula,
+                          update_values | update_quadrature_points |
+                            update_JxW_values | update_gradients);
+
+  const unsigned int dofs_per_cell = fe.n_dofs_per_cell();
+
+  const unsigned int n_q_points = quadrature_formula.size();
+
+  FullMatrix<double> local_matrix(dofs_per_cell, dofs_per_cell);
+  FullMatrix<double> local_preconditioner_matrix(dofs_per_cell, dofs_per_cell);
+  Vector<double>     local_rhs(dofs_per_cell);
+
+  std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
+
+  const RightHandSide<dim>    right_hand_side;
+  std::vector<Tensor<1, dim>> rhs_values(n_q_points, Tensor<1, dim>());
+
+  const FEValuesExtractors::Vector velocities(0);
+  const FEValuesExtractors::Scalar pressure(dim);
+
+  std::vector<SymmetricTensor<2, dim>> symgrad_phi_u(dofs_per_cell);
+  std::vector<double>                  div_phi_u(dofs_per_cell);
+  std::vector<Tensor<1, dim>>          phi_u(dofs_per_cell);
+  std::vector<double>                  phi_p(dofs_per_cell);
+
+  for (const auto &cell : dof_handler.active_cell_iterators())
+    {
+      fe_values.reinit(cell);
+      local_matrix                = 0;
+      local_preconditioner_matrix = 0;
+      local_rhs                   = 0;
+
+      right_hand_side.value_list(fe_values.get_quadrature_points(), rhs_values);
+
+      for (unsigned int q = 0; q < n_q_points; ++q)
+        {
+          for (unsigned int k = 0; k < dofs_per_cell; ++k)
+            {
+              symgrad_phi_u[k] = fe_values[velocities].symmetric_gradient(k, q);
+              div_phi_u[k]     = fe_values[velocities].divergence(k, q);
+              phi_u[k]         = fe_values[velocities].value(k, q);
+              phi_p[k]         = fe_values[pressure].value(k, q);
+            }
+
+          for (unsigned int i = 0; i < dofs_per_cell; ++i)
+            {
+              for (unsigned int j = 0; j <= i; ++j)
+                {
+                  local_matrix(i, j) +=
+                    (2 * (symgrad_phi_u[i] * symgrad_phi_u[j]) // (1)
+                     - div_phi_u[i] * phi_p[j]                 // (2)
+                     - phi_p[i] * div_phi_u[j])                // (3)
+                    * fe_values.JxW(q);                        // * dx
+
+                  local_preconditioner_matrix(i, j) +=
+                    (phi_p[i] * phi_p[j]) // (4)
+                    * fe_values.JxW(q);   // * dx
+                }
+              local_rhs(i) += phi_u[i]            // phi_u_i(x_q)
+                              * rhs_values[q]     // * f(x_q)
+                              * fe_values.JxW(q); // * dx
+            }
+        }
+
+      for (unsigned int i = 0; i < dofs_per_cell; ++i)
+        for (unsigned int j = i + 1; j < dofs_per_cell; ++j)
+          {
+            local_matrix(i, j) = local_matrix(j, i);
+            local_preconditioner_matrix(i, j) =
+              local_preconditioner_matrix(j, i);
+          }
+
+      cell->get_dof_indices(local_dof_indices);
+      constraints.distribute_local_to_global(
+        local_matrix, local_rhs, local_dof_indices, system_matrix, system_rhs);
+      constraints.distribute_local_to_global(local_preconditioner_matrix,
+                                             local_dof_indices,
+                                             preconditioner_matrix);
+    }
+
+  debug_output << "   Computing preconditioner..." << std::endl << std::flush;
+
+  A_preconditioner =
+    std::make_shared<typename InnerPreconditioner<dim>::type>();
+  A_preconditioner->initialize(
+    system_matrix.block(0, 0),
+    typename InnerPreconditioner<dim>::type::AdditionalData());
+}
+
+
+template <int dim>
+void
+StokesProblem<dim>::solve()
+{
+  const InverseMatrix<SparseMatrix<double>,
+                      typename InnerPreconditioner<dim>::type>
+                 A_inverse(system_matrix.block(0, 0), *A_preconditioner);
+  Vector<double> tmp(solution.block(0).size());
+
+  {
+    Vector<double> schur_rhs(solution.block(1).size());
+    A_inverse.vmult(tmp, system_rhs.block(0));
+    system_matrix.block(1, 0).vmult(schur_rhs, tmp);
+    schur_rhs -= system_rhs.block(1);
+
+    SchurComplement<typename InnerPreconditioner<dim>::type> schur_complement(
+      system_matrix, A_inverse);
+
+    SolverControl            solver_control(solution.block(1).size(),
+                                 1e-6 * schur_rhs.l2_norm());
+    SolverCG<Vector<double>> cg(solver_control);
+
+    SparseILU<double> preconditioner;
+    preconditioner.initialize(preconditioner_matrix.block(1, 1),
+                              SparseILU<double>::AdditionalData());
+
+    InverseMatrix<SparseMatrix<double>, SparseILU<double>> m_inverse(
+      preconditioner_matrix.block(1, 1), preconditioner);
+
+    cg.solve(schur_complement, solution.block(1), schur_rhs, m_inverse);
+
+    constraints.distribute(solution);
+
+    debug_output << "  " << solver_control.last_step()
+                 << " outer CG Schur complement iterations for pressure"
+                 << std::endl;
+  }
+
+  {
+    system_matrix.block(0, 1).vmult(tmp, solution.block(1));
+    tmp *= -1;
+    tmp += system_rhs.block(0);
+
+    A_inverse.vmult(solution.block(0), tmp);
+
+    constraints.distribute(solution);
+  }
+}
+
+
+template <int dim>
+void
+StokesProblem<dim>::output_results(
+  const unsigned int /*refinement_cycle*/) const
+{
+  std::vector<std::string> solution_names(dim, "velocity");
+  solution_names.emplace_back("pressure");
+
+  std::vector<DataComponentInterpretation::DataComponentInterpretation>
+    data_component_interpretation(
+      dim, DataComponentInterpretation::component_is_part_of_vector);
+  data_component_interpretation.push_back(
+    DataComponentInterpretation::component_is_scalar);
+
+  DataOut<dim> data_out;
+  data_out.attach_dof_handler(dof_handler);
+  data_out.add_data_vector(solution,
+                           solution_names,
+                           DataOut<dim>::type_dof_data,
+                           data_component_interpretation);
+  data_out.build_patches();
+}
+
+
+template <int dim>
+void
+StokesProblem<dim>::refine_mesh()
+{
+  Vector<float> estimated_error_per_cell(triangulation.n_active_cells());
+
+  const FEValuesExtractors::Scalar pressure(dim);
+  KellyErrorEstimator<dim>::estimate(
+    dof_handler,
+    QGauss<dim - 1>(degree + 1),
+    std::map<types::boundary_id, const Function<dim> *>(),
+    solution,
+    estimated_error_per_cell,
+    fe.component_mask(pressure));
+
+  GridRefinement::refine_and_coarsen_fixed_number(triangulation,
+                                                  estimated_error_per_cell,
+                                                  0.3,
+                                                  0.0);
+  triangulation.execute_coarsening_and_refinement();
+}
+
+
+template <int dim>
+Measurement
+StokesProblem<dim>::run()
+{
+  std::map<std::string, unsigned long long> cycle_count;
+
+  {
+    std::vector<unsigned int> subdivisions(dim, 1);
+    subdivisions[0] = 4;
+
+    const Point<dim> bottom_left = (dim == 2 ?                //
+                                      Point<dim>(-2, -1) :    // 2d case
+                                      Point<dim>(-2, 0, -1)); // 3d case
+
+    const Point<dim> top_right = (dim == 2 ?              //
+                                    Point<dim>(2, 0) :    // 2d case
+                                    Point<dim>(2, 1, 0)); // 3d case
+
+    GridGenerator::subdivided_hyper_rectangle(triangulation,
+                                              subdivisions,
+                                              bottom_left,
+                                              top_right);
+  }
+
+  for (const auto &cell : triangulation.active_cell_iterators())
+    for (const auto &face : cell->face_iterators())
+      if (face->center()[dim - 1] == 0)
+        face->set_all_boundary_ids(1);
+
+
+  switch (get_testing_environment())
+    {
+      case TestingEnvironment::light:
+        triangulation.refine_global(4 - dim);
+        break;
+      case TestingEnvironment::medium:
+        /* fallthrough */
+      case TestingEnvironment::heavy:
+        triangulation.refine_global(5 - dim);
+        break;
+    }
+
+  for (unsigned int refinement_cycle = 0; refinement_cycle < 6;
+       ++refinement_cycle)
+    {
+      debug_output << "Refinement cycle " << refinement_cycle << std::endl;
+
+      if (refinement_cycle > 0)
+        {
+          callgrind_wrapper::start_instrumentation();
+          refine_mesh();
+          cycle_count["refinement"] = callgrind_wrapper::stop_instrumentation();
+        }
+
+      callgrind_wrapper::start_instrumentation();
+      setup_dofs();
+      cycle_count["setup_system"] = callgrind_wrapper::stop_instrumentation();
+
+      debug_output << "   Assembling..." << std::endl << std::flush;
+      callgrind_wrapper::start_instrumentation();
+      assemble_system();
+      cycle_count["assemble_system"] =
+        callgrind_wrapper::stop_instrumentation();
+
+      callgrind_wrapper::start_instrumentation();
+      solve();
+      cycle_count["solve"] = callgrind_wrapper::stop_instrumentation();
+
+      callgrind_wrapper::start_instrumentation();
+      output_results(refinement_cycle);
+      cycle_count["output_results"] = callgrind_wrapper::stop_instrumentation();
+
+      debug_output << std::endl;
+    }
+  return {cycle_count["refinement"],
+          cycle_count["setup_system"],
+          cycle_count["assemble_system"],
+          cycle_count["solve"],
+          cycle_count["output_results"]};
+}
+
+
+std::tuple<Metric, unsigned int, std::vector<std::string>>
+describe_measurements()
+{
+  return {Metric::instruction_count,
+          1,
+          {"refinement",
+           "setup_system",
+           "assemble_system",
+           "solve",
+           "output_results"}};
+}
+
+
+Measurement
+perform_single_measurement()
+{
+  return StokesProblem<2>(1).run();
+}
diff --git a/tests/performance/instrumentation_step_22.with_valgrind=true.threads=1.release.run_only b/tests/performance/instrumentation_step_22.with_valgrind=true.threads=1.release.run_only
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/tests/performance/instrumentation_step_3.cc b/tests/performance/instrumentation_step_3.cc
new file mode 100644 (file)
index 0000000..5857b20
--- /dev/null
@@ -0,0 +1,267 @@
+// ---------------------------------------------------------------------
+//
+// Copyright (C) 2022 by the deal.II authors
+//
+// This file is part of the deal.II library.
+//
+// The deal.II library is free software; you can use it, redistribute
+// it, and/or modify it under the terms of the GNU Lesser General
+// Public License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
+// The full text of the license can be found in the file LICENSE.md at
+// the top level directory of deal.II.
+//
+// ---------------------------------------------------------------------
+
+//
+// Description:
+//
+// A performance benchmark based on step 3 that measures the number of
+// instruction cycles for system setup, assembly, solve and postprocessing
+// for a Stokes problem.
+//
+// Status: experimental
+//
+
+#include <deal.II/base/function.h>
+#include <deal.II/base/quadrature_lib.h>
+#include <deal.II/base/timer.h>
+
+#include <deal.II/dofs/dof_handler.h>
+#include <deal.II/dofs/dof_tools.h>
+
+#include <deal.II/fe/fe_q.h>
+#include <deal.II/fe/fe_values.h>
+
+#include <deal.II/grid/grid_generator.h>
+#include <deal.II/grid/tria.h>
+
+#include <deal.II/lac/dynamic_sparsity_pattern.h>
+#include <deal.II/lac/full_matrix.h>
+#include <deal.II/lac/precondition.h>
+#include <deal.II/lac/solver_cg.h>
+#include <deal.II/lac/sparse_matrix.h>
+#include <deal.II/lac/vector.h>
+
+#include <deal.II/numerics/data_out.h>
+#include <deal.II/numerics/matrix_tools.h>
+#include <deal.II/numerics/vector_tools.h>
+
+#include "performance_test_driver.h"
+#include "valgrind_instrumentation.h"
+
+using namespace dealii;
+
+dealii::ConditionalOStream debug_output(std::cout, false);
+
+class Step3
+{
+public:
+  Step3();
+
+  Measurement
+  run();
+
+private:
+  void
+  make_grid();
+  void
+  setup_system();
+  void
+  assemble_system();
+  void
+  solve();
+  void
+  output_results() const;
+
+  Triangulation<2> triangulation;
+  FE_Q<2>          fe;
+  DoFHandler<2>    dof_handler;
+
+  SparsityPattern      sparsity_pattern;
+  SparseMatrix<double> system_matrix;
+
+  Vector<double> solution;
+  Vector<double> system_rhs;
+};
+
+
+Step3::Step3()
+  : fe(1)
+  , dof_handler(triangulation)
+{}
+
+
+void
+Step3::make_grid()
+{
+  GridGenerator::hyper_cube(triangulation, -1, 1);
+
+  switch (get_testing_environment())
+    {
+      case TestingEnvironment::light:
+        triangulation.refine_global(6);
+        break;
+      case TestingEnvironment::medium:
+        /* fallthrough */
+      case TestingEnvironment::heavy:
+        triangulation.refine_global(7);
+        break;
+    }
+
+  debug_output << "Number of active cells: " << triangulation.n_active_cells()
+               << std::endl;
+}
+
+
+void
+Step3::setup_system()
+{
+  dof_handler.distribute_dofs(fe);
+  debug_output << "Number of degrees of freedom: " << dof_handler.n_dofs()
+               << std::endl;
+
+  DynamicSparsityPattern dsp(dof_handler.n_dofs());
+  DoFTools::make_sparsity_pattern(dof_handler, dsp);
+  sparsity_pattern.copy_from(dsp);
+
+  system_matrix.reinit(sparsity_pattern);
+
+  solution.reinit(dof_handler.n_dofs());
+  system_rhs.reinit(dof_handler.n_dofs());
+}
+
+
+void
+Step3::assemble_system()
+{
+  QGauss<2>   quadrature_formula(fe.degree + 1);
+  FEValues<2> fe_values(fe,
+                        quadrature_formula,
+                        update_values | update_gradients | update_JxW_values);
+
+  const unsigned int dofs_per_cell = fe.n_dofs_per_cell();
+
+  FullMatrix<double> cell_matrix(dofs_per_cell, dofs_per_cell);
+  Vector<double>     cell_rhs(dofs_per_cell);
+
+  std::vector<types::global_dof_index> local_dof_indices(dofs_per_cell);
+
+  for (const auto &cell : dof_handler.active_cell_iterators())
+    {
+      fe_values.reinit(cell);
+
+      cell_matrix = 0;
+      cell_rhs    = 0;
+
+      for (const unsigned int q_index : fe_values.quadrature_point_indices())
+        {
+          for (const unsigned int i : fe_values.dof_indices())
+            for (const unsigned int j : fe_values.dof_indices())
+              cell_matrix(i, j) +=
+                (fe_values.shape_grad(i, q_index) * // grad phi_i(x_q)
+                 fe_values.shape_grad(j, q_index) * // grad phi_j(x_q)
+                 fe_values.JxW(q_index));           // dx
+
+          for (const unsigned int i : fe_values.dof_indices())
+            cell_rhs(i) += (fe_values.shape_value(i, q_index) * // phi_i(x_q)
+                            1. *                                // f(x_q)
+                            fe_values.JxW(q_index));            // dx
+        }
+      cell->get_dof_indices(local_dof_indices);
+
+      for (const unsigned int i : fe_values.dof_indices())
+        for (const unsigned int j : fe_values.dof_indices())
+          system_matrix.add(local_dof_indices[i],
+                            local_dof_indices[j],
+                            cell_matrix(i, j));
+
+      for (const unsigned int i : fe_values.dof_indices())
+        system_rhs(local_dof_indices[i]) += cell_rhs(i);
+    }
+
+
+  std::map<types::global_dof_index, double> boundary_values;
+  VectorTools::interpolate_boundary_values(dof_handler,
+                                           0,
+                                           Functions::ZeroFunction<2>(),
+                                           boundary_values);
+  MatrixTools::apply_boundary_values(boundary_values,
+                                     system_matrix,
+                                     solution,
+                                     system_rhs);
+}
+
+
+void
+Step3::solve()
+{
+  SolverControl            solver_control(1000, 1e-6 * system_rhs.l2_norm());
+  SolverCG<Vector<double>> solver(solver_control);
+  PreconditionSSOR<SparseMatrix<double>> preconditioner;
+  preconditioner.initialize(system_matrix, 1.2);
+  solver.solve(system_matrix, solution, system_rhs, preconditioner);
+}
+
+
+void
+Step3::output_results() const
+{
+  DataOut<2> data_out;
+  data_out.attach_dof_handler(dof_handler);
+  data_out.add_data_vector(solution, "solution");
+  data_out.build_patches();
+}
+
+
+Measurement
+Step3::run()
+{
+  std::map<std::string, unsigned long long> cycle_count;
+
+  callgrind_wrapper::start_instrumentation();
+  make_grid();
+  cycle_count["make_grid"] = callgrind_wrapper::stop_instrumentation();
+
+  callgrind_wrapper::start_instrumentation();
+  setup_system();
+  cycle_count["setup_system"] = callgrind_wrapper::stop_instrumentation();
+
+  callgrind_wrapper::start_instrumentation();
+  assemble_system();
+  cycle_count["assemble_system"] = callgrind_wrapper::stop_instrumentation();
+
+  callgrind_wrapper::start_instrumentation();
+  solve();
+  cycle_count["solve"] = callgrind_wrapper::stop_instrumentation();
+
+  callgrind_wrapper::start_instrumentation();
+  output_results();
+  cycle_count["output_results"] = callgrind_wrapper::stop_instrumentation();
+
+  return {cycle_count["make_grid"],
+          cycle_count["setup_system"],
+          cycle_count["assemble_system"],
+          cycle_count["solve"],
+          cycle_count["output_results"]};
+}
+
+
+std::tuple<Metric, unsigned int, std::vector<std::string>>
+describe_measurements()
+{
+  return {Metric::instruction_count,
+          1,
+          {"make_grid",
+           "setup_system",
+           "assemble_system",
+           "solve",
+           "output_results"}};
+}
+
+
+Measurement
+perform_single_measurement()
+{
+  return Step3().run();
+}
diff --git a/tests/performance/instrumentation_step_3.with_valgrind=true.threads=1.release.run_only b/tests/performance/instrumentation_step_3.with_valgrind=true.threads=1.release.run_only
new file mode 100644 (file)
index 0000000..e69de29

In the beginning the Universe was created. This has made a lot of people very angry and has been widely regarded as a bad move.

Douglas Adams


Typeset in Trocchi and Trocchi Bold Sans Serif.