Commit d5450f60 authored by Jean-Noël Grad's avatar Jean-Noël Grad

Create benchmarks

Benchmarking suite to measure the integrator execution time under
various conditions: build features, struct Particle implementation,
number of cores, number of particles, interaction types.
parent c1d850ef
......@@ -72,6 +72,7 @@ option(WITH_GSL "Build with GSL support" ON)
option(WITH_CUDA "Build with GPU support" ON)
option(WITH_HDF5 "Build with HDF5 support" ON)
option(WITH_TESTS "Enable tests" ON)
option(WITH_BENCHMARKS "Enable benchmarks" OFF)
option(WITH_SCAFACOS "Build with Scafacos support" ON)
option(WITH_VALGRIND_INSTRUMENTATION "Build with valgrind instrumentation markers" OFF)
if( CMAKE_VERSION VERSION_GREATER 3.5.2 AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang" )
......@@ -453,6 +454,11 @@ if(WITH_TESTS)
add_subdirectory(testsuite)
endif(WITH_TESTS)
if(WITH_BENCHMARKS)
add_custom_target(benchmark)
add_subdirectory(maintainer/benchmarks)
endif(WITH_BENCHMARKS)
#######################################################################
# Subdirectories
#######################################################################
......
ProcessorCount(NP)
if(EXISTS ${MPIEXEC})
# OpenMPI 3.0 and higher checks the number of processes against the number of CPUs
execute_process(COMMAND ${MPIEXEC} --version RESULT_VARIABLE mpi_version_result OUTPUT_VARIABLE mpi_version_output ERROR_VARIABLE mpi_version_output)
if (mpi_version_result EQUAL 0 AND mpi_version_output MATCHES "\\(Open(RTE| MPI)\\) ([3-9]\\.|1[0-9])")
set(MPIEXEC_OVERSUBSCRIBE "-oversubscribe")
else()
set(MPIEXEC_OVERSUBSCRIBE "")
endif()
endif()
add_custom_target(benchmark_python)
function(PYTHON_BENCHMARK)
cmake_parse_arguments(BENCHMARK "" "FILE;RUN_WITH_MPI;MIN_NUM_PROC;MAX_NUM_PROC" "ARGUMENTS;DEPENDENCIES" ${ARGN})
get_filename_component(BENCHMARK_NAME ${BENCHMARK_FILE} NAME_WE)
foreach(argument IN LISTS BENCHMARK_ARGUMENTS)
set(BENCHMARK_NAME "${BENCHMARK_NAME}_${argument}")
endforeach(argument)
configure_file(${BENCHMARK_FILE} ${CMAKE_CURRENT_BINARY_DIR}/${BENCHMARK_FILE})
foreach(dependency IN LISTS BENCHMARK_DEPENDENCIES)
configure_file(${dependency} ${CMAKE_CURRENT_BINARY_DIR}/${dependency})
endforeach(dependency)
set(BENCHMARK_FILE "${CMAKE_CURRENT_BINARY_DIR}/${BENCHMARK_FILE}")
# default values
if (NOT DEFINED BENCHMARK_RUN_WITH_MPI)
set(BENCHMARK_RUN_WITH_MPI TRUE)
endif()
if (NOT DEFINED BENCHMARK_MIN_NUM_PROC)
set(BENCHMARK_MIN_NUM_PROC 1)
endif()
if (NOT DEFINED BENCHMARK_MAX_NUM_PROC)
set(BENCHMARK_MAX_NUM_PROC ${NP})
endif()
# parallel schemes
if(EXISTS ${MPIEXEC} AND ${BENCHMARK_RUN_WITH_MPI})
set(BENCHMARK_CONFIGURATIONS "0")
if(${NP} GREATER 0 AND ${BENCHMARK_MAX_NUM_PROC} GREATER 0 AND ${BENCHMARK_MIN_NUM_PROC} LESS 2)
list(APPEND BENCHMARK_CONFIGURATIONS 1)
endif()
if(${NP} GREATER 1 AND ${BENCHMARK_MAX_NUM_PROC} GREATER 1 AND ${BENCHMARK_MIN_NUM_PROC} LESS 3)
list(APPEND BENCHMARK_CONFIGURATIONS 2)
endif()
if(${NP} GREATER 3 AND ${BENCHMARK_MAX_NUM_PROC} GREATER 3 AND ${BENCHMARK_MIN_NUM_PROC} LESS 5)
list(APPEND BENCHMARK_CONFIGURATIONS 4)
endif()
if(${NP} GREATER 7 AND ${BENCHMARK_MAX_NUM_PROC} GREATER 7 AND ${BENCHMARK_MIN_NUM_PROC} LESS 9)
list(APPEND BENCHMARK_CONFIGURATIONS 8)
endif()
if(${NP} GREATER 15 AND ${BENCHMARK_MAX_NUM_PROC} GREATER 15 AND ${BENCHMARK_MIN_NUM_PROC} LESS 17)
list(APPEND BENCHMARK_CONFIGURATIONS 16)
endif()
list(REMOVE_AT BENCHMARK_CONFIGURATIONS 0)
foreach(nproc IN LISTS BENCHMARK_CONFIGURATIONS)
add_custom_target(${BENCHMARK_NAME}_parallel_${nproc}
${MPIEXEC} ${MPIEXEC_OVERSUBSCRIBE} ${MPIEXEC_NUMPROC_FLAG} ${nproc}
${CMAKE_BINARY_DIR}/pypresso ${BENCHMARK_FILE} ${BENCHMARK_ARGUMENTS})
add_dependencies(benchmark_python ${BENCHMARK_NAME}_parallel_${nproc})
endforeach(nproc)
else()
add_custom_target(${BENCHMARK_NAME}_serial
${CMAKE_BINARY_DIR}/pypresso ${BENCHMARK_FILE} ${BENCHMARK_ARGUMENTS})
add_dependencies(benchmark_python ${BENCHMARK_NAME}_serial)
endif()
endfunction(PYTHON_BENCHMARK)
python_benchmark(FILE lj.py ARGUMENTS 1000 liquid)
python_benchmark(FILE lj.py ARGUMENTS 1000 gas)
python_benchmark(FILE lj.py ARGUMENTS 10000 liquid)
python_benchmark(FILE lj.py ARGUMENTS 10000 gas)
python_benchmark(FILE p3m.py ARGUMENTS 1000 solution)
python_benchmark(FILE p3m.py ARGUMENTS 1000 gas)
python_benchmark(FILE p3m.py ARGUMENTS 10000 solution)
python_benchmark(FILE p3m.py ARGUMENTS 10000 gas)
add_dependencies(benchmark benchmark_python)
#
# Copyright (C) 2013-2018 The ESPResSo project
#
# This file is part of ESPResSo.
#
# ESPResSo is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ESPResSo is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import print_function
import os
import sys
import numpy as np
import espressomd
from time import time, sleep
from espressomd import thermostat
required_features = ["LENNARD_JONES"]
espressomd.assert_features(required_features)
print(espressomd.features())
# Interaction parameters (repulsive Lennard-Jones)
#############################################################
lj_eps = 1.0 # LJ epsilon
lj_sig = 1.0 # particle diameter
lj_cap = 20. # force cap
lj_cut = lj_sig * 2**(1. / 6.) # cutoff distance
# System parameters
#############################################################
try:
nproc = int(os.environ.get("OMPI_COMM_WORLD_SIZE", 1))
n_part_per_core = int(sys.argv[1])
n_part = nproc * n_part_per_core
if sys.argv[2] == 'gas':
density = 0.02
elif sys.argv[2] == 'liquid':
density = 0.30
else:
density = float(sys.argv[2])
assert density > 0, "density must be a positive number"
assert density < np.pi / (3 * np.sqrt(2)), \
"density exceeds the physical limit of sphere packing (~0.74)"
mode = "benchmark"
if len(sys.argv) == 4:
assert sys.argv[3] == "--visualize"
mode = "visualization"
from espressomd import visualization
from threading import Thread
except (ValueError, IndexError) as err:
print(err.message)
print("\nUsage: [mpiexec -np <N cores>] pypresso lj.py "
"<N particles per core> <density> [--visualize]\n")
exit(1)
measurement_steps = int(np.round(5e6 / n_part_per_core, -2))
assert(measurement_steps >= 100), \
"{} steps per tick are too short".format(measurement_steps)
# volume of N spheres with radius r: N * (4/3*pi*r^3)
box_l = (n_part * 4. / 3. * np.pi * (lj_sig / 2.)**3 / density)**(1./3.)
# System
#############################################################
system = espressomd.System(box_l=3*(box_l,))
# PRNG seeds
#############################################################
system.random_number_generator_state = list(range(
nproc * (system._get_PRNG_state_size() + 1)))
#system.random_number_generator_state = list(range(len(system.random_number_generator_state)))
np.random.seed(1)
# Integration parameters
#############################################################
system.time_step = 0.01
system.cell_system.skin = 0.5
system.thermostat.turn_off()
#############################################################
# Setup System #
#############################################################
# Interaction setup
#############################################################
system.non_bonded_inter[0, 0].lennard_jones.set_params(
epsilon=lj_eps, sigma=lj_sig, cutoff=lj_cut, shift="auto")
system.force_cap = lj_cap
print("LJ-parameters:")
print(system.non_bonded_inter[0, 0].lennard_jones.get_params())
# Particle setup
#############################################################
for i in range(n_part):
system.part.add(id=i, pos=np.random.random(3) * system.box_l)
#############################################################
# Warmup Integration #
#############################################################
system.integrator.set_steepest_descent(f_max=0, gamma=0.001, max_displacement=0.01)
# warmup
while system.analysis.energy()["total"] > 10 * n_part:
print('minimization: {:.1f}'.format(system.analysis.energy()["total"]))
system.integrator.run(10)
print()
system.integrator.set_vv()
system.thermostat.set_langevin(kT=1.0, gamma=1.0)
# tune skin
print('tune: {}'.format(system.cell_system.tune_skin(min_skin=0.2, max_skin=1, tol=0.05, int_steps=100)))
system.integrator.run(min(30 * measurement_steps, 60000))
print('tune: {}'.format(system.cell_system.tune_skin(min_skin=0.2, max_skin=1, tol=0.05, int_steps=100)))
print(system.non_bonded_inter[0, 0].lennard_jones)
if mode == 'benchmark':
report_path = 'benchmarks.csv'
if '${CMAKE_BINARY_DIR}'[0] != '$': # CMake variable
report_path = '${CMAKE_BINARY_DIR}/benchmarks.csv.part'
# print initial energies
energies = system.analysis.energy()
print(energies)
# time integration loop
print("Timing every {} steps".format(measurement_steps))
main_tick = time()
all_t = []
for i in range(30):
tick = time()
system.integrator.run(measurement_steps)
tock = time()
t = (tock-tick) / measurement_steps
print('step {}, time = {:.2e}, verlet: {:.2f}'.format(i, t,
system.cell_system.get_state()["verlet_reuse"]))
all_t.append(t)
main_tock = time()
# average time
all_t = np.array(all_t)
avg = np.average(all_t)
ci = 1.96 * np.std(all_t) / np.sqrt(len(all_t) - 1)
print("average: {:.3e} +/- {:.3e} (95% C.I.)".format(avg, ci))
# print final energies
energies = system.analysis.energy()
print(energies)
# write report
report = ('"{script}","{arguments}",{cores},"{mpi}",{mean:.3e},'
'{ci:.3e},{n},{dur:.1f},{E1:.5e},{E2:.5e},{E3:.5e}\n'.format(
script=os.path.basename(sys.argv[0]),
arguments=" ".join(map(str, sys.argv[1:])),
cores=nproc, dur=main_tock - main_tick, n=measurement_steps,
mpi="OMPI_COMM_WORLD_SIZE" in os.environ, mean=avg, ci=ci,
E1=system.analysis.energy()["total"],
E2=system.analysis.energy()["kinetic"],
E3=system.analysis.energy()["non_bonded"]))
if not os.path.isfile(report_path):
report = ('"script","arguments","cores","MPI","mean","ci",'
'"steps_per_tick","duration","E1","E2","E3"\n' + report)
with open(report_path, 'a') as f:
f.write(report)
else:
# use visualizer
visualizer = visualization.openGLLive(system)
def main_thread():
while True:
system.integrator.run(1)
visualizer.update()
sleep(1/60.) # limit framerate to at most 60 FPS
t = Thread(target=main_thread)
t.daemon = True
t.start()
visualizer.start()
#
# Copyright (C) 2013-2018 The ESPResSo project
#
# This file is part of ESPResSo.
#
# ESPResSo is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ESPResSo is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import print_function
import os
import sys
import numpy as np
import espressomd
from time import time, sleep
from espressomd import thermostat
from espressomd import electrostatics
required_features = ["ELECTROSTATICS", "LENNARD_JONES", "MASS"]
espressomd.assert_features(required_features)
print(espressomd.features())
# Interaction parameters (repulsive Lennard-Jones)
#############################################################
lj_cap = 20. # force cap
species = ["Cl", "Na", "Solvent"]
types = {"Cl": 0, "Na": 1, "Solvent": 2}
charges = {"Cl": -1.0, "Na": 1.0, "Solvent": 0.0}
lj_sigmas = {"Cl": 3.85, "Na": 2.52, "Solvent": 1.8}
lj_epsilons = {"Cl": 192.45, "Na": 17.44, "Solvent": 50.0}
lj_cuts = {"Cl": 2.0 * lj_sigmas["Cl"], "Na": 2.0 * lj_sigmas["Na"],
"Solvent": 2.0 * lj_sigmas["Solvent"]}
masses = {"Cl": 35.453, "Na": 22.99, "Solvent": 18.0}
# System parameters
#############################################################
try:
nproc = int(os.environ.get("OMPI_COMM_WORLD_SIZE", 1))
n_part_per_core = int(sys.argv[1])
n_part = nproc * n_part_per_core
sim_type = sys.argv[2]
assert sim_type in ('solution', 'gas')
mode = "benchmark"
if len(sys.argv) == 4:
assert sys.argv[3] == "--visualize"
mode = "visualization"
from espressomd import visualization
from threading import Thread
except (ValueError, IndexError) as err:
print(err.message)
print("\nUsage: [mpiexec -np <N cores>] pypresso lj.py "
"<N particles per core> <type> [--visualize]\n")
exit(1)
measurement_steps = int(np.round(2e6 / n_part_per_core, -2))
assert(measurement_steps >= 100), \
"{} steps per tick are too short".format(measurement_steps)
# volume of N spheres with radius r: N * (4/3*pi*r^3)
density = 0.25
if sim_type == "gas":
lj_sig = lj_sigmas["Na"] + lj_sigmas["Cl"]
else:
lj_sig = lj_sigmas["Solvent"]
box_l = (n_part * 4. / 3. * np.pi * (lj_sig / 2.)**3 / density)**(1./3.)
if sim_type == "gas":
species = ["Cl", "Na"]
# electrostatics
# E = 1 /(4*pi*e_0*e_r)*q1*q2/r
# e_0 in F/m or C^2/N/m^2
# E in N.M or J
Na = 6.0221408e23 # Avogadro's constant
epsilon_0 = 8.85419e-12 # vacuum permittivity
epsilon_r = 4.0 # relative permittivity
q = 1.6021766e-19 # elementary charge
# Coulomb prefactor in kJ/mol, using Angstroms
prefactor = 1 / (4 * np.pi * epsilon_0 * epsilon_r) * q**2 * Na * 1e-3 * 1e10
# temperature
kB = 1.38064852e-23 # Boltzmann's constant in J/K
kB_kjmol = kB * Na / 1000 # Boltzmann's constant in kJ/mol/K
SI_temperature = 400.0
temperature = SI_temperature * kB_kjmol
# System
#############################################################
system = espressomd.System(box_l=3*(box_l,))
system.cell_system.set_domain_decomposition(use_verlet_lists=True)
# PRNG seeds
#############################################################
system.random_number_generator_state = list(range(
nproc * (system._get_PRNG_state_size() + 1)))
#system.random_number_generator_state = list(range(len(system.random_number_generator_state)))
np.random.seed(1)
# Integration parameters
#############################################################
system.time_step = 0.01
system.cell_system.skin = 1.2
system.thermostat.turn_off()
#############################################################
# Setup System #
#############################################################
# Interaction setup
#############################################################
for i in range(len(species)):
for j in range(i, len(species)):
s = [species[i], species[j]]
lj_sig = (lj_sigmas[s[0]] + lj_sigmas[s[1]]) * 0.5
lj_cut = (lj_cuts[s[0]] + lj_cuts[s[1]]) * 0.5
lj_eps = (lj_epsilons[s[0]] * lj_epsilons[s[1]])**0.5
system.non_bonded_inter[types[s[0]], types[s[1]]].lennard_jones.set_params(
epsilon=lj_eps, sigma=lj_sig, cutoff=lj_cut, shift="auto")
# Particle setup
#############################################################
if sim_type == "gas":
for i in range(int(n_part / 2.)):
for t in ["Na", "Cl"]:
system.part.add(pos=np.random.random(3) * system.box_l,
q=charges[t], type=types[t], mass=masses[t])
else:
for i in range(int(n_part / 30.)):
for t in ["Na", "Cl"] + 28* ["Solvent"]:
system.part.add(pos=np.random.random(3) * system.box_l,
q=charges[t], type=types[t], mass=masses[t])
#############################################################
# Warmup Integration #
#############################################################
energy = system.analysis.energy()
print("Before Minimization: E_total = {}".format(energy['total']))
system.minimize_energy.init(f_max=1000, gamma=30.0,
max_steps=1000, max_displacement=0.01)
system.minimize_energy.minimize()
energy = system.analysis.energy()
print("After Minimization: E_total = {}".format(energy['total']))
print("Tune p3m")
p3m = electrostatics.P3M(prefactor=prefactor, accuracy=1e-1)
system.actors.add(p3m)
system.thermostat.set_langevin(kT=temperature, gamma=2.0)
if mode == 'benchmark':
report_path = 'benchmarks.csv'
if '${CMAKE_BINARY_DIR}'[0] != '$': # CMake variable
report_path = '${CMAKE_BINARY_DIR}/benchmarks.csv.part'
# print initial energies
energies = system.analysis.energy()
print(energies)
# time integration loop
print("Timing every {} steps".format(measurement_steps))
main_tick = time()
all_t = []
for i in range(30):
tick = time()
system.integrator.run(measurement_steps)
tock = time()
t = (tock-tick) / measurement_steps
print('step {}, time = {:.2e}, verlet: {:.2f}'.format(i, t,
system.cell_system.get_state()["verlet_reuse"]))
all_t.append(t)
main_tock = time()
# average time
all_t = np.array(all_t)
avg = np.average(all_t)
ci = 1.96 * np.std(all_t) / np.sqrt(len(all_t) - 1)
print("average: {:.3e} +/- {:.3e} (95% C.I.)".format(avg, ci))
# print final energies
energies = system.analysis.energy()
print(energies)
# write report
report = ('"{script}","{arguments}",{cores},"{mpi}",{mean:.3e},'
'{ci:.3e},{n},{dur:.1f},{E1:.5e},{E2:.5e},{E3:.5e}\n'.format(
script=os.path.basename(sys.argv[0]),
arguments=" ".join(map(str, sys.argv[1:])),
cores=nproc, dur=main_tock - main_tick, n=measurement_steps,
mpi="OMPI_COMM_WORLD_SIZE" in os.environ, mean=avg, ci=ci,
E1=system.analysis.energy()["total"],
E2=system.analysis.energy()["coulomb"],
E3=system.analysis.energy()["non_bonded"]))
if not os.path.isfile(report_path):
report = ('"script","arguments","cores","MPI","mean","ci",'
'"steps_per_tick","duration","E1","E2","E3"\n' + report)
with open(report_path, 'a') as f:
f.write(report)
else:
# use visualizer
visualizer = visualization.openGLLive(system)
visualizer.run(1)
#!/bin/bash
commits="c1d850ef3c4 c72cb35c3c5 eaa84cd1c92"
configs="myconfig-minimal.hpp ../src/core/myconfig-default.hpp ../maintainer/configs/maxset.hpp"
cd "$(git rev-parse --show-toplevel)"
mkdir -p build
cd build
cat > myconfig-minimal.hpp << EOF
#define ELECTROSTATICS
#define LENNARD_JONES
#define MASS
EOF
rm -f benchmarks.log
cat > benchmarks.csv << EOF
"commit","config","script","arguments","cores","MPI","mean","ci","steps_per_tick","duration","E1","E2","E3"
EOF
for commit in ${commits}
do
rm -rf src/ maintainer/
git checkout ${commit} ../src
cmake .. -DWITH_BENCHMARKS=ON
for config in ${configs}
do
cp ${config} myconfig.hpp
make -j$(nproc)
rm -f benchmarks.csv.part
touch benchmarks.csv.part
make benchmark 2>&1 | tee benchmarks.log
sed -ri "s/^/\"${commit}\",\"$(basename ${config})\",/" benchmarks.csv.part
cat benchmarks.csv.part >> benchmarks.csv
done
done
git checkout HEAD ../src
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment