thesis version
This commit is contained in:
commit
b688da651b
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
build/
|
||||||
|
debug/
|
||||||
|
release/
|
||||||
|
clang/
|
||||||
|
notes
|
||||||
|
__pycache__/
|
205
CMakeLists.txt
Normal file
205
CMakeLists.txt
Normal file
@ -0,0 +1,205 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.0)
|
||||||
|
get_filename_component(project_name ${CMAKE_CURRENT_SOURCE_DIR} NAME)
|
||||||
|
project(${project_name})
|
||||||
|
|
||||||
|
set(EXTENSION "cpp")
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 99)
|
||||||
|
set(CMAKE_CXX_STANDARD 14)
|
||||||
|
|
||||||
|
set(GEN_BINARY OFF)
|
||||||
|
set(GEN_LIBRARY OFF)
|
||||||
|
set(LIB_TYPE STATIC) # NONE, STATIC, SHARED, MODULE
|
||||||
|
set(LIBS_TYPE STATIC)
|
||||||
|
|
||||||
|
set(FLAGS_ANY "-Wall -Wextra -Wfatal-errors -Winline -fopenmp")
|
||||||
|
set(FLAGS_DEBUG "-DDEBUG -Og -pg -fsanitize=thread")
|
||||||
|
set(FLAGS_RELEASE "-DNDEBUG -O2")
|
||||||
|
|
||||||
|
set(SRCDIRS src)
|
||||||
|
set(LIBSDIRS lib)
|
||||||
|
set(TESTSDIRS celero tests plot)
|
||||||
|
set(EXAMPLESDIRS examples)
|
||||||
|
set(MANDIRS )
|
||||||
|
|
||||||
|
set(INCLUDE_DIRS inc lib)
|
||||||
|
set(LIBRARIES "-lpthread")
|
||||||
|
|
||||||
|
set(celero_FLAGS "-fopenmp")
|
||||||
|
set(celero_INCLUDE_DIRS "celero")
|
||||||
|
set(celero_LIBRARIES "-lpthread -fopenmp")
|
||||||
|
|
||||||
|
set(USER_LIBRARIES "")
|
||||||
|
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${FLAGS_ANY}")
|
||||||
|
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${FLAGS_ANY} ${FLAGS_DEBUG}")
|
||||||
|
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${FLAGS_ANY} ${FLAGS_RELEASE}")
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${FLAGS_ANY}")
|
||||||
|
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${FLAGS_ANY} ${FLAGS_DEBUG}")
|
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${FLAGS_ANY} ${FLAGS_RELEASE}")
|
||||||
|
|
||||||
|
if(USE_SANITIZER STREQUAL "Address")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address")
|
||||||
|
elseif(USE_SANITIZER STREQUAL "Leak")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=leak")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=leak")
|
||||||
|
elseif(USE_SANITIZER STREQUAL "Thread")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
|
||||||
|
elseif(USE_SANITIZER STREQUAL "Undefined")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined")
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
## Libraries
|
||||||
|
if(NOT ${LIB_TYPE} MATCHES "^NONE$")
|
||||||
|
# Project library
|
||||||
|
if(GEN_LIBRARY)
|
||||||
|
set(lib_src "")
|
||||||
|
foreach(srcdir ${SRCDIRS})
|
||||||
|
set(srcpath ${CMAKE_CURRENT_SOURCE_DIR}/${srcdir})
|
||||||
|
file(GLOB_RECURSE tmpsrc ${srcpath}/*.${EXTENSION})
|
||||||
|
list(APPEND lib_src ${tmpsrc})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
set(lib ${PROJECT_NAME})
|
||||||
|
if(lib_src)
|
||||||
|
message(STATUS "+ Library: ${lib}")
|
||||||
|
add_library(${lib} ${LIB_TYPE} ${lib_src})
|
||||||
|
target_include_directories(${lib} PUBLIC ${INCLUDE_DIRS})
|
||||||
|
target_link_libraries(${lib} ${LIBRARIES})
|
||||||
|
list(APPEND USER_LIBRARIES ${lib})
|
||||||
|
else()
|
||||||
|
message(WARNING "! Library ${lib}: no sources")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
## Other libraries
|
||||||
|
if(NOT ${LIBS_TYPE} MATCHES "^NONE$")
|
||||||
|
foreach(libsdir ${LIBSDIRS})
|
||||||
|
set(libspath ${CMAKE_CURRENT_SOURCE_DIR}/${libsdir})
|
||||||
|
file(GLOB libs RELATIVE ${libspath} ${libspath}/*)
|
||||||
|
if(libs)
|
||||||
|
foreach(child ${libs})
|
||||||
|
set(lib "")
|
||||||
|
if(IS_DIRECTORY ${libspath}/${child})
|
||||||
|
set(lib ${child})
|
||||||
|
file(GLOB_RECURSE lib_src ${libspath}/${child}/*.${EXTENSION})
|
||||||
|
else()
|
||||||
|
message(WARNING "! Ignoring file: ${libsdir}/${child}")
|
||||||
|
endif()
|
||||||
|
if(lib)
|
||||||
|
if(lib_src)
|
||||||
|
message(STATUS "+ Library: ${lib}")
|
||||||
|
add_library(${lib} ${LIBS_TYPE} ${lib_src})
|
||||||
|
target_include_directories(${lib} PUBLIC ${INCLUDE_DIRS})
|
||||||
|
target_link_libraries(${lib} ${LIBRARIES})
|
||||||
|
list(APPEND USER_LIBRARIES ${lib})
|
||||||
|
else()
|
||||||
|
message(WARNING "! Library ${lib}: no sources")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
## Binary
|
||||||
|
if(GEN_BINARY)
|
||||||
|
set(src "")
|
||||||
|
foreach(srcdir ${SRCDIRS})
|
||||||
|
set(srcpath ${CMAKE_CURRENT_SOURCE_DIR}/${srcdir})
|
||||||
|
file(GLOB_RECURSE tmpsrc ${srcpath}/*.${EXTENSION})
|
||||||
|
list(APPEND src ${tmpsrc})
|
||||||
|
endforeach()
|
||||||
|
set(bin ${PROJECT_NAME})
|
||||||
|
if(src)
|
||||||
|
if(GEN_LIBRARY)
|
||||||
|
set(bin ${bin}.bin)
|
||||||
|
endif()
|
||||||
|
message(STATUS "+ Binary: ${bin}")
|
||||||
|
add_executable(${bin} ${src})
|
||||||
|
target_include_directories(${bin} PUBLIC ${LIBSDIRS} ${INCLUDE_DIRS})
|
||||||
|
target_link_libraries(${bin} ${LIBRARIES} ${USER_LIBRARIES})
|
||||||
|
else()
|
||||||
|
message(WARNING "! Binary ${bin}: no sources")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
## Tests
|
||||||
|
foreach(testsdir ${TESTSDIRS})
|
||||||
|
set(testspath ${CMAKE_CURRENT_SOURCE_DIR}/${testsdir})
|
||||||
|
file(GLOB_RECURSE tests_src ${testspath}/*.${EXTENSION})
|
||||||
|
if(tests_src)
|
||||||
|
set(tests ${testsdir}_${PROJECT_NAME})
|
||||||
|
message(STATUS "+ Tests: ${tests}")
|
||||||
|
add_executable(${tests} ${tests_src})
|
||||||
|
target_compile_options(${tests} PUBLIC ${${testsdir}_FLAGS})
|
||||||
|
target_include_directories(${tests} PUBLIC ${SRCDIRS} ${LIBSDIRS} ${INCLUDE_DIRS} ${${testsdir}_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(${tests} ${LIBRARIES} ${USER_LIBRARIES} ${${testsdir}_LIBRARIES})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
foreach(examplesdir ${EXAMPLESDIRS})
|
||||||
|
set(examplespath ${CMAKE_CURRENT_SOURCE_DIR}/${examplesdir})
|
||||||
|
file(GLOB examples RELATIVE ${examplespath} ${examplespath}/*)
|
||||||
|
if(examples)
|
||||||
|
foreach(child ${examples})
|
||||||
|
set(example_bin_filename "")
|
||||||
|
set(example "")
|
||||||
|
if(IS_DIRECTORY ${examplespath}/${child})
|
||||||
|
set(example_bin_filename ${child})
|
||||||
|
set(example ${examplesdir}_${example_bin_filename})
|
||||||
|
file(GLOB_RECURSE example_src ${examplespath}/${child}/*.${EXTENSION})
|
||||||
|
else()
|
||||||
|
get_filename_component(extension ${child} EXT)
|
||||||
|
if(${extension} MATCHES "^.${EXTENSION}$")
|
||||||
|
get_filename_component(example_name ${child} NAME_WE)
|
||||||
|
set(example_bin_filename ${example_name})
|
||||||
|
set(example ${examplesdir}_${example_bin_filename})
|
||||||
|
set(example_src ${examplespath}/${child})
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
if(example)
|
||||||
|
if(example_src)
|
||||||
|
message(STATUS "+ Example: ${examplesdir}/${example}")
|
||||||
|
add_executable(${example} ${example_src})
|
||||||
|
target_include_directories(${example} PUBLIC ${SRCDIRS} ${LIBSDIRS} ${INCLUDE_DIRS})
|
||||||
|
target_link_libraries(${example} ${LIBRARIES} ${USER_LIBRARIES})
|
||||||
|
set_target_properties(${example} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${examplesdir})
|
||||||
|
set_target_properties(${example} PROPERTIES OUTPUT_NAME ${example_bin_filename})
|
||||||
|
else()
|
||||||
|
message(WARNING "! Example ${example}: no sources")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
## Man pages
|
||||||
|
foreach(mandir ${MANDIRS})
|
||||||
|
set(MANPATH ${CMAKE_CURRENT_SOURCE_DIR}/${mandir})
|
||||||
|
set(MAN_OUTPATH ${CMAKE_BINARY_DIR}/deb/usr/share/man)
|
||||||
|
file(GLOB_RECURSE man_src RELATIVE ${MANPATH} ${MANPATH}/*)
|
||||||
|
if(man_src)
|
||||||
|
set(man_outfiles "")
|
||||||
|
foreach(man_file IN LISTS man_src)
|
||||||
|
set(man_outfile ${MAN_OUTPATH}/${man_file}.gz)
|
||||||
|
get_filename_component(man_outdir ${man_outfile} DIRECTORY)
|
||||||
|
list(APPEND man_outfiles ${man_outfile})
|
||||||
|
message(STATUS "+ manpage: ${man_file}")
|
||||||
|
add_custom_command(OUTPUT ${man_outfile}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E make_directory ${man_outdir}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy ${MANPATH}/${man_file} ${MAN_OUTPATH}/${man_file}
|
||||||
|
COMMAND gzip -f ${MAN_OUTPATH}/${man_file}
|
||||||
|
DEPENDS ${MANPATH}/${man_file})
|
||||||
|
endforeach()
|
||||||
|
add_custom_target(man ALL DEPENDS ${man_outfiles})
|
||||||
|
endif()
|
||||||
|
endforeach()
|
674
LICENSE
Normal file
674
LICENSE
Normal file
@ -0,0 +1,674 @@
|
|||||||
|
GNU GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
Preamble
|
||||||
|
|
||||||
|
The GNU General Public License is a free, copyleft license for
|
||||||
|
software and other kinds of works.
|
||||||
|
|
||||||
|
The licenses for most software and other practical works are designed
|
||||||
|
to take away your freedom to share and change the works. By contrast,
|
||||||
|
the GNU General Public License is intended to guarantee your freedom to
|
||||||
|
share and change all versions of a program--to make sure it remains free
|
||||||
|
software for all its users. We, the Free Software Foundation, use the
|
||||||
|
GNU General Public License for most of our software; it applies also to
|
||||||
|
any other work released this way by its authors. You can apply it to
|
||||||
|
your programs, too.
|
||||||
|
|
||||||
|
When we speak of free software, we are referring to freedom, not
|
||||||
|
price. Our General Public Licenses are designed to make sure that you
|
||||||
|
have the freedom to distribute copies of free software (and charge for
|
||||||
|
them if you wish), that you receive source code or can get it if you
|
||||||
|
want it, that you can change the software or use pieces of it in new
|
||||||
|
free programs, and that you know you can do these things.
|
||||||
|
|
||||||
|
To protect your rights, we need to prevent others from denying you
|
||||||
|
these rights or asking you to surrender the rights. Therefore, you have
|
||||||
|
certain responsibilities if you distribute copies of the software, or if
|
||||||
|
you modify it: responsibilities to respect the freedom of others.
|
||||||
|
|
||||||
|
For example, if you distribute copies of such a program, whether
|
||||||
|
gratis or for a fee, you must pass on to the recipients the same
|
||||||
|
freedoms that you received. You must make sure that they, too, receive
|
||||||
|
or can get the source code. And you must show them these terms so they
|
||||||
|
know their rights.
|
||||||
|
|
||||||
|
Developers that use the GNU GPL protect your rights with two steps:
|
||||||
|
(1) assert copyright on the software, and (2) offer you this License
|
||||||
|
giving you legal permission to copy, distribute and/or modify it.
|
||||||
|
|
||||||
|
For the developers' and authors' protection, the GPL clearly explains
|
||||||
|
that there is no warranty for this free software. For both users' and
|
||||||
|
authors' sake, the GPL requires that modified versions be marked as
|
||||||
|
changed, so that their problems will not be attributed erroneously to
|
||||||
|
authors of previous versions.
|
||||||
|
|
||||||
|
Some devices are designed to deny users access to install or run
|
||||||
|
modified versions of the software inside them, although the manufacturer
|
||||||
|
can do so. This is fundamentally incompatible with the aim of
|
||||||
|
protecting users' freedom to change the software. The systematic
|
||||||
|
pattern of such abuse occurs in the area of products for individuals to
|
||||||
|
use, which is precisely where it is most unacceptable. Therefore, we
|
||||||
|
have designed this version of the GPL to prohibit the practice for those
|
||||||
|
products. If such problems arise substantially in other domains, we
|
||||||
|
stand ready to extend this provision to those domains in future versions
|
||||||
|
of the GPL, as needed to protect the freedom of users.
|
||||||
|
|
||||||
|
Finally, every program is threatened constantly by software patents.
|
||||||
|
States should not allow patents to restrict development and use of
|
||||||
|
software on general-purpose computers, but in those that do, we wish to
|
||||||
|
avoid the special danger that patents applied to a free program could
|
||||||
|
make it effectively proprietary. To prevent this, the GPL assures that
|
||||||
|
patents cannot be used to render the program non-free.
|
||||||
|
|
||||||
|
The precise terms and conditions for copying, distribution and
|
||||||
|
modification follow.
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
0. Definitions.
|
||||||
|
|
||||||
|
"This License" refers to version 3 of the GNU General Public License.
|
||||||
|
|
||||||
|
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||||
|
works, such as semiconductor masks.
|
||||||
|
|
||||||
|
"The Program" refers to any copyrightable work licensed under this
|
||||||
|
License. Each licensee is addressed as "you". "Licensees" and
|
||||||
|
"recipients" may be individuals or organizations.
|
||||||
|
|
||||||
|
To "modify" a work means to copy from or adapt all or part of the work
|
||||||
|
in a fashion requiring copyright permission, other than the making of an
|
||||||
|
exact copy. The resulting work is called a "modified version" of the
|
||||||
|
earlier work or a work "based on" the earlier work.
|
||||||
|
|
||||||
|
A "covered work" means either the unmodified Program or a work based
|
||||||
|
on the Program.
|
||||||
|
|
||||||
|
To "propagate" a work means to do anything with it that, without
|
||||||
|
permission, would make you directly or secondarily liable for
|
||||||
|
infringement under applicable copyright law, except executing it on a
|
||||||
|
computer or modifying a private copy. Propagation includes copying,
|
||||||
|
distribution (with or without modification), making available to the
|
||||||
|
public, and in some countries other activities as well.
|
||||||
|
|
||||||
|
To "convey" a work means any kind of propagation that enables other
|
||||||
|
parties to make or receive copies. Mere interaction with a user through
|
||||||
|
a computer network, with no transfer of a copy, is not conveying.
|
||||||
|
|
||||||
|
An interactive user interface displays "Appropriate Legal Notices"
|
||||||
|
to the extent that it includes a convenient and prominently visible
|
||||||
|
feature that (1) displays an appropriate copyright notice, and (2)
|
||||||
|
tells the user that there is no warranty for the work (except to the
|
||||||
|
extent that warranties are provided), that licensees may convey the
|
||||||
|
work under this License, and how to view a copy of this License. If
|
||||||
|
the interface presents a list of user commands or options, such as a
|
||||||
|
menu, a prominent item in the list meets this criterion.
|
||||||
|
|
||||||
|
1. Source Code.
|
||||||
|
|
||||||
|
The "source code" for a work means the preferred form of the work
|
||||||
|
for making modifications to it. "Object code" means any non-source
|
||||||
|
form of a work.
|
||||||
|
|
||||||
|
A "Standard Interface" means an interface that either is an official
|
||||||
|
standard defined by a recognized standards body, or, in the case of
|
||||||
|
interfaces specified for a particular programming language, one that
|
||||||
|
is widely used among developers working in that language.
|
||||||
|
|
||||||
|
The "System Libraries" of an executable work include anything, other
|
||||||
|
than the work as a whole, that (a) is included in the normal form of
|
||||||
|
packaging a Major Component, but which is not part of that Major
|
||||||
|
Component, and (b) serves only to enable use of the work with that
|
||||||
|
Major Component, or to implement a Standard Interface for which an
|
||||||
|
implementation is available to the public in source code form. A
|
||||||
|
"Major Component", in this context, means a major essential component
|
||||||
|
(kernel, window system, and so on) of the specific operating system
|
||||||
|
(if any) on which the executable work runs, or a compiler used to
|
||||||
|
produce the work, or an object code interpreter used to run it.
|
||||||
|
|
||||||
|
The "Corresponding Source" for a work in object code form means all
|
||||||
|
the source code needed to generate, install, and (for an executable
|
||||||
|
work) run the object code and to modify the work, including scripts to
|
||||||
|
control those activities. However, it does not include the work's
|
||||||
|
System Libraries, or general-purpose tools or generally available free
|
||||||
|
programs which are used unmodified in performing those activities but
|
||||||
|
which are not part of the work. For example, Corresponding Source
|
||||||
|
includes interface definition files associated with source files for
|
||||||
|
the work, and the source code for shared libraries and dynamically
|
||||||
|
linked subprograms that the work is specifically designed to require,
|
||||||
|
such as by intimate data communication or control flow between those
|
||||||
|
subprograms and other parts of the work.
|
||||||
|
|
||||||
|
The Corresponding Source need not include anything that users
|
||||||
|
can regenerate automatically from other parts of the Corresponding
|
||||||
|
Source.
|
||||||
|
|
||||||
|
The Corresponding Source for a work in source code form is that
|
||||||
|
same work.
|
||||||
|
|
||||||
|
2. Basic Permissions.
|
||||||
|
|
||||||
|
All rights granted under this License are granted for the term of
|
||||||
|
copyright on the Program, and are irrevocable provided the stated
|
||||||
|
conditions are met. This License explicitly affirms your unlimited
|
||||||
|
permission to run the unmodified Program. The output from running a
|
||||||
|
covered work is covered by this License only if the output, given its
|
||||||
|
content, constitutes a covered work. This License acknowledges your
|
||||||
|
rights of fair use or other equivalent, as provided by copyright law.
|
||||||
|
|
||||||
|
You may make, run and propagate covered works that you do not
|
||||||
|
convey, without conditions so long as your license otherwise remains
|
||||||
|
in force. You may convey covered works to others for the sole purpose
|
||||||
|
of having them make modifications exclusively for you, or provide you
|
||||||
|
with facilities for running those works, provided that you comply with
|
||||||
|
the terms of this License in conveying all material for which you do
|
||||||
|
not control copyright. Those thus making or running the covered works
|
||||||
|
for you must do so exclusively on your behalf, under your direction
|
||||||
|
and control, on terms that prohibit them from making any copies of
|
||||||
|
your copyrighted material outside their relationship with you.
|
||||||
|
|
||||||
|
Conveying under any other circumstances is permitted solely under
|
||||||
|
the conditions stated below. Sublicensing is not allowed; section 10
|
||||||
|
makes it unnecessary.
|
||||||
|
|
||||||
|
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||||
|
|
||||||
|
No covered work shall be deemed part of an effective technological
|
||||||
|
measure under any applicable law fulfilling obligations under article
|
||||||
|
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||||
|
similar laws prohibiting or restricting circumvention of such
|
||||||
|
measures.
|
||||||
|
|
||||||
|
When you convey a covered work, you waive any legal power to forbid
|
||||||
|
circumvention of technological measures to the extent such circumvention
|
||||||
|
is effected by exercising rights under this License with respect to
|
||||||
|
the covered work, and you disclaim any intention to limit operation or
|
||||||
|
modification of the work as a means of enforcing, against the work's
|
||||||
|
users, your or third parties' legal rights to forbid circumvention of
|
||||||
|
technological measures.
|
||||||
|
|
||||||
|
4. Conveying Verbatim Copies.
|
||||||
|
|
||||||
|
You may convey verbatim copies of the Program's source code as you
|
||||||
|
receive it, in any medium, provided that you conspicuously and
|
||||||
|
appropriately publish on each copy an appropriate copyright notice;
|
||||||
|
keep intact all notices stating that this License and any
|
||||||
|
non-permissive terms added in accord with section 7 apply to the code;
|
||||||
|
keep intact all notices of the absence of any warranty; and give all
|
||||||
|
recipients a copy of this License along with the Program.
|
||||||
|
|
||||||
|
You may charge any price or no price for each copy that you convey,
|
||||||
|
and you may offer support or warranty protection for a fee.
|
||||||
|
|
||||||
|
5. Conveying Modified Source Versions.
|
||||||
|
|
||||||
|
You may convey a work based on the Program, or the modifications to
|
||||||
|
produce it from the Program, in the form of source code under the
|
||||||
|
terms of section 4, provided that you also meet all of these conditions:
|
||||||
|
|
||||||
|
a) The work must carry prominent notices stating that you modified
|
||||||
|
it, and giving a relevant date.
|
||||||
|
|
||||||
|
b) The work must carry prominent notices stating that it is
|
||||||
|
released under this License and any conditions added under section
|
||||||
|
7. This requirement modifies the requirement in section 4 to
|
||||||
|
"keep intact all notices".
|
||||||
|
|
||||||
|
c) You must license the entire work, as a whole, under this
|
||||||
|
License to anyone who comes into possession of a copy. This
|
||||||
|
License will therefore apply, along with any applicable section 7
|
||||||
|
additional terms, to the whole of the work, and all its parts,
|
||||||
|
regardless of how they are packaged. This License gives no
|
||||||
|
permission to license the work in any other way, but it does not
|
||||||
|
invalidate such permission if you have separately received it.
|
||||||
|
|
||||||
|
d) If the work has interactive user interfaces, each must display
|
||||||
|
Appropriate Legal Notices; however, if the Program has interactive
|
||||||
|
interfaces that do not display Appropriate Legal Notices, your
|
||||||
|
work need not make them do so.
|
||||||
|
|
||||||
|
A compilation of a covered work with other separate and independent
|
||||||
|
works, which are not by their nature extensions of the covered work,
|
||||||
|
and which are not combined with it such as to form a larger program,
|
||||||
|
in or on a volume of a storage or distribution medium, is called an
|
||||||
|
"aggregate" if the compilation and its resulting copyright are not
|
||||||
|
used to limit the access or legal rights of the compilation's users
|
||||||
|
beyond what the individual works permit. Inclusion of a covered work
|
||||||
|
in an aggregate does not cause this License to apply to the other
|
||||||
|
parts of the aggregate.
|
||||||
|
|
||||||
|
6. Conveying Non-Source Forms.
|
||||||
|
|
||||||
|
You may convey a covered work in object code form under the terms
|
||||||
|
of sections 4 and 5, provided that you also convey the
|
||||||
|
machine-readable Corresponding Source under the terms of this License,
|
||||||
|
in one of these ways:
|
||||||
|
|
||||||
|
a) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by the
|
||||||
|
Corresponding Source fixed on a durable physical medium
|
||||||
|
customarily used for software interchange.
|
||||||
|
|
||||||
|
b) Convey the object code in, or embodied in, a physical product
|
||||||
|
(including a physical distribution medium), accompanied by a
|
||||||
|
written offer, valid for at least three years and valid for as
|
||||||
|
long as you offer spare parts or customer support for that product
|
||||||
|
model, to give anyone who possesses the object code either (1) a
|
||||||
|
copy of the Corresponding Source for all the software in the
|
||||||
|
product that is covered by this License, on a durable physical
|
||||||
|
medium customarily used for software interchange, for a price no
|
||||||
|
more than your reasonable cost of physically performing this
|
||||||
|
conveying of source, or (2) access to copy the
|
||||||
|
Corresponding Source from a network server at no charge.
|
||||||
|
|
||||||
|
c) Convey individual copies of the object code with a copy of the
|
||||||
|
written offer to provide the Corresponding Source. This
|
||||||
|
alternative is allowed only occasionally and noncommercially, and
|
||||||
|
only if you received the object code with such an offer, in accord
|
||||||
|
with subsection 6b.
|
||||||
|
|
||||||
|
d) Convey the object code by offering access from a designated
|
||||||
|
place (gratis or for a charge), and offer equivalent access to the
|
||||||
|
Corresponding Source in the same way through the same place at no
|
||||||
|
further charge. You need not require recipients to copy the
|
||||||
|
Corresponding Source along with the object code. If the place to
|
||||||
|
copy the object code is a network server, the Corresponding Source
|
||||||
|
may be on a different server (operated by you or a third party)
|
||||||
|
that supports equivalent copying facilities, provided you maintain
|
||||||
|
clear directions next to the object code saying where to find the
|
||||||
|
Corresponding Source. Regardless of what server hosts the
|
||||||
|
Corresponding Source, you remain obligated to ensure that it is
|
||||||
|
available for as long as needed to satisfy these requirements.
|
||||||
|
|
||||||
|
e) Convey the object code using peer-to-peer transmission, provided
|
||||||
|
you inform other peers where the object code and Corresponding
|
||||||
|
Source of the work are being offered to the general public at no
|
||||||
|
charge under subsection 6d.
|
||||||
|
|
||||||
|
A separable portion of the object code, whose source code is excluded
|
||||||
|
from the Corresponding Source as a System Library, need not be
|
||||||
|
included in conveying the object code work.
|
||||||
|
|
||||||
|
A "User Product" is either (1) a "consumer product", which means any
|
||||||
|
tangible personal property which is normally used for personal, family,
|
||||||
|
or household purposes, or (2) anything designed or sold for incorporation
|
||||||
|
into a dwelling. In determining whether a product is a consumer product,
|
||||||
|
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||||
|
product received by a particular user, "normally used" refers to a
|
||||||
|
typical or common use of that class of product, regardless of the status
|
||||||
|
of the particular user or of the way in which the particular user
|
||||||
|
actually uses, or expects or is expected to use, the product. A product
|
||||||
|
is a consumer product regardless of whether the product has substantial
|
||||||
|
commercial, industrial or non-consumer uses, unless such uses represent
|
||||||
|
the only significant mode of use of the product.
|
||||||
|
|
||||||
|
"Installation Information" for a User Product means any methods,
|
||||||
|
procedures, authorization keys, or other information required to install
|
||||||
|
and execute modified versions of a covered work in that User Product from
|
||||||
|
a modified version of its Corresponding Source. The information must
|
||||||
|
suffice to ensure that the continued functioning of the modified object
|
||||||
|
code is in no case prevented or interfered with solely because
|
||||||
|
modification has been made.
|
||||||
|
|
||||||
|
If you convey an object code work under this section in, or with, or
|
||||||
|
specifically for use in, a User Product, and the conveying occurs as
|
||||||
|
part of a transaction in which the right of possession and use of the
|
||||||
|
User Product is transferred to the recipient in perpetuity or for a
|
||||||
|
fixed term (regardless of how the transaction is characterized), the
|
||||||
|
Corresponding Source conveyed under this section must be accompanied
|
||||||
|
by the Installation Information. But this requirement does not apply
|
||||||
|
if neither you nor any third party retains the ability to install
|
||||||
|
modified object code on the User Product (for example, the work has
|
||||||
|
been installed in ROM).
|
||||||
|
|
||||||
|
The requirement to provide Installation Information does not include a
|
||||||
|
requirement to continue to provide support service, warranty, or updates
|
||||||
|
for a work that has been modified or installed by the recipient, or for
|
||||||
|
the User Product in which it has been modified or installed. Access to a
|
||||||
|
network may be denied when the modification itself materially and
|
||||||
|
adversely affects the operation of the network or violates the rules and
|
||||||
|
protocols for communication across the network.
|
||||||
|
|
||||||
|
Corresponding Source conveyed, and Installation Information provided,
|
||||||
|
in accord with this section must be in a format that is publicly
|
||||||
|
documented (and with an implementation available to the public in
|
||||||
|
source code form), and must require no special password or key for
|
||||||
|
unpacking, reading or copying.
|
||||||
|
|
||||||
|
7. Additional Terms.
|
||||||
|
|
||||||
|
"Additional permissions" are terms that supplement the terms of this
|
||||||
|
License by making exceptions from one or more of its conditions.
|
||||||
|
Additional permissions that are applicable to the entire Program shall
|
||||||
|
be treated as though they were included in this License, to the extent
|
||||||
|
that they are valid under applicable law. If additional permissions
|
||||||
|
apply only to part of the Program, that part may be used separately
|
||||||
|
under those permissions, but the entire Program remains governed by
|
||||||
|
this License without regard to the additional permissions.
|
||||||
|
|
||||||
|
When you convey a copy of a covered work, you may at your option
|
||||||
|
remove any additional permissions from that copy, or from any part of
|
||||||
|
it. (Additional permissions may be written to require their own
|
||||||
|
removal in certain cases when you modify the work.) You may place
|
||||||
|
additional permissions on material, added by you to a covered work,
|
||||||
|
for which you have or can give appropriate copyright permission.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, for material you
|
||||||
|
add to a covered work, you may (if authorized by the copyright holders of
|
||||||
|
that material) supplement the terms of this License with terms:
|
||||||
|
|
||||||
|
a) Disclaiming warranty or limiting liability differently from the
|
||||||
|
terms of sections 15 and 16 of this License; or
|
||||||
|
|
||||||
|
b) Requiring preservation of specified reasonable legal notices or
|
||||||
|
author attributions in that material or in the Appropriate Legal
|
||||||
|
Notices displayed by works containing it; or
|
||||||
|
|
||||||
|
c) Prohibiting misrepresentation of the origin of that material, or
|
||||||
|
requiring that modified versions of such material be marked in
|
||||||
|
reasonable ways as different from the original version; or
|
||||||
|
|
||||||
|
d) Limiting the use for publicity purposes of names of licensors or
|
||||||
|
authors of the material; or
|
||||||
|
|
||||||
|
e) Declining to grant rights under trademark law for use of some
|
||||||
|
trade names, trademarks, or service marks; or
|
||||||
|
|
||||||
|
f) Requiring indemnification of licensors and authors of that
|
||||||
|
material by anyone who conveys the material (or modified versions of
|
||||||
|
it) with contractual assumptions of liability to the recipient, for
|
||||||
|
any liability that these contractual assumptions directly impose on
|
||||||
|
those licensors and authors.
|
||||||
|
|
||||||
|
All other non-permissive additional terms are considered "further
|
||||||
|
restrictions" within the meaning of section 10. If the Program as you
|
||||||
|
received it, or any part of it, contains a notice stating that it is
|
||||||
|
governed by this License along with a term that is a further
|
||||||
|
restriction, you may remove that term. If a license document contains
|
||||||
|
a further restriction but permits relicensing or conveying under this
|
||||||
|
License, you may add to a covered work material governed by the terms
|
||||||
|
of that license document, provided that the further restriction does
|
||||||
|
not survive such relicensing or conveying.
|
||||||
|
|
||||||
|
If you add terms to a covered work in accord with this section, you
|
||||||
|
must place, in the relevant source files, a statement of the
|
||||||
|
additional terms that apply to those files, or a notice indicating
|
||||||
|
where to find the applicable terms.
|
||||||
|
|
||||||
|
Additional terms, permissive or non-permissive, may be stated in the
|
||||||
|
form of a separately written license, or stated as exceptions;
|
||||||
|
the above requirements apply either way.
|
||||||
|
|
||||||
|
8. Termination.
|
||||||
|
|
||||||
|
You may not propagate or modify a covered work except as expressly
|
||||||
|
provided under this License. Any attempt otherwise to propagate or
|
||||||
|
modify it is void, and will automatically terminate your rights under
|
||||||
|
this License (including any patent licenses granted under the third
|
||||||
|
paragraph of section 11).
|
||||||
|
|
||||||
|
However, if you cease all violation of this License, then your
|
||||||
|
license from a particular copyright holder is reinstated (a)
|
||||||
|
provisionally, unless and until the copyright holder explicitly and
|
||||||
|
finally terminates your license, and (b) permanently, if the copyright
|
||||||
|
holder fails to notify you of the violation by some reasonable means
|
||||||
|
prior to 60 days after the cessation.
|
||||||
|
|
||||||
|
Moreover, your license from a particular copyright holder is
|
||||||
|
reinstated permanently if the copyright holder notifies you of the
|
||||||
|
violation by some reasonable means, this is the first time you have
|
||||||
|
received notice of violation of this License (for any work) from that
|
||||||
|
copyright holder, and you cure the violation prior to 30 days after
|
||||||
|
your receipt of the notice.
|
||||||
|
|
||||||
|
Termination of your rights under this section does not terminate the
|
||||||
|
licenses of parties who have received copies or rights from you under
|
||||||
|
this License. If your rights have been terminated and not permanently
|
||||||
|
reinstated, you do not qualify to receive new licenses for the same
|
||||||
|
material under section 10.
|
||||||
|
|
||||||
|
9. Acceptance Not Required for Having Copies.
|
||||||
|
|
||||||
|
You are not required to accept this License in order to receive or
|
||||||
|
run a copy of the Program. Ancillary propagation of a covered work
|
||||||
|
occurring solely as a consequence of using peer-to-peer transmission
|
||||||
|
to receive a copy likewise does not require acceptance. However,
|
||||||
|
nothing other than this License grants you permission to propagate or
|
||||||
|
modify any covered work. These actions infringe copyright if you do
|
||||||
|
not accept this License. Therefore, by modifying or propagating a
|
||||||
|
covered work, you indicate your acceptance of this License to do so.
|
||||||
|
|
||||||
|
10. Automatic Licensing of Downstream Recipients.
|
||||||
|
|
||||||
|
Each time you convey a covered work, the recipient automatically
|
||||||
|
receives a license from the original licensors, to run, modify and
|
||||||
|
propagate that work, subject to this License. You are not responsible
|
||||||
|
for enforcing compliance by third parties with this License.
|
||||||
|
|
||||||
|
An "entity transaction" is a transaction transferring control of an
|
||||||
|
organization, or substantially all assets of one, or subdividing an
|
||||||
|
organization, or merging organizations. If propagation of a covered
|
||||||
|
work results from an entity transaction, each party to that
|
||||||
|
transaction who receives a copy of the work also receives whatever
|
||||||
|
licenses to the work the party's predecessor in interest had or could
|
||||||
|
give under the previous paragraph, plus a right to possession of the
|
||||||
|
Corresponding Source of the work from the predecessor in interest, if
|
||||||
|
the predecessor has it or can get it with reasonable efforts.
|
||||||
|
|
||||||
|
You may not impose any further restrictions on the exercise of the
|
||||||
|
rights granted or affirmed under this License. For example, you may
|
||||||
|
not impose a license fee, royalty, or other charge for exercise of
|
||||||
|
rights granted under this License, and you may not initiate litigation
|
||||||
|
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||||
|
any patent claim is infringed by making, using, selling, offering for
|
||||||
|
sale, or importing the Program or any portion of it.
|
||||||
|
|
||||||
|
11. Patents.
|
||||||
|
|
||||||
|
A "contributor" is a copyright holder who authorizes use under this
|
||||||
|
License of the Program or a work on which the Program is based. The
|
||||||
|
work thus licensed is called the contributor's "contributor version".
|
||||||
|
|
||||||
|
A contributor's "essential patent claims" are all patent claims
|
||||||
|
owned or controlled by the contributor, whether already acquired or
|
||||||
|
hereafter acquired, that would be infringed by some manner, permitted
|
||||||
|
by this License, of making, using, or selling its contributor version,
|
||||||
|
but do not include claims that would be infringed only as a
|
||||||
|
consequence of further modification of the contributor version. For
|
||||||
|
purposes of this definition, "control" includes the right to grant
|
||||||
|
patent sublicenses in a manner consistent with the requirements of
|
||||||
|
this License.
|
||||||
|
|
||||||
|
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||||
|
patent license under the contributor's essential patent claims, to
|
||||||
|
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||||
|
propagate the contents of its contributor version.
|
||||||
|
|
||||||
|
In the following three paragraphs, a "patent license" is any express
|
||||||
|
agreement or commitment, however denominated, not to enforce a patent
|
||||||
|
(such as an express permission to practice a patent or covenant not to
|
||||||
|
sue for patent infringement). To "grant" such a patent license to a
|
||||||
|
party means to make such an agreement or commitment not to enforce a
|
||||||
|
patent against the party.
|
||||||
|
|
||||||
|
If you convey a covered work, knowingly relying on a patent license,
|
||||||
|
and the Corresponding Source of the work is not available for anyone
|
||||||
|
to copy, free of charge and under the terms of this License, through a
|
||||||
|
publicly available network server or other readily accessible means,
|
||||||
|
then you must either (1) cause the Corresponding Source to be so
|
||||||
|
available, or (2) arrange to deprive yourself of the benefit of the
|
||||||
|
patent license for this particular work, or (3) arrange, in a manner
|
||||||
|
consistent with the requirements of this License, to extend the patent
|
||||||
|
license to downstream recipients. "Knowingly relying" means you have
|
||||||
|
actual knowledge that, but for the patent license, your conveying the
|
||||||
|
covered work in a country, or your recipient's use of the covered work
|
||||||
|
in a country, would infringe one or more identifiable patents in that
|
||||||
|
country that you have reason to believe are valid.
|
||||||
|
|
||||||
|
If, pursuant to or in connection with a single transaction or
|
||||||
|
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||||
|
covered work, and grant a patent license to some of the parties
|
||||||
|
receiving the covered work authorizing them to use, propagate, modify
|
||||||
|
or convey a specific copy of the covered work, then the patent license
|
||||||
|
you grant is automatically extended to all recipients of the covered
|
||||||
|
work and works based on it.
|
||||||
|
|
||||||
|
A patent license is "discriminatory" if it does not include within
|
||||||
|
the scope of its coverage, prohibits the exercise of, or is
|
||||||
|
conditioned on the non-exercise of one or more of the rights that are
|
||||||
|
specifically granted under this License. You may not convey a covered
|
||||||
|
work if you are a party to an arrangement with a third party that is
|
||||||
|
in the business of distributing software, under which you make payment
|
||||||
|
to the third party based on the extent of your activity of conveying
|
||||||
|
the work, and under which the third party grants, to any of the
|
||||||
|
parties who would receive the covered work from you, a discriminatory
|
||||||
|
patent license (a) in connection with copies of the covered work
|
||||||
|
conveyed by you (or copies made from those copies), or (b) primarily
|
||||||
|
for and in connection with specific products or compilations that
|
||||||
|
contain the covered work, unless you entered into that arrangement,
|
||||||
|
or that patent license was granted, prior to 28 March 2007.
|
||||||
|
|
||||||
|
Nothing in this License shall be construed as excluding or limiting
|
||||||
|
any implied license or other defenses to infringement that may
|
||||||
|
otherwise be available to you under applicable patent law.
|
||||||
|
|
||||||
|
12. No Surrender of Others' Freedom.
|
||||||
|
|
||||||
|
If conditions are imposed on you (whether by court order, agreement or
|
||||||
|
otherwise) that contradict the conditions of this License, they do not
|
||||||
|
excuse you from the conditions of this License. If you cannot convey a
|
||||||
|
covered work so as to satisfy simultaneously your obligations under this
|
||||||
|
License and any other pertinent obligations, then as a consequence you may
|
||||||
|
not convey it at all. For example, if you agree to terms that obligate you
|
||||||
|
to collect a royalty for further conveying from those to whom you convey
|
||||||
|
the Program, the only way you could satisfy both those terms and this
|
||||||
|
License would be to refrain entirely from conveying the Program.
|
||||||
|
|
||||||
|
13. Use with the GNU Affero General Public License.
|
||||||
|
|
||||||
|
Notwithstanding any other provision of this License, you have
|
||||||
|
permission to link or combine any covered work with a work licensed
|
||||||
|
under version 3 of the GNU Affero General Public License into a single
|
||||||
|
combined work, and to convey the resulting work. The terms of this
|
||||||
|
License will continue to apply to the part which is the covered work,
|
||||||
|
but the special requirements of the GNU Affero General Public License,
|
||||||
|
section 13, concerning interaction through a network will apply to the
|
||||||
|
combination as such.
|
||||||
|
|
||||||
|
14. Revised Versions of this License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions of
|
||||||
|
the GNU General Public License from time to time. Such new versions will
|
||||||
|
be similar in spirit to the present version, but may differ in detail to
|
||||||
|
address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Program specifies that a certain numbered version of the GNU General
|
||||||
|
Public License "or any later version" applies to it, you have the
|
||||||
|
option of following the terms and conditions either of that numbered
|
||||||
|
version or of any later version published by the Free Software
|
||||||
|
Foundation. If the Program does not specify a version number of the
|
||||||
|
GNU General Public License, you may choose any version ever published
|
||||||
|
by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Program specifies that a proxy can decide which future
|
||||||
|
versions of the GNU General Public License can be used, that proxy's
|
||||||
|
public statement of acceptance of a version permanently authorizes you
|
||||||
|
to choose that version for the Program.
|
||||||
|
|
||||||
|
Later license versions may give you additional or different
|
||||||
|
permissions. However, no additional obligations are imposed on any
|
||||||
|
author or copyright holder as a result of your choosing to follow a
|
||||||
|
later version.
|
||||||
|
|
||||||
|
15. Disclaimer of Warranty.
|
||||||
|
|
||||||
|
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||||
|
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||||
|
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||||
|
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||||
|
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||||
|
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||||
|
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||||
|
|
||||||
|
16. Limitation of Liability.
|
||||||
|
|
||||||
|
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||||
|
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||||
|
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||||
|
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||||
|
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||||
|
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||||
|
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||||
|
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
SUCH DAMAGES.
|
||||||
|
|
||||||
|
17. Interpretation of Sections 15 and 16.
|
||||||
|
|
||||||
|
If the disclaimer of warranty and limitation of liability provided
|
||||||
|
above cannot be given local legal effect according to their terms,
|
||||||
|
reviewing courts shall apply local law that most closely approximates
|
||||||
|
an absolute waiver of all civil liability in connection with the
|
||||||
|
Program, unless a warranty or assumption of liability accompanies a
|
||||||
|
copy of the Program in return for a fee.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
How to Apply These Terms to Your New Programs
|
||||||
|
|
||||||
|
If you develop a new program, and you want it to be of the greatest
|
||||||
|
possible use to the public, the best way to achieve this is to make it
|
||||||
|
free software which everyone can redistribute and change under these terms.
|
||||||
|
|
||||||
|
To do so, attach the following notices to the program. It is safest
|
||||||
|
to attach them to the start of each source file to most effectively
|
||||||
|
state the exclusion of warranty; and each file should have at least
|
||||||
|
the "copyright" line and a pointer to where the full notice is found.
|
||||||
|
|
||||||
|
pfor
|
||||||
|
Copyright (C) 2021 phd / dev
|
||||||
|
|
||||||
|
This program 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.
|
||||||
|
|
||||||
|
This program 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 <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
Also add information on how to contact you by electronic and paper mail.
|
||||||
|
|
||||||
|
If the program does terminal interaction, make it output a short
|
||||||
|
notice like this when it starts in an interactive mode:
|
||||||
|
|
||||||
|
<program> Copyright (C) 2021 phd / dev
|
||||||
|
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||||
|
This is free software, and you are welcome to redistribute it
|
||||||
|
under certain conditions; type `show c' for details.
|
||||||
|
|
||||||
|
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||||
|
parts of the General Public License. Of course, your program's commands
|
||||||
|
might be different; for a GUI interface, you would use an "about box".
|
||||||
|
|
||||||
|
You should also get your employer (if you work as a programmer) or school,
|
||||||
|
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||||
|
For more information on this, and how to apply and follow the GNU GPL, see
|
||||||
|
<https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
The GNU General Public License does not permit incorporating your program
|
||||||
|
into proprietary programs. If your program is a subroutine library, you
|
||||||
|
may consider it more useful to permit linking proprietary applications with
|
||||||
|
the library. If this is what you want to do, use the GNU Lesser General
|
||||||
|
Public License instead of this License. But first, please read
|
||||||
|
<https://www.gnu.org/licenses/why-not-lgpl.html>.
|
145
README.md
Normal file
145
README.md
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
# About
|
||||||
|
|
||||||
|
This is an active library, using C++ template metaprogramming, to do assisted parallelisation using algorithmic skeletons.
|
||||||
|
This work has been done for my Ph.D. thesis.
|
||||||
|
|
||||||
|
## Brief
|
||||||
|
|
||||||
|
It implements [algorithmic skeletons](https://en.wikipedia.org/wiki/Algorithmic_skeleton) to provide an abstraction
|
||||||
|
for developers to write parallel software.
|
||||||
|
It exposes:
|
||||||
|
- bones: atomic algorithmic structure to use to build skeletons;
|
||||||
|
- links: function signatures with placeholders to define data transfers between tasks;
|
||||||
|
- execution policies: a solution to select how tasks will be distributed.
|
||||||
|
|
||||||
|
After describing an algorithm, one can generate a functionoid that implements it and run it.
|
||||||
|
Using the links system in combination with the execution policy and overall structure knowledge,
|
||||||
|
the library provide a way to guarantee repeatability from one execution to another and even
|
||||||
|
for different number of allotted cores.
|
||||||
|
This is particularly useful for stochastic programs because of the use of pseudo-random numbers.
|
||||||
|
|
||||||
|
Main features:
|
||||||
|
- parallel implementation is hidden and separated from domain code;
|
||||||
|
- execution is controlled by the chosen execution policy;
|
||||||
|
- repeatability is automatically enabled.
|
||||||
|
|
||||||
|
## Related projects
|
||||||
|
|
||||||
|
- [ROSA](https://phd.pereda.fr/dev/rosa), an algorithmic skeletons collection for [OR](https://en.wikipedia.org/wiki/Operations_research) algorithms;
|
||||||
|
- [TMP](https://phd.pereda.fr/dev/tmp), template metaprogramming library used to implement this library.
|
||||||
|
|
||||||
|
See [ROSA](https://phd.pereda.fr/dev/rosa) for more complete and meaningful examples of algorithmic skeletons and
|
||||||
|
for the performances presented in the thesis.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
The code below defines `Gen`, a integral sequence generator, starting from the value given
|
||||||
|
when constructed.
|
||||||
|
```cpp
|
||||||
|
struct Gen {
|
||||||
|
int value;
|
||||||
|
int operator()() { return value++; }
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The next code defines `transform`, a function that modifies its input depending on some
|
||||||
|
pseudo-random number generated using the given generator.
|
||||||
|
```cpp
|
||||||
|
int transform(int v, std::mt19937& rng) {
|
||||||
|
std::uniform_int_distribution<int> d(-3, 3);
|
||||||
|
return v + d(rng);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The library exposes a raw interface to build skeletons from its structure and links definitions.
|
||||||
|
An example using the type `Gen`, the function `transform` and the standard function `std::min<int>` is shown below.
|
||||||
|
```cpp
|
||||||
|
using Structure =
|
||||||
|
S<FarmSel,
|
||||||
|
S<Serial, Gen, FN(transform)>,
|
||||||
|
Fn<int const&(&)(int const&, int const&), std::min<int>>
|
||||||
|
>;
|
||||||
|
|
||||||
|
using Links =
|
||||||
|
L<FarmSel, int(),
|
||||||
|
L<Serial, R<1>(),
|
||||||
|
int(),
|
||||||
|
int(R<0>, RNG)
|
||||||
|
>,
|
||||||
|
int(int, int)
|
||||||
|
>;
|
||||||
|
|
||||||
|
using Skeleton = BuildSkeletonT<Structure, Links>;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto algo = implement<StaticPool, Skeleton>();
|
||||||
|
algo.skeleton.n = 10;
|
||||||
|
algo.skeleton.task.task<0>() = Gen{5};
|
||||||
|
|
||||||
|
algo.executor.repeatability.upTo(8);
|
||||||
|
algo.executor.cores = 8;
|
||||||
|
|
||||||
|
auto r = algo();
|
||||||
|
std::printf("%d\n", r);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The same can be achieved using the EDSL provided by the library as below:
|
||||||
|
```cpp
|
||||||
|
int main() {
|
||||||
|
auto gen = alsk::edsl::makeOperand<int(), Gen>();
|
||||||
|
auto transform = alsk::edsl::makeOperand<int(alsk::arg::R<0>, alsk::arg::RNG), FN(::transform)>();
|
||||||
|
auto selectMin = alsk::edsl::makeOperand<int(int, int), Fn<int const&(&)(int const&, int const&), std::min<int>>>();
|
||||||
|
|
||||||
|
constexpr auto body = (10*alsk::edsl::link<alsk::arg::R<1>()>(gen, transform)) ->* selectMin;
|
||||||
|
auto algo = alsk::edsl::implement<alsk::exec::StaticPool>(body);
|
||||||
|
algo.skeleton.task.task<0>() = Gen{5};
|
||||||
|
|
||||||
|
algo.executor.repeatability.upTo(8);
|
||||||
|
algo.executor.cores = 8;
|
||||||
|
|
||||||
|
auto r = algo();
|
||||||
|
std::printf("%d\n", r);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Usually, the first interface is used to produce templates instead of types directly.
|
||||||
|
|
||||||
|
As the examples above show, the library can handle contextual arguments like random number generators to ensure
|
||||||
|
the repeatability of the execution (in this case, up to 8 cores as specified).
|
||||||
|
|
||||||
|
Additionally, the library optimises the number of PRNG to guarantee repeatability.
|
||||||
|
Without optimisation, the number of PRNG is linear because it must be equal to the number of tasks using a PRNG to run (red).
|
||||||
|
When the possible numbers of cores are from 1 to 64, it can be reduced (blue).
|
||||||
|
When the possible numbers of cores are powers of 2 up to 64, it becomes cyclic with a low maximum (orange).
|
||||||
|
|
||||||
|
<div align="center"><img src="https://phd.pereda.fr/assets/alsk/optimised_repeatability.png" width="500"></div>
|
||||||
|
|
||||||
|
## Related publications
|
||||||
|
|
||||||
|
- "Repeatability with Random Numbers Using Algorithmic Skeletons", ESM 2020 (https://hal.archives-ouvertes.fr/hal-02980472);
|
||||||
|
- "Modeling Algorithmic Skeletons for Automatic Parallelization Using Template Metaprogramming", HPCS 2019 (IEEE) [10.1109/HPCS48598.2019.9188128](https://doi.org/10.1109/HPCS48598.2019.9188128);
|
||||||
|
- "Processing Algorithmic Skeletons at Compile-Time", ROADEF 2020 (https://hal.archives-ouvertes.fr/hal-02573660);
|
||||||
|
- "Algorithmic Skeletons Using Template Metaprogramming", ICAST 2019;
|
||||||
|
- "Parallel Algorithmic Skeletons for Metaheuristics", ROADEF 2019 (https://hal.archives-ouvertes.fr/hal-02059533).
|
||||||
|
|
||||||
|
## Organisation
|
||||||
|
|
||||||
|
Main directories:
|
||||||
|
- `src/alsk`: the library sources;
|
||||||
|
- `examples`: some examples using the library.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
To produce the `Makefile` and build the project:
|
||||||
|
```bash
|
||||||
|
mkdir build
|
||||||
|
cd build
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||||
|
make
|
||||||
|
```
|
||||||
|
|
||||||
|
To run examples:
|
||||||
|
```bash
|
||||||
|
./build/examples/${example_name}
|
||||||
|
```
|
32
celero/bone/common.cpp
Normal file
32
celero/bone/common.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
namespace bench {
|
||||||
|
|
||||||
|
Data Task::operator()(int min, int max) const {
|
||||||
|
Data v(size);
|
||||||
|
std::generate_n(std::begin(v), size, [&, i=0]() mutable { return (++i)%(max-min+1) + min; });
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
|
||||||
|
Data taskD(Data const& data) {
|
||||||
|
Data out(data.size()+2);
|
||||||
|
std::copy(std::begin(data), std::end(data), std::begin(out)+2);
|
||||||
|
out[0] = std::accumulate(std::begin(data), std::end(data), Data::value_type{});
|
||||||
|
out[1] = out[0]&1? out[0]*out[0] : out[0];
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data const& select(Data const& a, Data const& b) {
|
||||||
|
Data::value_type sumA = std::accumulate(std::begin(a), std::end(a), Data::value_type{});
|
||||||
|
Data::value_type sumB = std::accumulate(std::begin(b), std::end(b), Data::value_type{});
|
||||||
|
|
||||||
|
return sumA < sumB? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
Data::value_type project(Data const& a, Data::value_type const& init) {
|
||||||
|
return std::accumulate(std::begin(a), std::end(a), init);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
57
celero/bone/common.h
Normal file
57
celero/bone/common.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#ifndef ALSK_CELERO_BONE_COMMON_H
|
||||||
|
#define ALSK_CELERO_BONE_COMMON_H
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <celero/Celero.h>
|
||||||
|
|
||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
namespace bench {
|
||||||
|
|
||||||
|
using Data = std::vector<int>;
|
||||||
|
using Value = Data::value_type;
|
||||||
|
|
||||||
|
struct Task {
|
||||||
|
std::size_t size;
|
||||||
|
Data operator()(int min, int max) const;
|
||||||
|
|
||||||
|
// TODO inline version: improve benchmarking for skeleton?
|
||||||
|
// Data operator()(int min, int max) const {
|
||||||
|
// Data v(size);
|
||||||
|
// std::generate_n(std::begin(v), size, [&, i=0]() mutable { return (++i)%(max-min+1) + min; });
|
||||||
|
// return v;
|
||||||
|
// };
|
||||||
|
};
|
||||||
|
constexpr auto eTask = alsk::edsl::makeOperand<Data(int, int), Task>();
|
||||||
|
constexpr auto eTaskStdFun = alsk::edsl::makeOperand<Data(int, int), std::function<Data(int, int)>>();
|
||||||
|
|
||||||
|
template<std::size_t count>
|
||||||
|
void taskV() {
|
||||||
|
std::vector<int> v(count);
|
||||||
|
std::generate_n(std::begin(v), count, [i=0]() mutable { return i++; });
|
||||||
|
for(std::size_t i = 0; i < count; ++i)
|
||||||
|
celero::DoNotOptimizeAway(std::accumulate(begin(v), end(v), i));
|
||||||
|
}
|
||||||
|
template<std::size_t count>
|
||||||
|
constexpr auto eTaskV = alsk::edsl::makeOperand<void(), FN(taskV<count>)>();
|
||||||
|
template<std::size_t count>
|
||||||
|
constexpr auto eTaskVStdFun = alsk::edsl::makeOperand<void(), std::function<void()>>();
|
||||||
|
|
||||||
|
Data taskD(Data const&);
|
||||||
|
constexpr auto eTaskD = alsk::edsl::makeOperand<Data(Data const&), FN(taskD)>();
|
||||||
|
constexpr auto eTaskDStdFun = alsk::edsl::makeOperand<Data(Data const&), std::function<Data(Data const&)>>();
|
||||||
|
|
||||||
|
Data const& select(Data const&, Data const&);
|
||||||
|
constexpr auto eSelect = alsk::edsl::makeOperand<Data(Data const&, Data const&), FN(select)>();
|
||||||
|
constexpr auto eSelectStdFun = alsk::edsl::makeOperand<Data(Data const&, Data const&), std::function<Data(Data const&, Data const&)>>();
|
||||||
|
|
||||||
|
Value project(Data const&, Value const&);
|
||||||
|
constexpr auto eProject = alsk::edsl::makeOperand<Value(Data const&, Value const&), FN(project)>();
|
||||||
|
constexpr auto eProjectStdFun = alsk::edsl::makeOperand<Value(Data const&, Value const&), std::function<Value(Data const&, Value const&)>>();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
34
celero/bone/farm.cpp
Normal file
34
celero/bone/farm.cpp
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
using namespace bench;
|
||||||
|
|
||||||
|
constexpr unsigned samples = 30, iterations = 10, cores = 4;
|
||||||
|
|
||||||
|
constexpr unsigned n = 64;
|
||||||
|
constexpr std::size_t vecSize = 1'000;
|
||||||
|
|
||||||
|
constexpr auto eFarm = n*eTaskV<vecSize>;
|
||||||
|
|
||||||
|
BASELINE(Farm, Handwritten, samples, iterations) {
|
||||||
|
for(unsigned i = 0; i < n; ++i) taskV<vecSize>();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(Farm, Skeleton, samples, iterations) {
|
||||||
|
auto farm = alsk::edsl::implement<alsk::exec::Sequential>(eFarm);
|
||||||
|
farm();
|
||||||
|
}
|
||||||
|
|
||||||
|
BASELINE(FarmPar, Handwritter, samples, iterations) {
|
||||||
|
#pragma omp parallel for num_threads(cores)
|
||||||
|
for(unsigned i = 0; i < n; ++i) taskV<vecSize>();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(FarmPar, Parallel, samples, iterations) {
|
||||||
|
auto farm = alsk::edsl::implement<alsk::exec::StaticThread>(eFarm);
|
||||||
|
farm.executor.cores = cores;
|
||||||
|
|
||||||
|
farm();
|
||||||
|
}
|
111
celero/bone/farmsel.cpp
Normal file
111
celero/bone/farmsel.cpp
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
using namespace bench;
|
||||||
|
using namespace alsk::edsl;
|
||||||
|
using namespace alsk::arg;
|
||||||
|
|
||||||
|
constexpr unsigned samples = 10, iterations = 100, cores = 4;
|
||||||
|
|
||||||
|
constexpr std::size_t vecSize = 10'000;
|
||||||
|
constexpr unsigned n = 128;
|
||||||
|
constexpr int minValue = -250, maxValue = +250;
|
||||||
|
|
||||||
|
decltype(auto) hwFarmSel(int min, int max) {
|
||||||
|
Task task{vecSize};
|
||||||
|
Data best{};
|
||||||
|
|
||||||
|
if(n)
|
||||||
|
best = task(min, max);
|
||||||
|
for(std::size_t i = 1; i < n; ++i) {
|
||||||
|
Data current = task(min, max);
|
||||||
|
best = select(current, best);
|
||||||
|
}
|
||||||
|
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
decltype(auto) hwFarmSelSk(int min, int max) {
|
||||||
|
Task task{vecSize};
|
||||||
|
Data best{};
|
||||||
|
|
||||||
|
std::vector<Data> bests(n);
|
||||||
|
|
||||||
|
for(std::size_t i = 0; i < n; ++i)
|
||||||
|
bests[i] = task(min, max);
|
||||||
|
|
||||||
|
best = std::move(bests[0]);
|
||||||
|
for(std::size_t i = 1; i < n; ++i)
|
||||||
|
best = select(std::move(bests[i-1]), std::move(best));
|
||||||
|
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
decltype(auto) hwFarmSelPar(int min, int max) {
|
||||||
|
Task task{vecSize};
|
||||||
|
Data best{};
|
||||||
|
|
||||||
|
std::vector<Data> bests(n);
|
||||||
|
|
||||||
|
#pragma omp parallel for num_threads(cores)
|
||||||
|
for(std::size_t i = 0; i < n; ++i)
|
||||||
|
bests[i] = task(min, max);
|
||||||
|
|
||||||
|
best = std::move(bests[0]);
|
||||||
|
for(std::size_t i = 1; i < n; ++i)
|
||||||
|
best = select(std::move(bests[i-1]), std::move(best));
|
||||||
|
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto eFarmSel = link<R<1>(int, int)>(n * link<Data(P<0>, P<1>)>(eTask)) ->* eSelect;
|
||||||
|
constexpr auto eFarmSelStdFun = link<R<1>(int, int)>(n * link<Data(P<0>, P<1>)>(eTaskStdFun)) ->* eSelectStdFun;
|
||||||
|
|
||||||
|
BASELINE(FarmSel, Handwritten, samples, iterations) {
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
hwFarmSel(minValue, maxValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(FarmSel, HandwrittenSk, samples, iterations) {
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
hwFarmSelSk(minValue, maxValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(FarmSel, Skeleton, samples, iterations) {
|
||||||
|
auto farmSel = alsk::edsl::implement<alsk::exec::Sequential>(eFarmSel);
|
||||||
|
farmSel.skeleton.task.size = vecSize;
|
||||||
|
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
farmSel(minValue, maxValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(FarmSel, SkeletonStdFunction, samples, iterations) {
|
||||||
|
auto farmSel = alsk::edsl::implement<alsk::exec::Sequential>(eFarmSelStdFun);
|
||||||
|
farmSel.skeleton.task = Task{vecSize};
|
||||||
|
farmSel.skeleton.select = bench::select;
|
||||||
|
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
farmSel(minValue, maxValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BASELINE(FarmSelPar, Handwritten, samples, iterations) {
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
hwFarmSelPar(minValue, maxValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(FarmSelPar, Skeleton, samples, iterations) {
|
||||||
|
auto farmSel = alsk::edsl::implement<alsk::exec::StaticThread>(eFarmSel);
|
||||||
|
farmSel.executor.cores = cores;
|
||||||
|
farmSel.skeleton.task.size = vecSize;
|
||||||
|
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
farmSel(minValue, maxValue)
|
||||||
|
);
|
||||||
|
}
|
50
celero/bone/itersel.cpp
Normal file
50
celero/bone/itersel.cpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
using namespace bench;
|
||||||
|
using namespace alsk::edsl;
|
||||||
|
using namespace alsk::arg;
|
||||||
|
|
||||||
|
constexpr unsigned samples = 50, iterations = 100;
|
||||||
|
constexpr unsigned n = 8192; // if too small => bad results
|
||||||
|
constexpr auto initVector = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
|
||||||
|
|
||||||
|
decltype(auto) hwIterSel(Data const& init) {
|
||||||
|
Data best = init;
|
||||||
|
|
||||||
|
for(std::size_t i = 0; i < n; ++i) {
|
||||||
|
Data current = taskD(best);
|
||||||
|
best = select(std::move(current), std::move(best));
|
||||||
|
}
|
||||||
|
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto eIterSel = &link<Data(Data const&)>(n * eTaskD) ->* eSelect;
|
||||||
|
constexpr auto eIterSelStdFun = &link<Data(Data const&)>(n * eTaskDStdFun) ->* eSelectStdFun;
|
||||||
|
|
||||||
|
BASELINE(IterSel, Handwritten, samples, iterations) {
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
hwIterSel(initVector)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(IterSel, Skeleton, samples, iterations) {
|
||||||
|
auto iterSel = alsk::edsl::implement<alsk::exec::Sequential>(eIterSel);
|
||||||
|
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
iterSel(initVector)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(IterSel, SkeletonStdFunction, samples, iterations) {
|
||||||
|
auto iterSel = alsk::edsl::implement<alsk::exec::Sequential>(eIterSelStdFun);
|
||||||
|
iterSel.skeleton.task = taskD;
|
||||||
|
iterSel.skeleton.select = bench::select;
|
||||||
|
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
iterSel(initVector)
|
||||||
|
);
|
||||||
|
}
|
32
celero/bone/loop.cpp
Normal file
32
celero/bone/loop.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
using namespace bench;
|
||||||
|
using namespace alsk::arg;
|
||||||
|
|
||||||
|
constexpr unsigned samples = 50, iterations = 100;
|
||||||
|
constexpr unsigned n = 100, vecSize = 100;
|
||||||
|
|
||||||
|
void hwLoop() {
|
||||||
|
for(std::size_t i = 0; i < n; ++i) taskV<vecSize>();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto eLoop = seq(n * eTaskV<vecSize>);
|
||||||
|
constexpr auto eLoopStdFun = seq(n * eTaskVStdFun<vecSize>);
|
||||||
|
|
||||||
|
BASELINE(Loop, Handwritten, samples, iterations) {
|
||||||
|
hwLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(Loop, Skeleton, samples, iterations) {
|
||||||
|
auto loop = alsk::edsl::implement<alsk::exec::Sequential>(eLoop);
|
||||||
|
loop();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(Loop, SkeletonStdFunction, samples, iterations) {
|
||||||
|
auto loop = alsk::edsl::implement<alsk::exec::Sequential>(eLoopStdFun);
|
||||||
|
loop.skeleton.task = taskV<vecSize>;
|
||||||
|
loop();
|
||||||
|
}
|
70
celero/bone/serial.cpp
Normal file
70
celero/bone/serial.cpp
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
using namespace bench;
|
||||||
|
using namespace alsk::arg;
|
||||||
|
using namespace alsk::edsl;
|
||||||
|
|
||||||
|
constexpr unsigned samples = 50, iterations = 100;
|
||||||
|
constexpr std::size_t vecSize = 100'000;
|
||||||
|
constexpr int minValue = -250, maxValue = +250;
|
||||||
|
|
||||||
|
decltype(auto) hwSerial(int min, int max) {
|
||||||
|
Task task0{vecSize}, task1{vecSize};
|
||||||
|
Data v0 = task0(min, max), v1 = task1(min, max);
|
||||||
|
|
||||||
|
Data const& v = select(v0, v1);
|
||||||
|
return project(v, rand());
|
||||||
|
}
|
||||||
|
|
||||||
|
decltype(auto) hwSerialBad(int min, int max) {
|
||||||
|
Task task0{vecSize}, task1{vecSize};
|
||||||
|
Data v2 = select(task0(min, max), task1(min, max));
|
||||||
|
return project(v2, rand());
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto eRand = makeOperand<int(), FN(rand)>();
|
||||||
|
constexpr auto lTask = link<Data(P<0>, P<1>)>(eTask);
|
||||||
|
constexpr auto eSerial = link<R<4>(int, int)>(lTask & lTask & link<Data(R<0>, R<1>)>(eSelect) & eRand & link<Value(R<2>, R<3>)>(eProject));
|
||||||
|
|
||||||
|
constexpr auto eRandStdFun = makeOperand<int(), std::function<int()>>();
|
||||||
|
constexpr auto lTaskStdFun = link<Data(P<0>, P<1>)>(eTaskStdFun);
|
||||||
|
constexpr auto eSerialStdFun = link<R<4>(int, int)>(
|
||||||
|
lTaskStdFun & lTaskStdFun & link<Data(R<0>, R<1>)>(eSelectStdFun) &
|
||||||
|
eRandStdFun & link<Value(R<2>, R<3>)>(eProjectStdFun));
|
||||||
|
|
||||||
|
BASELINE(Serial, Handwritten, samples, iterations) {
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
hwSerial(minValue, maxValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(Serial, HandwrittenBad, samples, iterations) {
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
hwSerialBad(minValue, maxValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(Serial, Skeleton, samples, iterations) {
|
||||||
|
auto serial = alsk::edsl::implement<alsk::exec::Sequential>(eSerial);
|
||||||
|
serial.skeleton.task<0>().size = vecSize;
|
||||||
|
serial.skeleton.task<1>().size = vecSize;
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
serial(minValue, maxValue)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(Serial, SkeletonStdFunction, samples, iterations) {
|
||||||
|
auto serial = alsk::edsl::implement<alsk::exec::Sequential>(eSerialStdFun);
|
||||||
|
serial.skeleton.task<0>() = Task{vecSize};
|
||||||
|
serial.skeleton.task<1>() = Task{vecSize};
|
||||||
|
serial.skeleton.task<2>() = bench::select;
|
||||||
|
serial.skeleton.task<3>() = rand;
|
||||||
|
serial.skeleton.task<4>() = project;
|
||||||
|
|
||||||
|
celero::DoNotOptimizeAway(
|
||||||
|
serial(minValue, maxValue)
|
||||||
|
);
|
||||||
|
}
|
21
celero/bone/while.cpp
Normal file
21
celero/bone/while.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
using namespace bench;
|
||||||
|
using namespace alsk::arg;
|
||||||
|
|
||||||
|
constexpr unsigned samples = 50, iterations = 100;
|
||||||
|
constexpr unsigned n = 100, vecSize = 100;
|
||||||
|
|
||||||
|
bool test(int& c) { return --c; }
|
||||||
|
|
||||||
|
void hwLoop(int& c) {
|
||||||
|
while(test(c)) taskV<vecSize>();
|
||||||
|
}
|
||||||
|
|
||||||
|
BASELINE(While, Handwritten, samples, iterations) {
|
||||||
|
int count = n;
|
||||||
|
hwLoop(count);
|
||||||
|
}
|
57
celero/executor/common.h
Normal file
57
celero/executor/common.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#ifndef ALSK_CELERO_EXECUTOR_COMMON_H
|
||||||
|
#define ALSK_CELERO_EXECUTOR_COMMON_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <celero/Celero.h>
|
||||||
|
|
||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
#include "../bone/common.h"
|
||||||
|
|
||||||
|
namespace bench {
|
||||||
|
|
||||||
|
constexpr auto buildExprFarm() {
|
||||||
|
using namespace alsk::arg;
|
||||||
|
using namespace alsk::edsl;
|
||||||
|
return 20 * eTaskV<1000>;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto exprFarm = buildExprFarm();
|
||||||
|
|
||||||
|
constexpr auto buildExprFarmSel() {
|
||||||
|
using namespace alsk::arg;
|
||||||
|
using namespace alsk::edsl;
|
||||||
|
return link<void(int, int)>(link<Data(P<0>, P<1>)>(eTask) & link<Data(R<0>)>((50 * link<Data(P<0>)>(eTaskD)) ->* eSelect));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto exprFarmSel = buildExprFarmSel();
|
||||||
|
|
||||||
|
constexpr auto buildExprTwo() {
|
||||||
|
using namespace alsk::arg;
|
||||||
|
using namespace alsk::edsl;
|
||||||
|
|
||||||
|
constexpr auto farmsel = link<Data(R<0>)>(1000 * link<Data(P<0>)>(eTaskD)) ->* eSelect;
|
||||||
|
constexpr auto serial = link<R<1>(P<0>, P<1>)>(link<Data(P<0>, P<1>)>(eTask) & farmsel);
|
||||||
|
return link<void(int, int)>(2 * serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto exprTwo = buildExprTwo();
|
||||||
|
|
||||||
|
constexpr auto buildExprTwoS() {
|
||||||
|
using namespace alsk::arg;
|
||||||
|
using namespace alsk::edsl;
|
||||||
|
|
||||||
|
constexpr auto farmsel = link<Data(Data const&)>(1000 * link<Data(P<0>)>(eTaskD)) ->* eSelect;
|
||||||
|
constexpr auto itersel = &link<Data(R<0>)>(2 * farmsel) ->* eSelect;
|
||||||
|
constexpr auto serial = link<R<1>(P<0>, P<1>)>(link<Data(P<0>, P<1>)>(eTask) & itersel);
|
||||||
|
constexpr auto loop = &link<void(P<0>, P<1>)>(2 * serial);
|
||||||
|
return link<void(int, int)>(2 * loop);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto exprTwoS = buildExprTwoS();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
52
celero/executor/farm.cpp
Normal file
52
celero/executor/farm.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
constexpr unsigned samples = 12, iterations = 10, cores = 4;
|
||||||
|
|
||||||
|
BASELINE(ExecFarm, Sequential, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::Sequential>(bench::exprFarm);
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarm, FirstLevelEqui, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::FirstLevelEqui>(bench::exprFarm);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarm, FirstLevelGreedy, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::FirstLevelGreedy>(bench::exprFarm);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarm, FirstLevelNoOpti, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::FirstLevelNoOpti>(bench::exprFarm);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarm, DynamicPool, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::DynamicPool>(bench::exprFarm);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarm, StaticPool, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::StaticPool>(bench::exprFarm);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarm, StaticPoolId, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::StaticPoolId>(bench::exprFarm);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f();
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarm, StaticThread, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::StaticThread>(bench::exprFarm);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f();
|
||||||
|
}
|
62
celero/executor/farmsel.cpp
Normal file
62
celero/executor/farmsel.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
constexpr unsigned samples = 12, iterations = 10, cores = 4;
|
||||||
|
constexpr std::size_t vecSize = 100'000;
|
||||||
|
constexpr int minValue = -250, maxValue = +250;
|
||||||
|
|
||||||
|
BASELINE(ExecFarmSel, Sequential, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::Sequential>(bench::exprFarmSel);
|
||||||
|
f.skeleton.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarmSel, FirstLevelEqui, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::FirstLevelEqui>(bench::exprFarmSel);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarmSel, FirstLevelGreedy, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::FirstLevelGreedy>(bench::exprFarmSel);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarmSel, FirstLevelNoOpti, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::FirstLevelNoOpti>(bench::exprFarmSel);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarmSel, DynamicPool, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::DynamicPool>(bench::exprFarmSel);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarmSel, StaticPool, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::StaticPool>(bench::exprFarmSel);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarmSel, StaticPoolId, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::StaticPoolId>(bench::exprFarmSel);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecFarmSel, StaticThread, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::StaticThread>(bench::exprFarmSel);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
3
celero/executor/sequential.cpp
Normal file
3
celero/executor/sequential.cpp
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
62
celero/executor/twolevels.cpp
Normal file
62
celero/executor/twolevels.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
constexpr unsigned samples = 12, iterations = 10, cores = 4;
|
||||||
|
constexpr std::size_t vecSize = 1000;
|
||||||
|
constexpr int minValue = -250, maxValue = +250;
|
||||||
|
|
||||||
|
BASELINE(ExecTwoLevels, Sequential, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::Sequential>(bench::exprTwo);
|
||||||
|
f.skeleton.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevels, FirstLevelEqui, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::FirstLevelEqui>(bench::exprTwo);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevels, FirstLevelGreedy, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::FirstLevelGreedy>(bench::exprTwo);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevels, FirstLevelNoOpti, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::FirstLevelNoOpti>(bench::exprTwo);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevels, DynamicPool, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::DynamicPool>(bench::exprTwo);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevels, StaticPool, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::StaticPool>(bench::exprTwo);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevels, StaticPoolId, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::StaticPoolId>(bench::exprTwo);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevels, StaticThread, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::StaticThread>(bench::exprTwo);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
62
celero/executor/twolevelshard.cpp
Normal file
62
celero/executor/twolevelshard.cpp
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
constexpr unsigned samples = 12, iterations = 10, cores = 4;
|
||||||
|
constexpr std::size_t vecSize = 1'000;
|
||||||
|
constexpr int minValue = -250, maxValue = +250;
|
||||||
|
|
||||||
|
BASELINE(ExecTwoLevelsHard, Sequential, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::Sequential>(bench::exprTwoS);
|
||||||
|
f.skeleton.task.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevelsHard, FirstLevelEqui, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::FirstLevelEqui>(bench::exprTwoS);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevelsHard, FirstLevelGreedy, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::FirstLevelGreedy>(bench::exprTwoS);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevelsHard, FirstLevelNoOpti, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::FirstLevelNoOpti>(bench::exprTwoS);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevelsHard, DynamicPool, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::DynamicPool>(bench::exprTwoS);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevelsHard, StaticPool, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::StaticPool>(bench::exprTwoS);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevelsHard, StaticPoolId, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::StaticPoolId>(bench::exprTwoS);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(ExecTwoLevelsHard, StaticThread, samples, iterations) {
|
||||||
|
auto f = alsk::edsl::implement<alsk::exec::StaticThread>(bench::exprTwoS);
|
||||||
|
f.executor.cores = cores;
|
||||||
|
f.skeleton.task.task.task<0>().size = vecSize;
|
||||||
|
f(minValue, maxValue);
|
||||||
|
}
|
40
celero/inc/udm.h
Normal file
40
celero/inc/udm.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef BENCH_INC_UDM_H
|
||||||
|
#define BENCH_INC_UDM_H
|
||||||
|
|
||||||
|
#include <celero/Celero.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
|
||||||
|
class GetRusageUDM: public celero::UserDefinedMeasurementTemplate<std::size_t> {
|
||||||
|
std::string getName() const override { return "time"; }
|
||||||
|
|
||||||
|
bool reportSize() const override { return false; }
|
||||||
|
// bool reportMean() const override { return false; }
|
||||||
|
bool reportVariance() const override { return false; }
|
||||||
|
bool reportStandardDeviation() const override { return false; }
|
||||||
|
bool reportSkewness() const override { return false; }
|
||||||
|
bool reportKurtosis() const override { return false; }
|
||||||
|
bool reportZScore() const override { return false; }
|
||||||
|
bool reportMin() const override { return false; }
|
||||||
|
bool reportMax() const override { return false; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class GetRusage {
|
||||||
|
int _who;
|
||||||
|
struct rusage _begin, _end;
|
||||||
|
int _iterations;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit GetRusage(int who = RUSAGE_SELF): _who{who} {}
|
||||||
|
void start(int iterations) { _iterations = iterations; getrusage(_who, &_begin); }
|
||||||
|
void stop() { getrusage(_who, &_end); }
|
||||||
|
|
||||||
|
std::size_t get() {
|
||||||
|
auto begin = _begin.ru_utime, end = _end.ru_utime;
|
||||||
|
auto totalUs = (end.tv_sec - begin.tv_sec) * 1e6 + (end.tv_usec - begin.tv_usec);
|
||||||
|
return totalUs/_iterations;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
2
celero/main.cpp
Normal file
2
celero/main.cpp
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
CELERO_MAIN
|
36
celero/thread.cpp
Normal file
36
celero/thread.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include <celero/Celero.h>
|
||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
constexpr unsigned samples = 20;
|
||||||
|
constexpr unsigned iterations = 500;
|
||||||
|
|
||||||
|
constexpr unsigned count = 1'000'000;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
unsigned r;
|
||||||
|
void *f(void * = nullptr) {
|
||||||
|
r = 0;
|
||||||
|
for(unsigned volatile i = 0; i < count; ++i) r += r;
|
||||||
|
return &r;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
BASELINE(Thread, None, samples, iterations) {
|
||||||
|
celero::DoNotOptimizeAway(f());
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(Thread, cthread, samples, iterations) {
|
||||||
|
void *r;
|
||||||
|
pthread_t thread;
|
||||||
|
pthread_create(&thread, NULL, f, NULL);
|
||||||
|
pthread_join(thread, &r);
|
||||||
|
celero::DoNotOptimizeAway(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK(Thread, stdthread, samples, iterations) {
|
||||||
|
std::thread thread{f, nullptr};
|
||||||
|
thread.join();
|
||||||
|
celero::DoNotOptimizeAway(thread);
|
||||||
|
}
|
28
examples/basic_edsl.cpp
Normal file
28
examples/basic_edsl.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#include <alsk/alsk.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
struct Gen {
|
||||||
|
int value;
|
||||||
|
int operator()() { return value++; }
|
||||||
|
};
|
||||||
|
|
||||||
|
int transform(int v, std::mt19937& rng) {
|
||||||
|
std::uniform_int_distribution<int> d(-3, 3);
|
||||||
|
return v + d(rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto gen = alsk::edsl::makeOperand<int(), Gen>();
|
||||||
|
auto transform = alsk::edsl::makeOperand<int(alsk::arg::R<0>, alsk::arg::RNG), FN(::transform)>();
|
||||||
|
auto selectMin = alsk::edsl::makeOperand<int(int, int), Fn<int const&(&)(int const&, int const&), std::min<int>>>();
|
||||||
|
|
||||||
|
constexpr auto body = (10*alsk::edsl::link<alsk::arg::R<1>()>(gen, transform)) ->* selectMin;
|
||||||
|
auto algo = alsk::edsl::implement<alsk::exec::StaticPool>(body);
|
||||||
|
algo.skeleton.task.task<0>() = Gen{5};
|
||||||
|
|
||||||
|
algo.executor.repeatability.upTo(8);
|
||||||
|
algo.executor.cores = 8;
|
||||||
|
|
||||||
|
auto r = algo();
|
||||||
|
std::printf("%d\n", r);
|
||||||
|
}
|
41
examples/basic_raw.cpp
Normal file
41
examples/basic_raw.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
struct Gen {
|
||||||
|
int value;
|
||||||
|
int operator()() { return value++; }
|
||||||
|
};
|
||||||
|
|
||||||
|
int transform(int v, std::mt19937& rng) {
|
||||||
|
std::uniform_int_distribution<int> d(-3, 3);
|
||||||
|
return v + d(rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* raw interface */
|
||||||
|
using Structure =
|
||||||
|
alsk::S<alsk::FarmSel,
|
||||||
|
alsk::S<alsk::Serial, Gen, FN(transform)>,
|
||||||
|
Fn<int const&(&)(int const&, int const&), std::min<int>>
|
||||||
|
>;
|
||||||
|
|
||||||
|
using Links =
|
||||||
|
alsk::L<alsk::FarmSel, int(),
|
||||||
|
alsk::L<alsk::Serial, alsk::arg::R<1>(),
|
||||||
|
int(),
|
||||||
|
int(alsk::arg::R<0>, alsk::arg::RNG)
|
||||||
|
>,
|
||||||
|
int(int, int)
|
||||||
|
>;
|
||||||
|
|
||||||
|
using Skeleton = alsk::BuildSkeletonT<Structure, Links>;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto algo = alsk::implement<alsk::exec::StaticPool, Skeleton>();
|
||||||
|
algo.skeleton.n = 10;
|
||||||
|
algo.skeleton.task.task<0>() = Gen{5};
|
||||||
|
|
||||||
|
algo.executor.repeatability.upTo(8);
|
||||||
|
algo.executor.cores = 8;
|
||||||
|
|
||||||
|
auto r = algo();
|
||||||
|
std::printf("%d\n", r);
|
||||||
|
}
|
32
examples/dynamicpool.cpp
Normal file
32
examples/dynamicpool.cpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
using namespace alsk::arg;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
alsk::exec::ExecutorState<alsk::exec::DynamicPool<void>> state;
|
||||||
|
|
||||||
|
state.config(4);
|
||||||
|
|
||||||
|
constexpr int n = 40;
|
||||||
|
std::array<std::future<void>, n> futures;
|
||||||
|
|
||||||
|
std::puts("begin");
|
||||||
|
|
||||||
|
for(int i = 0; i < n; ++i) {
|
||||||
|
futures[i] = state.run([i] { for(int x = 0; x < 20'000'000+5'000'000*i; ++x); });
|
||||||
|
}
|
||||||
|
|
||||||
|
std::puts("wait");
|
||||||
|
|
||||||
|
std::promise<int> p;
|
||||||
|
std::future<int> f = state.run([] { return 42; }, p);
|
||||||
|
|
||||||
|
std::printf("with value: %d\n", f.get());
|
||||||
|
|
||||||
|
for(int i = 0; i < n; ++i)
|
||||||
|
futures[i].wait();
|
||||||
|
|
||||||
|
std::puts("end");
|
||||||
|
}
|
52
examples/farmsel.cpp
Normal file
52
examples/farmsel.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
constexpr unsigned benchN = 32;
|
||||||
|
constexpr int benchMin = -250;
|
||||||
|
constexpr int benchMax = +250;
|
||||||
|
|
||||||
|
constexpr unsigned benchVSize = 1'000'000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Functions
|
||||||
|
*/
|
||||||
|
namespace bench {
|
||||||
|
|
||||||
|
using C = std::vector<int>;
|
||||||
|
|
||||||
|
struct Task {
|
||||||
|
std::size_t size;
|
||||||
|
|
||||||
|
auto operator()(int min, int max) {
|
||||||
|
C v(size);
|
||||||
|
std::generate_n(std::begin(v), size, [&, i=0]() mutable { return (++i)%(max-min+1) + min; });
|
||||||
|
return v;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
C select(C const& a, C const& b) {
|
||||||
|
C::value_type sumA = std::accumulate(std::begin(a), std::end(a), C::value_type{});
|
||||||
|
C::value_type sumB = std::accumulate(std::begin(b), std::end(b), C::value_type{});
|
||||||
|
|
||||||
|
return sumA < sumB? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace alsk::arg;
|
||||||
|
using tmp::Pack;
|
||||||
|
|
||||||
|
using SkelFarmSel = alsk::FarmSel<
|
||||||
|
R<1>(int, int),
|
||||||
|
Pack<bench::Task, bench::C(P<0>, P<1>)>,
|
||||||
|
Pack<decltype(&bench::select), bench::C(bench::C const&, bench::C const&)>
|
||||||
|
>;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto farmSel = alsk::implement<alsk::exec::Sequential, SkelFarmSel>();
|
||||||
|
farmSel.skeleton.task = bench::Task{benchVSize};
|
||||||
|
farmSel.skeleton.select = bench::select;
|
||||||
|
farmSel.skeleton.n = benchN;
|
||||||
|
|
||||||
|
auto volatile r = farmSel(benchMin, benchMax);
|
||||||
|
}
|
170
examples/repeatability.cpp
Normal file
170
examples/repeatability.cpp
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
#include <numeric>
|
||||||
|
#include <random>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
template<typename S>
|
||||||
|
using Executor = alsk::exec::StaticThread<S>;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using RNG = std::mt19937;
|
||||||
|
using namespace alsk;
|
||||||
|
|
||||||
|
int task(RNG& rng) {
|
||||||
|
std::uniform_int_distribution<int> dist(-100, 100);
|
||||||
|
|
||||||
|
int a = dist(rng);
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
int b = dist(rng);
|
||||||
|
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
int sel(int a, int b) { return a + b; }
|
||||||
|
|
||||||
|
constexpr auto oSel = alsk::edsl::link<int(int, int), FN(sel)>();
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
void testA0() {
|
||||||
|
constexpr unsigned n = 20;
|
||||||
|
|
||||||
|
auto farm = [] {
|
||||||
|
RNG rng;
|
||||||
|
|
||||||
|
std::array<int, n> ri;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::array<std::thread, n> ti;
|
||||||
|
for (unsigned i = 0; i < n; ++i)
|
||||||
|
ti[i] = std::thread{[&r = ri[i]](RNG& rng) { r = task(rng); },
|
||||||
|
std::ref(rng)};
|
||||||
|
for (unsigned i = 0; i < n; ++i)
|
||||||
|
ti[i].join();
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::accumulate(std::begin(ri), std::end(ri), 0, sel);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::printf("taskA0 [n=%u]\n", n);
|
||||||
|
for(unsigned i = 0; i < 8; ++i)
|
||||||
|
std::printf(" [x=%u] %5d\n", i, farm());
|
||||||
|
}
|
||||||
|
|
||||||
|
void testA1() {
|
||||||
|
auto eFarm = alsk::edsl::link<int(RNG&)>(
|
||||||
|
(20*alsk::edsl::link<int(arg::P<0>), FN(task)>()) ->* oSel
|
||||||
|
);
|
||||||
|
|
||||||
|
auto farm = alsk::edsl::implement<Executor>(eFarm);
|
||||||
|
|
||||||
|
std::printf("testA1 [n=%lu]\n", farm.skeleton.n);
|
||||||
|
for(unsigned k = 1; k <= 8; ++k) {
|
||||||
|
RNG rng{};
|
||||||
|
|
||||||
|
farm.executor.cores = k;
|
||||||
|
std::printf(" [k=%u] %5d\n", k, farm(rng));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testA2() {
|
||||||
|
auto eFarm = alsk::edsl::link<int()>(
|
||||||
|
(20*alsk::edsl::link<int(arg::RNG), FN(task)>()) ->* oSel
|
||||||
|
);
|
||||||
|
|
||||||
|
auto farm = alsk::edsl::implement<Executor>(eFarm);
|
||||||
|
farm.executor.repeatability.upTo(8);
|
||||||
|
|
||||||
|
std::printf("testA2 [n=%lu, r=%lu]\n", farm.skeleton.n, farm.state.context.maxId());
|
||||||
|
for(unsigned k = 1; k <= 8; ++k) {
|
||||||
|
farm.executor.cores = k;
|
||||||
|
std::printf(" [k=%u] %5d\n", k, farm());
|
||||||
|
farm.state.context.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testA3() {
|
||||||
|
constexpr auto oTask = alsk::edsl::link<int(arg::RNG), FN(task)>();
|
||||||
|
auto eFarm = alsk::edsl::link<int()>(
|
||||||
|
(11*alsk::edsl::link<arg::R<1>()>(oTask & oTask)) ->* oSel
|
||||||
|
);
|
||||||
|
|
||||||
|
auto farm = alsk::edsl::implement<Executor>(eFarm);
|
||||||
|
farm.executor.repeatability.upTo(8);
|
||||||
|
|
||||||
|
std::printf("testA3 [n=%lu, r=%lu]\n", farm.skeleton.n, farm.state.context.maxId());
|
||||||
|
for(unsigned k = 1; k <= 8; ++k) {
|
||||||
|
farm.executor.cores = k;
|
||||||
|
std::printf(" [k=%u] %5d\n", k, farm());
|
||||||
|
farm.state.context.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testB0() {
|
||||||
|
constexpr unsigned n0 = 10, n1 = 8;
|
||||||
|
|
||||||
|
auto farm = [] {
|
||||||
|
RNG rng;
|
||||||
|
|
||||||
|
std::array<int, n0> ri;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto localTask = [&rng] {
|
||||||
|
std::array<int, n1> rj;
|
||||||
|
|
||||||
|
std::array<std::thread, n1> tj;
|
||||||
|
for (unsigned j = 0; j < n1; ++j)
|
||||||
|
tj[j] = std::thread{[&r = rj[j]](RNG& rng) { r = task(rng); },
|
||||||
|
std::ref(rng)};
|
||||||
|
for (unsigned j = 0; j < n1; ++j)
|
||||||
|
tj[j].join();
|
||||||
|
|
||||||
|
return std::accumulate(std::begin(rj), std::end(rj), 0, sel);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::array<std::thread, n0> ti;
|
||||||
|
for (unsigned i = 0; i < n0; ++i)
|
||||||
|
ti[i] = std::thread{[&r = ri[i], &localTask] { r = localTask(); }};
|
||||||
|
for (unsigned i = 0; i < n0; ++i)
|
||||||
|
ti[i].join();
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::accumulate(std::begin(ri), std::end(ri), 0, sel);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::printf("taskB0 [n0=%u, n1=%u]\n", n0, n1);
|
||||||
|
for(unsigned i = 0; i < 4; ++i)
|
||||||
|
std::printf(" [x=%u] %5d\n", i, farm());
|
||||||
|
}
|
||||||
|
|
||||||
|
void testB1() {
|
||||||
|
auto eFarm = alsk::edsl::link<int()>(
|
||||||
|
(10*alsk::edsl::link<arg::R<1>()>(
|
||||||
|
alsk::edsl::link<int(arg::RNG), FN(task)>() &
|
||||||
|
(8*alsk::edsl::link<int(arg::RNG), FN(task)>()) ->* oSel
|
||||||
|
)) ->* oSel
|
||||||
|
);
|
||||||
|
|
||||||
|
auto farm = alsk::edsl::implement<Executor>(eFarm);
|
||||||
|
farm.executor.repeatability.upTo(8);
|
||||||
|
|
||||||
|
std::printf("testB1 [n0=%lu, n1=%lu, r=%lu]\n", farm.skeleton.n, farm.skeleton.task.task<1>().n, farm.state.context.maxId());
|
||||||
|
for(unsigned k = 1; k <= 8; ++k) {
|
||||||
|
farm.executor.cores = k;
|
||||||
|
std::printf(" [k=%u] %5d\n", k, farm());
|
||||||
|
farm.state.context.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
testA0();
|
||||||
|
testA1();
|
||||||
|
testA2();
|
||||||
|
testA3();
|
||||||
|
|
||||||
|
testB0();
|
||||||
|
testB1();
|
||||||
|
}
|
15
examples/serial.cpp
Normal file
15
examples/serial.cpp
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
using namespace alsk::arg;
|
||||||
|
|
||||||
|
using Skel = alsk::Serial<
|
||||||
|
R<2>(int, int, int),
|
||||||
|
tmp::Pack<std::plus<int>, int(P<0>, P<1>)>,
|
||||||
|
tmp::Pack<std::plus<int>, int(R<0>, P<2>)>,
|
||||||
|
tmp::Pack<std::multiplies<int>, int(R<0>, R<1>)>
|
||||||
|
>;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto task = alsk::implement<alsk::exec::Sequential, Skel>();
|
||||||
|
return task(4, 2, 3);
|
||||||
|
}
|
26
examples/serial_itersel.cpp
Normal file
26
examples/serial_itersel.cpp
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
int produce(int a, int b) {
|
||||||
|
return rand()%(a|b);
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace alsk;
|
||||||
|
using namespace alsk::arg;
|
||||||
|
|
||||||
|
constexpr auto add = edsl::link<int(P<0>, P<1>), std::plus<int>>();
|
||||||
|
constexpr auto mul = edsl::link<int(R<1>, P<2>), std::multiplies<int>>();
|
||||||
|
constexpr auto min = edsl::link<int(int, int), Fn<int const&(&)(int const&, int const&), std::min<int>>>();
|
||||||
|
constexpr auto prod = edsl::link<int(int, P<1>), FN(produce)>();
|
||||||
|
|
||||||
|
using Skel = decltype(getSkeleton(
|
||||||
|
edsl::link<R<2>(int, int, int)>(
|
||||||
|
add &
|
||||||
|
edsl::link<int(R<0>, P<1>)>(seq(3 * prod) ->* min) &
|
||||||
|
mul
|
||||||
|
)
|
||||||
|
));
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
auto task = alsk::implement<exec::Sequential, Skel>();
|
||||||
|
std::printf("%d\n", task(10, 20, 5));
|
||||||
|
}
|
105
examples/tests.cpp
Normal file
105
examples/tests.cpp
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#include <alsk/alsk.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <numeric>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace alsk::arg;
|
||||||
|
using namespace alsk::edsl;
|
||||||
|
|
||||||
|
void example0(int count) {
|
||||||
|
struct Do { int operator()(int x) { std::puts("Do"); return x+1; } };
|
||||||
|
struct Then { void operator()(int v) { std::printf("Then {%d}\n", v); } };
|
||||||
|
struct Done { int operator()(int x, int y) { std::puts("Done"); return x*y; } };
|
||||||
|
|
||||||
|
auto aDo = makeOperand<int(P<0>), Do>();
|
||||||
|
auto aThen = makeOperand<Then>();
|
||||||
|
auto aDone = makeOperand<Done>();
|
||||||
|
|
||||||
|
auto in = link<R<2>(int)>(
|
||||||
|
aDo &
|
||||||
|
link<void(R<0>)>(
|
||||||
|
4 * link<void(P<0>)>(aThen)
|
||||||
|
) &
|
||||||
|
link<int(P<0>, R<0>)>(aDone)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto a = link<void(int)>(count * link<R<2>(P<0>)>(in));
|
||||||
|
|
||||||
|
auto f = implement<alsk::exec::Sequential>(a);
|
||||||
|
f(7);
|
||||||
|
|
||||||
|
auto fIn = implement<alsk::exec::Sequential>(in);
|
||||||
|
std::printf("result: %d\n", fIn(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
void example1() {
|
||||||
|
// TODO? not really stateful here
|
||||||
|
struct Generate { int value; int operator()(int b) { return ++value+b; } }; auto generate = makeOperand<Generate>();
|
||||||
|
struct Transform0 { int operator()(int x) { return x+1; } }; auto transform0 = makeOperand<Transform0>();
|
||||||
|
struct Transform1 { int operator()(int x) { return x-2; } }; auto transform1 = makeOperand<Transform1>();
|
||||||
|
struct Produce { int operator()(int x, int y) { return x*y; } }; auto produce = makeOperand<Produce>();
|
||||||
|
struct Select {
|
||||||
|
int mod;
|
||||||
|
int operator()(int a, int b) { if(a%mod == b%mod) return a<b? a : b; return (a%mod > b%mod)? a : b; }
|
||||||
|
};
|
||||||
|
auto select = makeOperand<int(int, int), Select>();
|
||||||
|
|
||||||
|
auto innerTask = link<R<3>(int)>(
|
||||||
|
link<int(P<0>)>(generate) &
|
||||||
|
link<int(R<0>)>(transform0) &
|
||||||
|
link<int(R<0>)>(transform1) &
|
||||||
|
link<int(R<2>, R<1>)>(produce)
|
||||||
|
);
|
||||||
|
auto task = link<int(int)>(10 * link<R<3>(P<0>)>(innerTask)) ->* select;
|
||||||
|
|
||||||
|
auto f = implement<alsk::exec::StaticPool>(task);
|
||||||
|
f.skeleton.select.mod = 5;
|
||||||
|
|
||||||
|
std::printf("results: {");
|
||||||
|
for(int i = 4; i < 9; ++i) std::printf("%d, ", f(i));
|
||||||
|
std::puts("}");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::mutex m;
|
||||||
|
|
||||||
|
void use(unsigned int n) { unsigned long long volatile v{}; for(unsigned int i{}; i < n; ++i) for(unsigned int j{}; j < 500; ++j) ++v; }
|
||||||
|
void example2() {
|
||||||
|
struct Info { void operator()(std::size_t id) {
|
||||||
|
std::lock_guard<std::mutex> lg{m};
|
||||||
|
std::cerr << std::this_thread::get_id() << ' ' << id << std::endl;
|
||||||
|
} }; //auto info = makeOperand<void(CtxId), Info>();
|
||||||
|
struct Generate { int v; int operator()(std::mt19937& g) { return v+g(); } }; auto generate = makeOperand<Generate>();
|
||||||
|
struct Transform0 { int operator()(int x) { use(1000); return x+1; } }; auto transform0 = makeOperand<Transform0>();
|
||||||
|
struct Transform1 { int operator()(int x) { use(1000); return x-2; } }; auto transform1 = makeOperand<Transform1>();
|
||||||
|
struct Produce { int operator()(int x, int y) { return x*y; } }; auto produce = makeOperand<Produce>();
|
||||||
|
struct Select {
|
||||||
|
int mod;
|
||||||
|
int operator()(int a, int b) { if(a%mod == b%mod) return a<b? a : b; return (a%mod > b%mod)? a : b; }
|
||||||
|
};
|
||||||
|
auto select = makeOperand<int(int, int), Select>();
|
||||||
|
|
||||||
|
auto innerSeq = link<R<3>(int)>(
|
||||||
|
link<int(RNG)>(generate) &
|
||||||
|
link<int(R<0>)>(transform0) &
|
||||||
|
link<int(R<0>)>(transform1) &
|
||||||
|
link<int(R<2>, R<1>)>(produce)
|
||||||
|
);
|
||||||
|
auto innerTask0 = link<int(int)>(16 * link<R<3>(P<0>)>(innerSeq)) ->* select;
|
||||||
|
auto innerTask = &link<int(int)>(30 * link<int(int)>(innerTask0)) ->* select;
|
||||||
|
auto task = link<void(int)>(2 * link<int(P<0>)>(innerTask));
|
||||||
|
|
||||||
|
auto f = implement<alsk::exec::StaticThread>(task);
|
||||||
|
f.executor.cores = 4;
|
||||||
|
f.executor.repeatability.upTo(f.executor.cores);
|
||||||
|
f.skeleton.task.select.mod = 12;
|
||||||
|
f.skeleton.task.task.select.mod = 17;
|
||||||
|
|
||||||
|
for(int i = 4; i < 9; ++i) f(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char**) {
|
||||||
|
example0(argc);
|
||||||
|
example2();
|
||||||
|
}
|
17877
inc/catch.hpp
Normal file
17877
inc/catch.hpp
Normal file
File diff suppressed because it is too large
Load Diff
7
inc/tmp/algorithm.h
Normal file
7
inc/tmp/algorithm.h
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#ifndef TMP_ALGORITHM_H
|
||||||
|
#define TMP_ALGORITHM_H
|
||||||
|
|
||||||
|
#include "packalgo.h"
|
||||||
|
#include "treealgo.h"
|
||||||
|
|
||||||
|
#endif
|
17
inc/tmp/bind.h
Normal file
17
inc/tmp/bind.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include "packalgo.h"
|
||||||
|
|
||||||
|
namespace tmp {
|
||||||
|
|
||||||
|
template<template<typename...> class TT, typename C>
|
||||||
|
struct Bind2nd {
|
||||||
|
template<typename T>
|
||||||
|
using F1 = TT<T, C>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ValueType, template<typename, ValueType, typename...> class TT, ValueType V>
|
||||||
|
struct Bind2ndV {
|
||||||
|
template<typename T>
|
||||||
|
using F1 = TT<T, V>;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
78
inc/tmp/debug.h
Normal file
78
inc/tmp/debug.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#ifndef TMP_DEBUG_H
|
||||||
|
#define TMP_DEBUG_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file tmp/debug.h
|
||||||
|
* @brief compile-time typename to string resolution
|
||||||
|
*
|
||||||
|
* see: https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c
|
||||||
|
* see: https://stackoverflow.com/a/20170989
|
||||||
|
*
|
||||||
|
* Adapted, minimal C++ version supported is C++14
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include "stringview.h"
|
||||||
|
|
||||||
|
namespace tmp {
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
constexpr unsigned strlen(char const*str) {
|
||||||
|
unsigned l = 0;
|
||||||
|
while(*str++) ++l;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief compile-time typename to string resolution
|
||||||
|
*
|
||||||
|
* @param[in] T type to resolve
|
||||||
|
*
|
||||||
|
* @return constexpr string_view containing the typename
|
||||||
|
*
|
||||||
|
* TODO this function has not been tested for Windows
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
constexpr string_view typeName() {
|
||||||
|
#if defined(__clang__)
|
||||||
|
string_view p = __PRETTY_FUNCTION__;
|
||||||
|
constexpr auto leftDummy = detail::strlen("tmp::string_view tmp::typeName() [T = ");
|
||||||
|
constexpr auto rightDummy = detail::strlen("]");
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
string_view p = __PRETTY_FUNCTION__;
|
||||||
|
constexpr auto leftDummy = detail::strlen("constexpr tmp::string_view tmp::typeName() [with T = ");
|
||||||
|
constexpr auto rightDummy = detail::strlen("]");
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
string_view p = __FUNCSIG__;
|
||||||
|
constexpr auto leftDummy = detail::strlen("");
|
||||||
|
constexpr auto rightDummy = detail::strlen("");
|
||||||
|
#else
|
||||||
|
return {"[err: tmp::typeNameF is not implemented for your compiler]"};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__clang__) || defined(__GNUC__) || defined(__MSC_VER)
|
||||||
|
return string_view(p.data() + leftDummy, p.size() - leftDummy - rightDummy);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... As, typename... Ts>
|
||||||
|
std::string strTypes(Ts...) {
|
||||||
|
using Fold = int[];
|
||||||
|
std::string result;
|
||||||
|
static_cast<void>(Fold{(result += std::string{typeName<As>()} + ", ", 0)...});
|
||||||
|
static_cast<void>(Fold{(result += std::string{typeName<Ts>()} + ", ", 0)...});
|
||||||
|
if(result.empty()) return "";
|
||||||
|
else return result.substr(0, result.size()-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... As, typename... Ts>
|
||||||
|
void printTypes(Ts...) {
|
||||||
|
std::puts(strTypes<As..., Ts...>().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
167
inc/tmp/pack.h
Normal file
167
inc/tmp/pack.h
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
#ifndef TMP_PACK_H
|
||||||
|
#define TMP_PACK_H
|
||||||
|
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
namespace tmp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pack
|
||||||
|
*/
|
||||||
|
template<typename... Ts> struct Pack {
|
||||||
|
static constexpr Size size = sizeof...(Ts);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PackHead
|
||||||
|
*/
|
||||||
|
template<typename> struct PackHeadImpl;
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
struct PackHeadImpl<Pack<T, Ts...>> {
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using PackHead = typename PackHeadImpl<T>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PackTrail
|
||||||
|
*/
|
||||||
|
template<typename> struct PackTrailImpl;
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
struct PackTrailImpl<Pack<T, Ts...>> {
|
||||||
|
using type = Pack<Ts...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using PackTrail = typename PackTrailImpl<T>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PackGet
|
||||||
|
*/
|
||||||
|
template<typename, Size> struct PackGetImpl;
|
||||||
|
|
||||||
|
template<typename T, typename... Ts, Size I>
|
||||||
|
struct PackGetImpl<Pack<T, Ts...>, I> {
|
||||||
|
using type = typename PackGetImpl<Pack<Ts...>, I-1>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
struct PackGetImpl<Pack<T, Ts...>, 0> {
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, Size I>
|
||||||
|
using PackGet = typename PackGetImpl<T, I>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PackPushFront
|
||||||
|
*/
|
||||||
|
template<typename, typename> struct PackPushFrontImpl;
|
||||||
|
|
||||||
|
template<typename... Ts, typename T>
|
||||||
|
struct PackPushFrontImpl<Pack<Ts...>, T> {
|
||||||
|
using type = Pack<T, Ts...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename P, typename T>
|
||||||
|
using PackPushFront = typename PackPushFrontImpl<P, T>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PackPushBack
|
||||||
|
*/
|
||||||
|
template<typename, typename> struct PackPushBackImpl;
|
||||||
|
|
||||||
|
template<typename... Ts, typename T>
|
||||||
|
struct PackPushBackImpl<Pack<Ts...>, T> {
|
||||||
|
using type = Pack<Ts..., T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename P, typename T>
|
||||||
|
using PackPushBack = typename PackPushBackImpl<P, T>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PackCat
|
||||||
|
*/
|
||||||
|
template<typename...> struct PackCatImpl;
|
||||||
|
|
||||||
|
template<typename... Ts, typename... Us, typename... Packs>
|
||||||
|
struct PackCatImpl<Pack<Ts...>, Pack<Us...>, Packs...> {
|
||||||
|
using type = typename PackCatImpl<Pack<Ts..., Us...>, Packs...>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ts>
|
||||||
|
struct PackCatImpl<Pack<Ts...>> {
|
||||||
|
using type = Pack<Ts...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct PackCatImpl<> {
|
||||||
|
using type = Pack<>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Packs>
|
||||||
|
using PackCat = typename PackCatImpl<Packs...>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PackReverse
|
||||||
|
*/
|
||||||
|
template<typename> struct PackReverseImpl;
|
||||||
|
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
struct PackReverseImpl<Pack<T, Ts...>> {
|
||||||
|
using type = PackPushBack<typename PackReverseImpl<Pack<Ts...>>::type, T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct PackReverseImpl<Pack<>> {
|
||||||
|
using type = Pack<>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename P>
|
||||||
|
using PackReverse = typename PackReverseImpl<P>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PackReplace
|
||||||
|
*/
|
||||||
|
template<typename, typename, typename> struct PackReplaceImpl;
|
||||||
|
|
||||||
|
template<typename Before, typename After, typename Current, typename... Ts>
|
||||||
|
struct PackReplaceImpl<Pack<Current, Ts...>, Before, After> {
|
||||||
|
using type = PackCat<Pack<Current>, typename PackReplaceImpl<Pack<Ts...>, Before, After>::type>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Before, typename After, typename... Ts>
|
||||||
|
struct PackReplaceImpl<Pack<Before, Ts...>, Before, After> {
|
||||||
|
using type = PackCat<Pack<After>, typename PackReplaceImpl<Pack<Ts...>, Before, After>::type>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Before, typename After>
|
||||||
|
struct PackReplaceImpl<Pack<>, Before, After> {
|
||||||
|
using type = Pack<>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename P, typename Before, typename After>
|
||||||
|
using PackReplace = typename PackReplaceImpl<P, Before, After>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repack
|
||||||
|
*/
|
||||||
|
template<template<typename...> class, typename> struct RepackImpl;
|
||||||
|
template<template<typename...> class Out, template<typename...> class In, typename... Ts>
|
||||||
|
struct RepackImpl<Out, In<Ts...>> {
|
||||||
|
using type = Out<Ts...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<template<typename...> class Out, typename In>
|
||||||
|
using Repack = typename RepackImpl<Out, In>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AsPack
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
using AsPack = Repack<Pack, T>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
103
inc/tmp/packalgo.h
Normal file
103
inc/tmp/packalgo.h
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#ifndef TMP_PACKALGO_H
|
||||||
|
#define TMP_PACKALGO_H
|
||||||
|
|
||||||
|
#include "pack.h"
|
||||||
|
#include "traits.h"
|
||||||
|
|
||||||
|
namespace tmp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform
|
||||||
|
*/
|
||||||
|
template<typename, template<typename...> class> struct TransformImpl;
|
||||||
|
template<typename T, typename... Ts, template<typename...> class F>
|
||||||
|
struct TransformImpl<Pack<T, Ts...>, F> {
|
||||||
|
using type = PackPushFront<typename TransformImpl<Pack<Ts...>, F>::type, F<T>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<template<typename...> class F>
|
||||||
|
struct TransformImpl<Pack<>, F> {
|
||||||
|
using type = Pack<>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename P, template<typename...> class F>
|
||||||
|
using Transform = typename TransformImpl<P, F>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accumulate
|
||||||
|
*/
|
||||||
|
template<typename, template<typename, typename> class, typename> struct AccumulateImpl;
|
||||||
|
template<typename T, typename... Ts, template<typename, typename> class F, typename A>
|
||||||
|
struct AccumulateImpl<Pack<T, Ts...>, F, A> {
|
||||||
|
using type = typename AccumulateImpl<Pack<Ts...>, F, F<A, T>>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<template<typename, typename> class F, typename A>
|
||||||
|
struct AccumulateImpl<Pack<>, F, A> {
|
||||||
|
using type = A;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename P, template<typename, typename> class F, typename A>
|
||||||
|
using Accumulate = typename AccumulateImpl<P, F, A>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter
|
||||||
|
*/
|
||||||
|
template<typename, template<typename> class> struct FilterImpl;
|
||||||
|
template<typename T, typename... Ts, template<typename> class F>
|
||||||
|
struct FilterImpl<Pack<T, Ts...>, F> {
|
||||||
|
using next = typename FilterImpl<Pack<Ts...>, F>::type;
|
||||||
|
using type = std::conditional_t<F<T>::value, PackPushFront<next, T>, next>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<template<typename> class F>
|
||||||
|
struct FilterImpl<Pack<>, F> {
|
||||||
|
using type = Pack<>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename P, template<typename> class F>
|
||||||
|
using Filter = typename FilterImpl<P, F>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cut
|
||||||
|
*/
|
||||||
|
template<typename, typename, template<typename> class> struct CutImpl;
|
||||||
|
template<typename R, typename... Rs, typename... Ls, template<typename> class F>
|
||||||
|
struct CutImpl<Pack<Ls...>, Pack<R, Rs...>, F> {
|
||||||
|
using next = typename CutImpl<Pack<Ls..., R>, Pack<Rs...>, F>::type;
|
||||||
|
using type = std::conditional_t<F<R>::value, next, Pack<Pack<Ls...>, Pack<R, Rs...>>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename... Ls, template<typename> class F>
|
||||||
|
struct CutImpl<Pack<Ls...>, Pack<>, F> {
|
||||||
|
using type = Pack<Pack<Ls...>, Pack<>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<template<typename> class F>
|
||||||
|
struct CutImpl<Pack<>, Pack<>, F> {
|
||||||
|
using type = Pack<Pack<>, Pack<>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename P, template<typename> class F>
|
||||||
|
using Cut = typename CutImpl<Pack<>, P, F>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse
|
||||||
|
*/
|
||||||
|
template<typename> struct ReverseImpl;
|
||||||
|
template<typename T, typename... Ts>
|
||||||
|
struct ReverseImpl<Pack<T, Ts...>> {
|
||||||
|
using type = PackPushBack<typename ReverseImpl<Pack<Ts...>>::type, T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct ReverseImpl<Pack<>> {
|
||||||
|
using type = Pack<>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using Reverse = typename ReverseImpl<T>::type;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
52
inc/tmp/stringview.h
Normal file
52
inc/tmp/stringview.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#ifndef TMP_STRINGVIEW_H
|
||||||
|
#define TMP_STRINGVIEW_H
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file tmp/stringview.h"
|
||||||
|
* @brief partial implementation of C++17 std::string_view
|
||||||
|
*
|
||||||
|
* see: https://stackoverflow.com/questions/81870/is-it-possible-to-print-a-variables-type-in-standard-c
|
||||||
|
* see: https://stackoverflow.com/a/20170989
|
||||||
|
* string_view is from "static_string" class
|
||||||
|
* to be replaced by std::string_view as soon as possible
|
||||||
|
*
|
||||||
|
* Adapted, minimal C++ version supported now C++11
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace tmp {
|
||||||
|
|
||||||
|
class string_view {
|
||||||
|
char const* const _p;
|
||||||
|
std::size_t const _sz;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using const_iterator = char const*;
|
||||||
|
|
||||||
|
template<std::size_t N>
|
||||||
|
constexpr string_view(char const(&a)[N]) noexcept: _p(a), _sz(N-1) {}
|
||||||
|
constexpr string_view(char const*p, std::size_t N) noexcept: _p(p), _sz(N) {}
|
||||||
|
|
||||||
|
constexpr char const*data() const noexcept {return _p;}
|
||||||
|
constexpr std::size_t size() const noexcept {return _sz;}
|
||||||
|
|
||||||
|
constexpr const_iterator begin() const noexcept {return _p;}
|
||||||
|
constexpr const_iterator end() const noexcept {return _p + _sz;}
|
||||||
|
|
||||||
|
constexpr char operator[](std::size_t n) const {
|
||||||
|
return n < _sz? _p[n] : throw std::out_of_range("string_view");
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit operator std::string() const { return {_p, _sz}; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename Ostream>
|
||||||
|
inline Ostream&operator<<(Ostream &os, string_view const&s) {
|
||||||
|
os.write(s.data(), s.size());
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
78
inc/tmp/traits.h
Normal file
78
inc/tmp/traits.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
#ifndef TMP_TRAITS_H
|
||||||
|
#define TMP_TRAITS_H
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include "pack.h"
|
||||||
|
|
||||||
|
namespace tmp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* void_t
|
||||||
|
*
|
||||||
|
* as of C++17, use std::void_t instead
|
||||||
|
*/
|
||||||
|
template<typename...> using void_t = void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Void
|
||||||
|
*
|
||||||
|
* use to replace `void` when an instance is required
|
||||||
|
*/
|
||||||
|
struct Void {};
|
||||||
|
|
||||||
|
// TODO: remove it?
|
||||||
|
template<typename Ostream>
|
||||||
|
Ostream &operator<<(Ostream &os, Void const&) {
|
||||||
|
return os << "Void{}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReturnType
|
||||||
|
*/
|
||||||
|
template<typename> struct ReturnTypeImpl;
|
||||||
|
template<typename R, typename... Ts>
|
||||||
|
struct ReturnTypeImpl<R(Ts...)> {
|
||||||
|
using type = R;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using ReturnType = typename ReturnTypeImpl<T>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameters
|
||||||
|
*/
|
||||||
|
template<typename> struct ParametersImpl;
|
||||||
|
template<typename R, typename... Ps>
|
||||||
|
struct ParametersImpl<R(Ps...)> {
|
||||||
|
using type = Pack<Ps...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using Parameters = typename ParametersImpl<T>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* invoke_result
|
||||||
|
* as of C++17, use std::invoke_result instead
|
||||||
|
*/
|
||||||
|
template<typename F, typename... Args>
|
||||||
|
using invoke_result = std::result_of<F(Args...)>;
|
||||||
|
|
||||||
|
template<typename F, typename... Args>
|
||||||
|
using invoke_result_t = typename invoke_result<F, Args...>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FunctionCat
|
||||||
|
*/
|
||||||
|
template<typename, typename> struct FunctionCatImpl;
|
||||||
|
|
||||||
|
template<typename Ret, typename... Ts, typename... Us>
|
||||||
|
struct FunctionCatImpl<Ret(Ts...), Ret(Us...)> {
|
||||||
|
using type = Ret(Ts..., Us...);
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename F1, typename F2>
|
||||||
|
using FunctionCat = typename FunctionCatImpl<F1, F2>::type;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
159
inc/tmp/tree.h
Normal file
159
inc/tmp/tree.h
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
#ifndef TMP_TREE_H
|
||||||
|
#define TMP_TREE_H
|
||||||
|
|
||||||
|
#include "bind.h"
|
||||||
|
#include "pack.h"
|
||||||
|
#include "packalgo.h"
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
namespace tmp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tree
|
||||||
|
*/
|
||||||
|
template<typename...> struct Tree;
|
||||||
|
|
||||||
|
template<typename N, typename... Cs>
|
||||||
|
struct Tree<N, Cs...> {
|
||||||
|
using node = N;
|
||||||
|
using children = Pack<Cs...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct Tree<> {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TreeNode
|
||||||
|
*
|
||||||
|
* for use in flat tree representations
|
||||||
|
*/
|
||||||
|
enum class TreeNodeType { Branch, Leaf };
|
||||||
|
template<Depth D, typename T, TreeNodeType N>
|
||||||
|
struct TreeNode {
|
||||||
|
static constexpr Depth depth = D;
|
||||||
|
using type = T;
|
||||||
|
static constexpr TreeNodeType nodeType = N;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, TreeNodeType NT = TreeNodeType::Branch>
|
||||||
|
using TreeRoot = TreeNode<0, T, NT>;
|
||||||
|
|
||||||
|
template<Depth D, typename T>
|
||||||
|
using TreeBranch = TreeNode<D, T, TreeNodeType::Branch>;
|
||||||
|
|
||||||
|
template<Depth D, typename T>
|
||||||
|
using TreeLeaf = TreeNode<D, T, TreeNodeType::Leaf>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TreeIsLeaf
|
||||||
|
*/
|
||||||
|
template<typename> struct TreeIsLeafImpl: std::true_type {};
|
||||||
|
template<typename... Ts>
|
||||||
|
struct TreeIsLeafImpl<Tree<Ts...>>: std::false_type {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr bool TreeIsLeaf = TreeIsLeafImpl<T>::value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PackFromTreeNLR
|
||||||
|
*
|
||||||
|
* creates a pack from a tree using NLR
|
||||||
|
* tree traversal algorithm
|
||||||
|
*/
|
||||||
|
template<typename, Depth=0> struct PackFromTreeNLRImpl;
|
||||||
|
|
||||||
|
template<typename N, typename... LRs, Depth D>
|
||||||
|
struct PackFromTreeNLRImpl<Tree<N, LRs...>, D> {
|
||||||
|
using type = PackPushFront<
|
||||||
|
PackCat<typename PackFromTreeNLRImpl<LRs, D+1>::type...>,
|
||||||
|
TreeBranch<D, N>
|
||||||
|
>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename N, Depth D>
|
||||||
|
struct PackFromTreeNLRImpl<Tree<N>, D> {
|
||||||
|
using type = Pack<TreeLeaf<D, N>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<Depth D>
|
||||||
|
struct PackFromTreeNLRImpl<Tree<>, D> {
|
||||||
|
using type = Pack<>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, Depth D = 0>
|
||||||
|
using PackFromTreeNLR = typename PackFromTreeNLRImpl<T, D>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TreeFromPackNLR
|
||||||
|
*
|
||||||
|
* creates a tree from a pack
|
||||||
|
* reverses PackFromTreeNLR
|
||||||
|
*/
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<Depth D, typename T>
|
||||||
|
using PartialTree = Pack<std::integral_constant<decltype(D), D>, T>;
|
||||||
|
|
||||||
|
template<Depth LIM>
|
||||||
|
struct GreaterThan {
|
||||||
|
template<typename T, typename=void> struct CmpImpl;
|
||||||
|
template<Depth D, typename T>
|
||||||
|
struct CmpImpl<PartialTree<D, T>>: std::integral_constant<bool, (D > LIM)> {};
|
||||||
|
template<typename T>
|
||||||
|
struct CmpImpl<T>: std::true_type {};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using Cmp = CmpImpl<T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* property:
|
||||||
|
* one block either groups future blocks or
|
||||||
|
* has a bigger or equal depth than following
|
||||||
|
*
|
||||||
|
* property:
|
||||||
|
* the list of depths when building is always
|
||||||
|
* decreasing (quite easy to demonstrate)
|
||||||
|
*/
|
||||||
|
template<typename, typename=void> struct TreeFromPackNLRImpl;
|
||||||
|
|
||||||
|
template<Depth D, typename T, typename... Ns>
|
||||||
|
struct TreeFromPackNLRImpl<Pack<TreeBranch<D, T>, Ns...>, std::enable_if_t<D != 0>> {
|
||||||
|
using next = typename TreeFromPackNLRImpl<Pack<Ns...>>::subtree;
|
||||||
|
using cutd = Cut<next, detail::GreaterThan<D>::template Cmp>;
|
||||||
|
using children = Transform<PackGet<cutd, 0>, Bind2ndV<Size, PackGet, 1>::template F1>;
|
||||||
|
using siblings = PackGet<cutd, 1>;
|
||||||
|
using part = detail::PartialTree<D, Repack<Tree, PackPushFront<children, T>>>;
|
||||||
|
using subtree = PackPushFront<siblings, part>;
|
||||||
|
|
||||||
|
using type = Repack<Tree, subtree>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, TreeNodeType NT, typename... Ns>
|
||||||
|
struct TreeFromPackNLRImpl<Pack<TreeRoot<T, NT>, Ns...>> {
|
||||||
|
using next = typename TreeFromPackNLRImpl<Pack<Ns...>>::subtree;
|
||||||
|
using children = Transform<next, Bind2ndV<Size, PackGet, 1>::template F1>;
|
||||||
|
using subtree = PackPushFront<children, T>;
|
||||||
|
using type = Repack<Tree, subtree>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<Depth D, typename T, typename... Ns>
|
||||||
|
struct TreeFromPackNLRImpl<Pack<TreeLeaf<D, T>, Ns...>, std::enable_if_t<D != 0>> {
|
||||||
|
using part = detail::PartialTree<D, Tree<T>>;
|
||||||
|
using subtree = PackPushFront<typename TreeFromPackNLRImpl<Pack<Ns...>>::subtree, part>;
|
||||||
|
using type = Repack<Tree, subtree>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct TreeFromPackNLRImpl<Pack<>> {
|
||||||
|
using subtree = Pack<>;
|
||||||
|
using type = Repack<Tree, subtree>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename P>
|
||||||
|
using TreeFromPackNLR = typename TreeFromPackNLRImpl<P>::type;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
105
inc/tmp/treealgo.h
Normal file
105
inc/tmp/treealgo.h
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
#ifndef TMP_TREEALGO_H
|
||||||
|
#define TMP_TREEALGO_H
|
||||||
|
|
||||||
|
#include "packalgo.h"
|
||||||
|
#include "tree.h"
|
||||||
|
#include "utility.h"
|
||||||
|
|
||||||
|
namespace tmp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TreeTransform
|
||||||
|
*/
|
||||||
|
template<typename, template<typename> class> struct TreeTransformImpl;
|
||||||
|
template<typename T, typename... Ts, template<typename> class F>
|
||||||
|
struct TreeTransformImpl<Tree<T, Ts...>, F> {
|
||||||
|
using type = Tree<F<T>, typename TreeTransformImpl<Ts, F>::type...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<template<typename> class F>
|
||||||
|
struct TreeTransformImpl<Tree<>, F> {
|
||||||
|
using type = Tree<>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, template<typename> class F>
|
||||||
|
using TreeTransform = typename TreeTransformImpl<T, F>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TreeAccumulate
|
||||||
|
*/
|
||||||
|
template<typename, template<typename, typename...> class, typename> struct TreeAccumulateImpl;
|
||||||
|
template<typename T, typename... Ts, template<typename, typename...> class F, typename A>
|
||||||
|
struct TreeAccumulateImpl<Tree<T, Ts...>, F, A> {
|
||||||
|
using type = F<T, typename TreeAccumulateImpl<Ts, F, A>::type...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, template<typename, typename...> class F, typename A>
|
||||||
|
struct TreeAccumulateImpl<Tree<T>, F, A> {
|
||||||
|
using type = F<T, A>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<template<typename, typename...> class F, typename A>
|
||||||
|
struct TreeAccumulateImpl<Tree<>, F, A> {
|
||||||
|
using type = A;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T, template<typename, typename...> class F, typename A>
|
||||||
|
using TreeAccumulate = typename TreeAccumulateImpl<T, F, A>::type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TreeNLRAccumulate
|
||||||
|
*/
|
||||||
|
template<typename T, template<typename, typename> class F, typename A>
|
||||||
|
using TreeNLRAccumulate = Accumulate<Transform<PackFromTreeNLR<T>, GetType>, F, A>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TreeHeight
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
struct TreeHeightImpl {
|
||||||
|
template<typename, typename... Ts>
|
||||||
|
using CalcHeight = std::integral_constant<Depth, 1 + detail::Max<Ts::type::value...>>;
|
||||||
|
using CalcHeightDefault = std::integral_constant<Depth, -1>;
|
||||||
|
|
||||||
|
static constexpr Depth value = TreeAccumulate<T, CalcHeight, CalcHeightDefault>::value;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
constexpr Depth TreeHeight = TreeHeightImpl<T>::value;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TreeAllRTLPaths
|
||||||
|
*/
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct PackPushFronter {
|
||||||
|
template<typename P>
|
||||||
|
using Do = PackPushFront<P, T>;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename> struct TreeAllRTLPathsImpl;
|
||||||
|
|
||||||
|
template<typename N, typename... Cs>
|
||||||
|
struct TreeAllRTLPathsImpl<Tree<N, Cs...>> {
|
||||||
|
using type = PackCat<Transform<typename TreeAllRTLPathsImpl<Cs>::type, Bind2nd<PackPushFront, N>::template F1>...>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename N>
|
||||||
|
struct TreeAllRTLPathsImpl<Tree<N>> {
|
||||||
|
using type = Pack<Pack<N>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct TreeAllRTLPathsImpl<Tree<>> {
|
||||||
|
using type = Pack<>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using TreeAllRTLPaths = typename TreeAllRTLPathsImpl<T>::type;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
30
inc/tmp/utility.h
Normal file
30
inc/tmp/utility.h
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef TMP_UTILITY_H
|
||||||
|
#define TMP_UTILITY_H
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace tmp {
|
||||||
|
|
||||||
|
using Size = decltype(sizeof 0);
|
||||||
|
|
||||||
|
using Depth = signed long long int;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
using GetType = typename T::type;
|
||||||
|
|
||||||
|
namespace detail {
|
||||||
|
|
||||||
|
template<Depth V, Depth... Vs>
|
||||||
|
struct MaxImpl: std::integral_constant<Depth, (V>MaxImpl<Vs...>::value? V:MaxImpl<Vs...>::value)> {};
|
||||||
|
|
||||||
|
template<Depth V>
|
||||||
|
struct MaxImpl<V>: std::integral_constant<decltype(V), V> {};
|
||||||
|
|
||||||
|
template<Depth... Vs>
|
||||||
|
constexpr Depth Max = MaxImpl<Vs...>::value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
402
lib/celero/Archive.cpp
Normal file
402
lib/celero/Archive.cpp
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <assert.h>
|
||||||
|
#include <celero/Archive.h>
|
||||||
|
#include <celero/Benchmark.h>
|
||||||
|
#include <celero/FileReader.h>
|
||||||
|
#include <celero/PimplImpl.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Structure to assist with archiving data during runtime and to a file.
|
||||||
|
///
|
||||||
|
struct ArchiveEntry
|
||||||
|
{
|
||||||
|
ArchiveEntry()
|
||||||
|
: GroupName(),
|
||||||
|
RunName(),
|
||||||
|
ExperimentValue(0),
|
||||||
|
ExperimentValueScale(0),
|
||||||
|
FirstRanDate(0),
|
||||||
|
TotalSamplesCollected(0),
|
||||||
|
AverageBaseline(0),
|
||||||
|
MinBaseline(0),
|
||||||
|
MinBaseline_TimeSinceEpoch(0),
|
||||||
|
MinStats(),
|
||||||
|
MaxBaseline(0),
|
||||||
|
MaxBaseline_TimeSinceEpoch(0),
|
||||||
|
MaxStats(),
|
||||||
|
CurrentBaseline(0),
|
||||||
|
CurrentBaseline_TimeSinceEpoch(0),
|
||||||
|
CurrentStats(),
|
||||||
|
Failure(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteHeader(std::ostream& str)
|
||||||
|
{
|
||||||
|
str << "GroupName,RunName,Failure,ExperimentValue,ExperimentValueScale,FirstRanDate,TotalSamplesCollected,AverageBaseline,";
|
||||||
|
str << "MinBaseline,MinBaselineTimeSinceEpoch,";
|
||||||
|
str << "MinStatSize,MinStatMean,MinStatVariance,MinStatStandardDeviation,MinStatSkewness,MinStatKurtosis,";
|
||||||
|
str << "MinStatMin,MinStatMax,";
|
||||||
|
str << "MaxBaseline,MaxBaselineTimeSinceEpoch,";
|
||||||
|
str << "MaxStatSize,MaxStatMean,MaxStatVariance,MaxStatStandardDeviation,MaxStatSkewness,MaxStatKurtosis,";
|
||||||
|
str << "MaxStatMin,MaxStatMax,";
|
||||||
|
str << "CurrentBaseline,CurrentBaselineTimeSinceEpoch,";
|
||||||
|
str << "CurrentStatSize,CurrentStatMean,CurrentStatVariance,CurrentStatStandardDeviation,CurrentStatSkewness,CurrentStatKurtosis,";
|
||||||
|
str << "CurrentStatMin,CurrentStatMax" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Stat
|
||||||
|
{
|
||||||
|
Stat() : Size(0), Mean(0), Variance(0), StandardDeviation(0), Skewness(0), Kurtosis(0), Min(0), Max(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Stat& operator=(const celero::Statistics<int64_t>& s)
|
||||||
|
{
|
||||||
|
this->Size = s.getSize();
|
||||||
|
this->Mean = s.getMean();
|
||||||
|
this->Variance = s.getVariance();
|
||||||
|
this->StandardDeviation = s.getStandardDeviation();
|
||||||
|
this->Skewness = s.getSkewness();
|
||||||
|
this->Kurtosis = s.getKurtosis();
|
||||||
|
this->Min = s.getMin();
|
||||||
|
this->Max = s.getMax();
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Size;
|
||||||
|
double Mean;
|
||||||
|
double Variance;
|
||||||
|
double StandardDeviation;
|
||||||
|
double Skewness;
|
||||||
|
double Kurtosis;
|
||||||
|
uint64_t Min;
|
||||||
|
uint64_t Max;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string GroupName;
|
||||||
|
std::string RunName;
|
||||||
|
|
||||||
|
/// The data set size, if one was specified.
|
||||||
|
int64_t ExperimentValue;
|
||||||
|
double ExperimentValueScale;
|
||||||
|
|
||||||
|
uint64_t FirstRanDate;
|
||||||
|
uint32_t TotalSamplesCollected;
|
||||||
|
|
||||||
|
double AverageBaseline;
|
||||||
|
|
||||||
|
double MinBaseline;
|
||||||
|
uint64_t MinBaseline_TimeSinceEpoch;
|
||||||
|
Stat MinStats;
|
||||||
|
|
||||||
|
double MaxBaseline;
|
||||||
|
uint64_t MaxBaseline_TimeSinceEpoch;
|
||||||
|
Stat MaxStats;
|
||||||
|
|
||||||
|
double CurrentBaseline;
|
||||||
|
uint64_t CurrentBaseline_TimeSinceEpoch;
|
||||||
|
Stat CurrentStats;
|
||||||
|
|
||||||
|
bool Failure;
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Overload operator<< to allow for easy output of result data to a human-readable text file.
|
||||||
|
///
|
||||||
|
std::ostream& operator<<(std::ostream& str, ArchiveEntry::Stat const& data)
|
||||||
|
{
|
||||||
|
str << data.Size << ",";
|
||||||
|
str << data.Mean << ",";
|
||||||
|
str << data.Variance << ",";
|
||||||
|
str << data.StandardDeviation << ",";
|
||||||
|
str << data.Skewness << ",";
|
||||||
|
str << data.Kurtosis << ",";
|
||||||
|
str << data.Min << ",";
|
||||||
|
str << data.Max;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Overload operator<< to allow for easy output of result data to a human-readable text file.
|
||||||
|
///
|
||||||
|
std::ostream& operator<<(std::ostream& str, ArchiveEntry const& data)
|
||||||
|
{
|
||||||
|
str << data.GroupName << ",";
|
||||||
|
str << data.RunName << ",";
|
||||||
|
str << data.Failure << ",";
|
||||||
|
str << data.ExperimentValue << ",";
|
||||||
|
str << data.ExperimentValueScale << ",";
|
||||||
|
str << data.FirstRanDate << ",";
|
||||||
|
str << data.TotalSamplesCollected << ",";
|
||||||
|
str << data.AverageBaseline << ",";
|
||||||
|
str << data.MinBaseline << ",";
|
||||||
|
str << data.MinBaseline_TimeSinceEpoch << ",";
|
||||||
|
str << data.MinStats << ",";
|
||||||
|
str << data.MaxBaseline << ",";
|
||||||
|
str << data.MaxBaseline_TimeSinceEpoch << ",";
|
||||||
|
str << data.MaxStats << ",";
|
||||||
|
str << data.CurrentBaseline << ",";
|
||||||
|
str << data.CurrentBaseline_TimeSinceEpoch << ",";
|
||||||
|
str << data.CurrentStats << std::endl;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Overload operator>> to allow for easy input of result data from a text file.
|
||||||
|
///
|
||||||
|
std::istream& operator>>(std::istream& str, ArchiveEntry::Stat& data)
|
||||||
|
{
|
||||||
|
// Use FieldReader to classify commas as whitespace.
|
||||||
|
str.imbue(std::locale(std::locale(), new celero::FieldReader));
|
||||||
|
|
||||||
|
str >> data.Size;
|
||||||
|
str >> data.Mean;
|
||||||
|
str >> data.Variance;
|
||||||
|
str >> data.StandardDeviation;
|
||||||
|
str >> data.Skewness;
|
||||||
|
str >> data.Kurtosis;
|
||||||
|
str >> data.Min;
|
||||||
|
str >> data.Max;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Overload operator>> to allow for easy input of result data from a text file.
|
||||||
|
///
|
||||||
|
std::istream& operator>>(std::istream& str, ArchiveEntry& data)
|
||||||
|
{
|
||||||
|
// Use FieldReader to classify commas as whitespace.
|
||||||
|
str.imbue(std::locale(std::locale(), new celero::FieldReader));
|
||||||
|
|
||||||
|
str >> data.GroupName;
|
||||||
|
str >> data.RunName;
|
||||||
|
str >> data.Failure;
|
||||||
|
str >> data.ExperimentValue;
|
||||||
|
str >> data.ExperimentValueScale;
|
||||||
|
str >> data.FirstRanDate;
|
||||||
|
str >> data.TotalSamplesCollected;
|
||||||
|
str >> data.AverageBaseline;
|
||||||
|
str >> data.MinBaseline;
|
||||||
|
str >> data.MinBaseline_TimeSinceEpoch;
|
||||||
|
str >> data.MinStats;
|
||||||
|
str >> data.MaxBaseline;
|
||||||
|
str >> data.MaxBaseline_TimeSinceEpoch;
|
||||||
|
str >> data.MaxStats;
|
||||||
|
str >> data.CurrentBaseline;
|
||||||
|
str >> data.CurrentBaseline_TimeSinceEpoch;
|
||||||
|
str >> data.CurrentStats;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \class Impl
|
||||||
|
///
|
||||||
|
class celero::Archive::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl() : results(), fileName()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return milliseconds since epoch.
|
||||||
|
uint64_t now() const
|
||||||
|
{
|
||||||
|
return static_cast<uint64_t>(
|
||||||
|
std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
|
||||||
|
}
|
||||||
|
|
||||||
|
void readExistingResults()
|
||||||
|
{
|
||||||
|
// Read in existing results?
|
||||||
|
std::ifstream is;
|
||||||
|
is.open(this->fileName, std::fstream::in);
|
||||||
|
|
||||||
|
if((is.is_open() == true) && (is.good() == true) && (is.fail() == false))
|
||||||
|
{
|
||||||
|
// Throw away the header.
|
||||||
|
is.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
|
||||||
|
|
||||||
|
// Read in existing results.
|
||||||
|
while((is.eof() == false) && (is.tellg() >= 0))
|
||||||
|
{
|
||||||
|
ArchiveEntry r;
|
||||||
|
is >> r;
|
||||||
|
|
||||||
|
if(r.GroupName.empty() == false)
|
||||||
|
{
|
||||||
|
this->results.push_back(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the file for reading.
|
||||||
|
is.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ArchiveEntry> results;
|
||||||
|
std::string fileName;
|
||||||
|
};
|
||||||
|
|
||||||
|
Archive::Archive() : pimpl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Archive::~Archive()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Archive& Archive::Instance()
|
||||||
|
{
|
||||||
|
static Archive singleton;
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Archive::setFileName(const std::string& x)
|
||||||
|
{
|
||||||
|
if(x.empty() == false)
|
||||||
|
{
|
||||||
|
this->pimpl->fileName = x;
|
||||||
|
this->pimpl->readExistingResults();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Archive::add(std::shared_ptr<celero::ExperimentResult> x)
|
||||||
|
{
|
||||||
|
if(x == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto found = std::find_if(std::begin(this->pimpl->results), std::end(this->pimpl->results), [x](const ArchiveEntry& r) -> bool {
|
||||||
|
return (r.GroupName == x->getExperiment()->getBenchmark()->getName()) && (r.RunName == x->getExperiment()->getName())
|
||||||
|
&& (r.ExperimentValue == x->getProblemSpaceValue());
|
||||||
|
});
|
||||||
|
|
||||||
|
if(found != std::end(this->pimpl->results))
|
||||||
|
{
|
||||||
|
if(x->getFailure() == true)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
found->CurrentBaseline = x->getBaselineMeasurement();
|
||||||
|
found->CurrentBaseline_TimeSinceEpoch = this->pimpl->now();
|
||||||
|
found->CurrentStats = *x->getTimeStatistics();
|
||||||
|
|
||||||
|
if(found->Failure || found->CurrentBaseline <= found->MinBaseline)
|
||||||
|
{
|
||||||
|
found->MinBaseline = found->CurrentBaseline;
|
||||||
|
found->MinBaseline_TimeSinceEpoch = found->CurrentBaseline_TimeSinceEpoch;
|
||||||
|
found->MinStats = found->CurrentStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(found->Failure || found->CurrentBaseline >= found->MaxBaseline)
|
||||||
|
{
|
||||||
|
found->MaxBaseline = found->CurrentBaseline;
|
||||||
|
found->MaxBaseline_TimeSinceEpoch = found->CurrentBaseline_TimeSinceEpoch;
|
||||||
|
found->MaxStats = found->CurrentStats;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is not good IEEE math.
|
||||||
|
if(found->Failure == false)
|
||||||
|
{
|
||||||
|
found->AverageBaseline =
|
||||||
|
((found->AverageBaseline * found->TotalSamplesCollected) + found->CurrentBaseline) / (found->TotalSamplesCollected + 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
found->AverageBaseline = found->CurrentBaseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
found->TotalSamplesCollected++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ArchiveEntry r;
|
||||||
|
|
||||||
|
const auto experiment = x->getExperiment();
|
||||||
|
|
||||||
|
if(experiment != nullptr)
|
||||||
|
{
|
||||||
|
r.GroupName = experiment->getBenchmark()->getName();
|
||||||
|
r.RunName = experiment->getName();
|
||||||
|
r.Failure = x->getFailure();
|
||||||
|
r.FirstRanDate = this->pimpl->now();
|
||||||
|
r.AverageBaseline = x->getBaselineMeasurement();
|
||||||
|
r.ExperimentValue = x->getProblemSpaceValue();
|
||||||
|
r.ExperimentValueScale = x->getProblemSpaceValueScale();
|
||||||
|
r.TotalSamplesCollected = x->getFailure() ? 0 : 1;
|
||||||
|
|
||||||
|
r.CurrentBaseline = x->getBaselineMeasurement();
|
||||||
|
r.CurrentBaseline_TimeSinceEpoch = r.FirstRanDate;
|
||||||
|
r.CurrentStats = *x->getTimeStatistics();
|
||||||
|
|
||||||
|
r.MaxBaseline = x->getBaselineMeasurement();
|
||||||
|
r.MaxBaseline_TimeSinceEpoch = r.FirstRanDate;
|
||||||
|
r.MaxStats = *x->getTimeStatistics();
|
||||||
|
|
||||||
|
r.MinBaseline = x->getBaselineMeasurement();
|
||||||
|
r.MinBaseline_TimeSinceEpoch = r.FirstRanDate;
|
||||||
|
r.MinStats = *x->getTimeStatistics();
|
||||||
|
|
||||||
|
this->pimpl->results.push_back(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Archive::save()
|
||||||
|
{
|
||||||
|
if(this->pimpl->fileName.empty() == false)
|
||||||
|
{
|
||||||
|
// Get ready to write out new results.
|
||||||
|
// We will write all known results every time, replacing file contents.
|
||||||
|
std::ofstream os;
|
||||||
|
os.open(this->pimpl->fileName.c_str(), std::fstream::out);
|
||||||
|
|
||||||
|
if(os.is_open() == true)
|
||||||
|
{
|
||||||
|
ArchiveEntry::WriteHeader(os);
|
||||||
|
|
||||||
|
for(auto& i : this->pimpl->results)
|
||||||
|
{
|
||||||
|
os << i;
|
||||||
|
}
|
||||||
|
|
||||||
|
os.flush();
|
||||||
|
os.close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Celero: Could not open result output file: \"" << this->pimpl->fileName << "\"" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
100
lib/celero/Archive.h
Normal file
100
lib/celero/Archive.h
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
#ifndef H_CELERO_ARCHIVE_H
|
||||||
|
#define H_CELERO_ARCHIVE_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Experiment.h>
|
||||||
|
#include <celero/ExperimentResult.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \class Archive
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
class CELERO_EXPORT Archive
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Singleton
|
||||||
|
///
|
||||||
|
static Archive& Instance();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Specify a file name for a results output file.
|
||||||
|
///
|
||||||
|
/// \param x The name of the output file in which to store Celero's results.
|
||||||
|
///
|
||||||
|
void setFileName(const std::string& x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Adds or updates a result which will be saved to a results archive file.
|
||||||
|
///
|
||||||
|
/// This should re-save on every new result so that the output can be monitored externally.
|
||||||
|
///
|
||||||
|
void add(std::shared_ptr<celero::ExperimentResult> x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Saves all current results to a results archive file.
|
||||||
|
///
|
||||||
|
/// Will overwrite all existing data and refresh with new data.
|
||||||
|
///
|
||||||
|
void save();
|
||||||
|
|
||||||
|
private:
|
||||||
|
///
|
||||||
|
/// Default Constructor
|
||||||
|
///
|
||||||
|
Archive();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Non-copyable.
|
||||||
|
/// Visual studio 2012 does not support "delete" here.
|
||||||
|
///
|
||||||
|
Archive(Archive&) = delete;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Default Destructor
|
||||||
|
///
|
||||||
|
~Archive();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Non-assignable.
|
||||||
|
/// Visual studio 2012 does not support "delete" here.
|
||||||
|
///
|
||||||
|
Archive& operator=(const Archive&)
|
||||||
|
{
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
class Impl;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
Pimpl<Impl> pimpl;
|
||||||
|
};
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
124
lib/celero/Benchmark.cpp
Normal file
124
lib/celero/Benchmark.cpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Benchmark.h>
|
||||||
|
#include <celero/PimplImpl.h>
|
||||||
|
#include <celero/Utilities.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
class Benchmark::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl() : stats(), name(), baseline(), experiments()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Impl(const std::string& x) : stats(), name(x), baseline(), experiments()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Impl(const Benchmark& other) : stats(), name(other.pimpl->name), baseline(), experiments()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy(const Benchmark& other)
|
||||||
|
{
|
||||||
|
stats = other.pimpl->stats;
|
||||||
|
name = other.pimpl->name;
|
||||||
|
baseline = other.pimpl->baseline;
|
||||||
|
experiments = other.pimpl->experiments;
|
||||||
|
}
|
||||||
|
|
||||||
|
Statistics<int64_t> stats;
|
||||||
|
|
||||||
|
/// Group name
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
std::shared_ptr<Experiment> baseline;
|
||||||
|
std::vector<std::shared_ptr<Experiment>> experiments;
|
||||||
|
};
|
||||||
|
|
||||||
|
Benchmark::Benchmark() : pimpl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark::Benchmark(const std::string& name) : pimpl(name)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark::Benchmark(const Benchmark& other) : pimpl(other)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark::~Benchmark()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Benchmark& Benchmark::operator=(const Benchmark& other)
|
||||||
|
{
|
||||||
|
if(&other != this)
|
||||||
|
{
|
||||||
|
this->pimpl->copy(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Benchmark::getName() const
|
||||||
|
{
|
||||||
|
return this->pimpl->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Benchmark::setBaseline(std::shared_ptr<Experiment> x)
|
||||||
|
{
|
||||||
|
this->pimpl->baseline = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Experiment> Benchmark::getBaseline() const
|
||||||
|
{
|
||||||
|
return this->pimpl->baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Benchmark::addExperiment(std::shared_ptr<Experiment> x)
|
||||||
|
{
|
||||||
|
this->pimpl->experiments.push_back(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Experiment> Benchmark::getExperiment(size_t x)
|
||||||
|
{
|
||||||
|
if(x < this->pimpl->experiments.size())
|
||||||
|
{
|
||||||
|
return this->pimpl->experiments[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Experiment> Benchmark::getExperiment(const std::string& x)
|
||||||
|
{
|
||||||
|
return *std::find_if(std::begin(this->pimpl->experiments), std::end(this->pimpl->experiments),
|
||||||
|
[x](decltype(*std::begin(this->pimpl->experiments)) i) -> bool { return (i->getName() == x); });
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Benchmark::getExperimentSize() const
|
||||||
|
{
|
||||||
|
return this->pimpl->experiments.size();
|
||||||
|
}
|
112
lib/celero/Benchmark.h
Normal file
112
lib/celero/Benchmark.h
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#ifndef H_CELERO_BENCHMARK_H
|
||||||
|
#define H_CELERO_BENCHMARK_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Experiment.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \class Benchmark
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
class CELERO_EXPORT Benchmark
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// \brief Default constructor
|
||||||
|
///
|
||||||
|
Benchmark();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Overloaded constructor.
|
||||||
|
///
|
||||||
|
/// \param name Name of the test group.
|
||||||
|
///
|
||||||
|
explicit Benchmark(const std::string& name);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
explicit Benchmark(const Benchmark& other);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Default destructor.
|
||||||
|
///
|
||||||
|
~Benchmark();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Assignment Operator
|
||||||
|
///
|
||||||
|
Benchmark& operator=(const Benchmark& other);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The name to group all experiment under.
|
||||||
|
///
|
||||||
|
std::string getName() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
void setBaseline(std::shared_ptr<Experiment> x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets the baseline case associated this benchmark.
|
||||||
|
///
|
||||||
|
std::shared_ptr<Experiment> getBaseline() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
void addExperiment(std::shared_ptr<Experiment> x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets the test case associated with the given experiment index.
|
||||||
|
///
|
||||||
|
std::shared_ptr<Experiment> getExperiment(size_t experimentIndex);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets the test case associated with the given experiment name.
|
||||||
|
///
|
||||||
|
std::shared_ptr<Experiment> getExperiment(const std::string& experimentName);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns the total number of experiments per benchmark.
|
||||||
|
///
|
||||||
|
size_t getExperimentSize() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
class Impl;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
Pimpl<Impl> pimpl;
|
||||||
|
};
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
52
lib/celero/Callbacks.cpp
Normal file
52
lib/celero/Callbacks.cpp
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Callbacks.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
std::vector<std::function<void(std::shared_ptr<Experiment>)>> ExperimentFunctions;
|
||||||
|
std::vector<std::function<void(std::shared_ptr<celero::ExperimentResult>)>> ExperimentResultFunctions;
|
||||||
|
|
||||||
|
void celero::impl::ExperimentComplete(std::shared_ptr<Experiment> x)
|
||||||
|
{
|
||||||
|
for(auto& i : ExperimentFunctions)
|
||||||
|
{
|
||||||
|
i(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void celero::impl::ExperimentResultComplete(std::shared_ptr<celero::ExperimentResult> x)
|
||||||
|
{
|
||||||
|
for(auto& i : ExperimentResultFunctions)
|
||||||
|
{
|
||||||
|
i(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void celero::AddExperimentCompleteFunction(std::function<void(std::shared_ptr<Experiment>)> x)
|
||||||
|
{
|
||||||
|
ExperimentFunctions.push_back(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void celero::AddExperimentResultCompleteFunction(std::function<void(std::shared_ptr<celero::ExperimentResult>)> x)
|
||||||
|
{
|
||||||
|
ExperimentResultFunctions.push_back(x);
|
||||||
|
}
|
65
lib/celero/Callbacks.h
Normal file
65
lib/celero/Callbacks.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#ifndef H_CELERO_CALLBACKS_H
|
||||||
|
#define H_CELERO_CALLBACKS_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \namespace celero
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018, 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// Ideas from Nick Brunn's Hayai (https://github.com/nickbruun/hayai) were used and I likely owe him a beer.
|
||||||
|
///
|
||||||
|
/// Special thanks to the band "3" for providing the development soundtrack.
|
||||||
|
///
|
||||||
|
/// "Iterations" refers to how many loops of the test function are measured as a time.
|
||||||
|
/// For very fast code, many iterations would help amoratize measurement error.
|
||||||
|
///
|
||||||
|
/// "Samples" refers to how many sets of "iterations" will be performed. Each "sample" is
|
||||||
|
/// a single measurement. Set to 0 to have Celero decide how many samples are required
|
||||||
|
/// for a minimally significant answer.
|
||||||
|
///
|
||||||
|
/// It is highly encouraged to only run this code compiled in a "Release" mode to use all available optimizations.
|
||||||
|
///
|
||||||
|
|
||||||
|
#include <celero/Experiment.h>
|
||||||
|
#include <celero/Export.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \brief Add a function to call when a experiment is completed.
|
||||||
|
///
|
||||||
|
/// This will be called at the end of a complete experiment (benchmark + experiment results.)
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void AddExperimentCompleteFunction(std::function<void(std::shared_ptr<celero::Experiment>)> x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Add a function to call when a experiment is completed.
|
||||||
|
///
|
||||||
|
/// This will be called at the end of every benchmark or user experiment upon completion.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void AddExperimentResultCompleteFunction(std::function<void(std::shared_ptr<celero::ExperimentResult>)> x);
|
||||||
|
|
||||||
|
namespace impl
|
||||||
|
{
|
||||||
|
CELERO_EXPORT void ExperimentComplete(std::shared_ptr<Experiment> x);
|
||||||
|
CELERO_EXPORT void ExperimentResultComplete(std::shared_ptr<celero::ExperimentResult> x);
|
||||||
|
} // namespace impl
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
289
lib/celero/Celero.cpp
Normal file
289
lib/celero/Celero.cpp
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Archive.h>
|
||||||
|
#include <celero/Benchmark.h>
|
||||||
|
#include <celero/Callbacks.h>
|
||||||
|
#include <celero/Celero.h>
|
||||||
|
#include <celero/CommandLine.h>
|
||||||
|
#include <celero/Console.h>
|
||||||
|
#include <celero/Distribution.h>
|
||||||
|
#include <celero/Exceptions.h>
|
||||||
|
#include <celero/Executor.h>
|
||||||
|
#include <celero/JUnit.h>
|
||||||
|
#include <celero/Print.h>
|
||||||
|
#include <celero/ResultTable.h>
|
||||||
|
#include <celero/TestVector.h>
|
||||||
|
#include <celero/UserDefinedMeasurement.h>
|
||||||
|
#include <celero/Utilities.h>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cmath>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
std::shared_ptr<celero::Benchmark> celero::RegisterTest(const char* groupName, const char* benchmarkName, const uint64_t samples,
|
||||||
|
const uint64_t iterations, const uint64_t threads,
|
||||||
|
std::shared_ptr<celero::Factory> experimentFactory, const double target)
|
||||||
|
{
|
||||||
|
if(groupName != nullptr && benchmarkName != nullptr)
|
||||||
|
{
|
||||||
|
auto bm = celero::TestVector::Instance()[groupName];
|
||||||
|
|
||||||
|
if(bm == nullptr)
|
||||||
|
{
|
||||||
|
bm = std::make_shared<Benchmark>(groupName);
|
||||||
|
celero::TestVector::Instance().push_back(bm);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto p = std::make_shared<Experiment>(bm);
|
||||||
|
p->setIsBaselineCase(false);
|
||||||
|
p->setName(benchmarkName);
|
||||||
|
p->setSamples(samples);
|
||||||
|
p->setIterations(iterations);
|
||||||
|
p->setThreads(threads);
|
||||||
|
p->setFactory(experimentFactory);
|
||||||
|
p->setBaselineTarget(target);
|
||||||
|
|
||||||
|
bm->addExperiment(p);
|
||||||
|
|
||||||
|
return bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<celero::Benchmark> celero::RegisterBaseline(const char* groupName, const char* benchmarkName, const uint64_t samples,
|
||||||
|
const uint64_t iterations, const uint64_t threads,
|
||||||
|
std::shared_ptr<celero::Factory> experimentFactory)
|
||||||
|
{
|
||||||
|
if(groupName != nullptr && benchmarkName != nullptr)
|
||||||
|
{
|
||||||
|
auto bm = celero::TestVector::Instance()[groupName];
|
||||||
|
|
||||||
|
if(bm == nullptr)
|
||||||
|
{
|
||||||
|
bm = std::make_shared<Benchmark>(groupName);
|
||||||
|
celero::TestVector::Instance().push_back(bm);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto p = std::make_shared<Experiment>(bm);
|
||||||
|
p->setIsBaselineCase(true);
|
||||||
|
p->setName(benchmarkName);
|
||||||
|
p->setSamples(samples);
|
||||||
|
p->setIterations(iterations);
|
||||||
|
p->setThreads(threads);
|
||||||
|
p->setFactory(experimentFactory);
|
||||||
|
p->setBaselineTarget(1.0);
|
||||||
|
|
||||||
|
bm->setBaseline(p);
|
||||||
|
|
||||||
|
return bm;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void celero::Run(int argc, char** argv)
|
||||||
|
{
|
||||||
|
#ifdef _DEBUG
|
||||||
|
std::cout << "Celero is running in Debug. Results are for debugging only as any measurements made while in Debug are likely not representative "
|
||||||
|
"of non-debug results."
|
||||||
|
<< std::endl
|
||||||
|
<< std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
cmdline::parser args;
|
||||||
|
args.add("list", 'l', "Prints a list of all available benchmarks.");
|
||||||
|
args.add<std::string>("group", 'g', "Runs a specific group of benchmarks.", false, "");
|
||||||
|
args.add<std::string>("outputTable", 't', "Saves a results table to the named file.", false, "");
|
||||||
|
args.add<std::string>("junit", 'j', "Saves a JUnit XML-formatted file to the named file.", false, "");
|
||||||
|
args.add<std::string>("archive", 'a', "Saves or updates a result archive file.", false, "");
|
||||||
|
args.add<uint64_t>("distribution", 'd', "Builds a file to help characterize the distribution of measurements and exits.", false, 0);
|
||||||
|
args.add<bool>("catchExceptions", 'e', "Allows Celero to catch exceptions and continue processing following benchmarks.", false, true);
|
||||||
|
args.parse_check(argc, argv);
|
||||||
|
|
||||||
|
if(args.exist("list") == true)
|
||||||
|
{
|
||||||
|
auto& tests = celero::TestVector::Instance();
|
||||||
|
std::vector<std::string> testNames;
|
||||||
|
|
||||||
|
for(auto i = size_t(0); i < tests.size(); i++)
|
||||||
|
{
|
||||||
|
auto bm = celero::TestVector::Instance()[i];
|
||||||
|
testNames.push_back(bm->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::sort(std::begin(testNames), std::end(testNames));
|
||||||
|
|
||||||
|
std::cout << "Avaliable tests:" << std::endl;
|
||||||
|
for(auto i : testNames)
|
||||||
|
{
|
||||||
|
std::cout << "\t" << i << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initial output
|
||||||
|
std::cout << "Celero" << std::endl;
|
||||||
|
|
||||||
|
// Disable dynamic CPU frequency scaling
|
||||||
|
celero::timer::CachePerformanceFrequency(false);
|
||||||
|
|
||||||
|
// Shall we build a distribution?
|
||||||
|
auto intArgument = args.get<uint64_t>("distribution");
|
||||||
|
if(intArgument > 0)
|
||||||
|
{
|
||||||
|
RunDistribution(intArgument);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has a result output file been specified?
|
||||||
|
auto mustCloseFile = false;
|
||||||
|
auto argument = args.get<std::string>("outputTable");
|
||||||
|
if(argument.empty() == false)
|
||||||
|
{
|
||||||
|
std::cout << "Writing results to: " << argument << std::endl;
|
||||||
|
celero::ResultTable::Instance().setFileName(argument);
|
||||||
|
|
||||||
|
celero::AddExperimentResultCompleteFunction([](std::shared_ptr<celero::ExperimentResult> p) { celero::ResultTable::Instance().add(p); });
|
||||||
|
mustCloseFile = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has a result output file been specified?
|
||||||
|
argument = args.get<std::string>("archive");
|
||||||
|
if(argument.empty() == false)
|
||||||
|
{
|
||||||
|
std::cout << "Archiving results to: " << argument << std::endl;
|
||||||
|
celero::Archive::Instance().setFileName(argument);
|
||||||
|
|
||||||
|
celero::AddExperimentResultCompleteFunction([](std::shared_ptr<celero::ExperimentResult> p) { celero::Archive::Instance().add(p); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has a JUnit output file been specified?
|
||||||
|
argument = args.get<std::string>("junit");
|
||||||
|
if(argument.empty() == false)
|
||||||
|
{
|
||||||
|
std::cout << "Writing JUnit results to: " << argument << std::endl;
|
||||||
|
celero::JUnit::Instance().setFileName(argument);
|
||||||
|
|
||||||
|
celero::AddExperimentResultCompleteFunction([](std::shared_ptr<celero::ExperimentResult> p) { celero::JUnit::Instance().add(p); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has a flag to catch exceptions or not been specified?
|
||||||
|
if(args.exist("catchExceptions") == true)
|
||||||
|
{
|
||||||
|
ExceptionSettings::SetCatchExceptions(args.get<bool>("catchExceptions"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Has a run group been specified?
|
||||||
|
argument = args.get<std::string>("group");
|
||||||
|
|
||||||
|
// Collect all user-defined fields
|
||||||
|
std::set<std::string> userDefinedFields;
|
||||||
|
auto collectFromBenchmark = [&](std::shared_ptr<Benchmark> bmark) {
|
||||||
|
// Collect from baseline
|
||||||
|
auto baselineExperiment = bmark->getBaseline();
|
||||||
|
if(baselineExperiment != nullptr)
|
||||||
|
{
|
||||||
|
auto test = baselineExperiment->getFactory()->Create();
|
||||||
|
UserDefinedMeasurementCollector udmCollector(test);
|
||||||
|
for(const auto& fieldName : udmCollector.getFields(test))
|
||||||
|
{
|
||||||
|
userDefinedFields.insert(fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect from all experiments
|
||||||
|
const auto experimentSize = bmark->getExperimentSize();
|
||||||
|
|
||||||
|
for(size_t i = 0; i < experimentSize; i++)
|
||||||
|
{
|
||||||
|
auto e = bmark->getExperiment(i);
|
||||||
|
assert(e != nullptr);
|
||||||
|
|
||||||
|
auto test = baselineExperiment->getFactory()->Create();
|
||||||
|
UserDefinedMeasurementCollector udmCollector(test);
|
||||||
|
for(const auto& fieldName : udmCollector.getFields(test))
|
||||||
|
{
|
||||||
|
userDefinedFields.insert(fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(argument.empty() == false)
|
||||||
|
{
|
||||||
|
auto bmark = celero::TestVector::Instance()[argument];
|
||||||
|
collectFromBenchmark(bmark);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(size_t i = 0; i < celero::TestVector::Instance().size(); i++)
|
||||||
|
{
|
||||||
|
auto bmark = celero::TestVector::Instance()[i];
|
||||||
|
collectFromBenchmark(bmark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> userDefinedFieldsOrder(userDefinedFields.begin(), userDefinedFields.end());
|
||||||
|
|
||||||
|
Printer::get().initialize(userDefinedFieldsOrder);
|
||||||
|
Printer::get().TableBanner();
|
||||||
|
|
||||||
|
const auto startTime = celero::timer::GetSystemTime();
|
||||||
|
|
||||||
|
if(argument.empty() == false)
|
||||||
|
{
|
||||||
|
executor::Run(argument);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
executor::RunAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto totalTime = celero::timer::ConvertSystemTime(celero::timer::GetSystemTime() - startTime);
|
||||||
|
|
||||||
|
if(mustCloseFile == true)
|
||||||
|
{
|
||||||
|
celero::ResultTable::Instance().closeFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Final output.
|
||||||
|
auto hours = std::to_string(static_cast<int>(totalTime) / 3600);
|
||||||
|
auto minutes = std::to_string((static_cast<int>(totalTime) % 3600) / 60);
|
||||||
|
auto seconds = std::to_string(fmod(totalTime, 60.0));
|
||||||
|
|
||||||
|
if(hours.length() < 2)
|
||||||
|
{
|
||||||
|
hours = std::string("0") + hours;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(minutes.length() < 2)
|
||||||
|
{
|
||||||
|
minutes = std::string("0") + minutes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fmod(totalTime, 60.0) < 10.0)
|
||||||
|
{
|
||||||
|
seconds = std::string("0") + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "Completed in " << hours << ":" << minutes << ":" << seconds << std::endl;
|
||||||
|
}
|
315
lib/celero/Celero.h
Normal file
315
lib/celero/Celero.h
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
#ifndef H_CELERO_CELERO_H
|
||||||
|
#define H_CELERO_CELERO_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \namespace celero
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
/// Special thanks to the bands "3" and "Coheed and Cambria" for providing the development soundtrack.
|
||||||
|
///
|
||||||
|
/// "Iterations" refers to how many loops of the test function are measured as a time.
|
||||||
|
/// For very fast code, many iterations would help amoratize measurement error.
|
||||||
|
///
|
||||||
|
/// "Samples" refers to how many sets of "Iterations" will be performed. Each "sample" is
|
||||||
|
/// a single measurement.
|
||||||
|
///
|
||||||
|
/// It is highly encouraged to only run this code compiled in a "Release" mode to use all available optimizations.
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <process.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <celero/Benchmark.h>
|
||||||
|
#include <celero/GenericFactory.h>
|
||||||
|
#include <celero/TestFixture.h>
|
||||||
|
#include <celero/ThreadTestFixture.h>
|
||||||
|
#include <celero/UserDefinedMeasurementCollector.h>
|
||||||
|
#include <celero/UserDefinedMeasurementTemplate.h>
|
||||||
|
#include <celero/Utilities.h>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \brief Adds a new test to the list of tests to be executed.
|
||||||
|
///
|
||||||
|
/// All tests must be registered prior to calling celer::Run().
|
||||||
|
///
|
||||||
|
/// \param groupName The name of the Test Group. Used for retrieving the associated baseline.
|
||||||
|
/// \param benchmarkName A unique name for a specific test within a Test Group.
|
||||||
|
/// \param samples The total number of times to execute the Test. (Each test contains iterations.)
|
||||||
|
/// \param iterations The total number of iterations per Test.
|
||||||
|
/// \param threads The total number of threads per Test sample.
|
||||||
|
/// \param experimentFactory The factory implementation for the test.
|
||||||
|
///
|
||||||
|
/// \returns a pointer to a Benchmark instance representing the given test.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT std::shared_ptr<celero::Benchmark> RegisterTest(const char* groupName, const char* benchmarkName, const uint64_t samples,
|
||||||
|
const uint64_t iterations, const uint64_t threads,
|
||||||
|
std::shared_ptr<celero::Factory> experimentFactory, const double target = -1);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Adds a new test baseline to the list of test baseliness to be executed.
|
||||||
|
///
|
||||||
|
/// All test baselines must be registered prior to calling celer::Run().
|
||||||
|
///
|
||||||
|
/// \param groupName The name of the Test Group that the baseline is associated with.
|
||||||
|
/// \param benchmarkName A unique name for a specific test baseline within a Test Group.
|
||||||
|
/// \param samples The total number of times to execute the Test baseline. (Each sample contains one or more iterations.)
|
||||||
|
/// \param iterations The total number of iterations per Test baseline sample.
|
||||||
|
/// \param threads The total number of threads per Test baseline.
|
||||||
|
/// \param experimentFactory The factory implementation for the test baseline.
|
||||||
|
///
|
||||||
|
/// \returns a pointer to a Benchmark instance representing the given test.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT std::shared_ptr<Benchmark> RegisterBaseline(const char* groupName, const char* benchmarkName, const uint64_t samples,
|
||||||
|
const uint64_t iterations, const uint64_t threads,
|
||||||
|
std::shared_ptr<Factory> experimentFactory);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Builds a distribution of total system measurement error.
|
||||||
|
///
|
||||||
|
/// The result vector contains microseconds for each trivial timer sample.
|
||||||
|
/// The purpose is to be able to characterize the generic distribution of results
|
||||||
|
/// on a given system.
|
||||||
|
///
|
||||||
|
/// This is just an attempt to characterize the distribution, not quantify it.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT std::vector<uint64_t> BuildDistribution(uint64_t numberOfSamples, uint64_t iterationsPerSample);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief The main test executor.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void Run(int argc, char** argv);
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define CELERO_MAIN
|
||||||
|
///
|
||||||
|
/// \brief A macro to build the most basic main() required to run the benchmark tests.
|
||||||
|
///
|
||||||
|
#define CELERO_MAIN \
|
||||||
|
int main(int argc, char** argv) \
|
||||||
|
{ \
|
||||||
|
celero::Run(argc, argv); \
|
||||||
|
return 0; \
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BENCHMARK_CLASS_NAME
|
||||||
|
///
|
||||||
|
/// \brief A macro to build a class name based on the test groupo and benchmark names.
|
||||||
|
///
|
||||||
|
#define BENCHMARK_CLASS_NAME(groupName, benchmarkName) CeleroUserBenchmark##_##groupName##_##benchmarkName
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BENCHMARK_IMPL
|
||||||
|
///
|
||||||
|
/// A macro to create a class of a unique name which can be used to register and execute a benchmark test.
|
||||||
|
///
|
||||||
|
#define BENCHMARK_IMPL(groupName, benchmarkName, fixtureName, samples, iterations, threads) \
|
||||||
|
class BENCHMARK_CLASS_NAME(groupName, benchmarkName) : public fixtureName \
|
||||||
|
{ \
|
||||||
|
public: \
|
||||||
|
BENCHMARK_CLASS_NAME(groupName, benchmarkName)() : fixtureName() \
|
||||||
|
{ \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
protected: \
|
||||||
|
virtual void UserBenchmark() override; \
|
||||||
|
\
|
||||||
|
private: \
|
||||||
|
static const std::shared_ptr<::celero::Benchmark> info; \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
const std::shared_ptr<::celero::Benchmark> BENCHMARK_CLASS_NAME(groupName, benchmarkName)::info = \
|
||||||
|
::celero::RegisterTest(#groupName, #benchmarkName, samples, iterations, threads, \
|
||||||
|
std::make_shared<::celero::GenericFactory<BENCHMARK_CLASS_NAME(groupName, benchmarkName)>>()); \
|
||||||
|
\
|
||||||
|
void BENCHMARK_CLASS_NAME(groupName, benchmarkName)::UserBenchmark()
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BENCHMARK_TEST_IMPL
|
||||||
|
///
|
||||||
|
/// A macro to create a class of a unique name which can be used to register and execute a benchmark test.
|
||||||
|
///
|
||||||
|
#define BENCHMARK_TEST_IMPL(groupName, benchmarkName, fixtureName, samples, iterations, threads, target) \
|
||||||
|
class BENCHMARK_CLASS_NAME(groupName, benchmarkName) : public fixtureName \
|
||||||
|
{ \
|
||||||
|
public: \
|
||||||
|
BENCHMARK_CLASS_NAME(groupName, benchmarkName)() : fixtureName() \
|
||||||
|
{ \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
protected: \
|
||||||
|
virtual void UserBenchmark() override; \
|
||||||
|
\
|
||||||
|
private: \
|
||||||
|
static const std::shared_ptr<::celero::Benchmark> info; \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
const std::shared_ptr<::celero::Benchmark> BENCHMARK_CLASS_NAME(groupName, benchmarkName)::info = \
|
||||||
|
::celero::RegisterTest(#groupName, #benchmarkName, samples, iterations, threads, \
|
||||||
|
std::make_shared<::celero::GenericFactory<BENCHMARK_CLASS_NAME(groupName, benchmarkName)>>(), target); \
|
||||||
|
\
|
||||||
|
void BENCHMARK_CLASS_NAME(groupName, benchmarkName)::UserBenchmark()
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BENCHMARK_F
|
||||||
|
///
|
||||||
|
/// \brief A macro to place in user code to define a UserBenchmark function for a benchmark containing a test fixture.
|
||||||
|
///
|
||||||
|
/// Using the BENCHMARK_ macro, this effectivly fills in a class's UserBenchmark() function.
|
||||||
|
///
|
||||||
|
#define BENCHMARK_F(groupName, benchmarkName, fixtureName, samples, iterations) \
|
||||||
|
BENCHMARK_IMPL(groupName, benchmarkName, fixtureName, samples, iterations, 1)
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BENCHMARK_T
|
||||||
|
///
|
||||||
|
/// \brief A macro to place in user code to define a UserBenchmark function for a benchmark containing a threaded test fixture.
|
||||||
|
///
|
||||||
|
/// Using the BENCHMARK_ macro, this effectivly fills in a class's UserBenchmark() function.
|
||||||
|
///
|
||||||
|
#define BENCHMARK_T(groupName, benchmarkName, fixtureName, samples, iterations, threads) \
|
||||||
|
BENCHMARK_IMPL(groupName, benchmarkName, fixtureName, samples, iterations, threads)
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BENCHMARK_TEST_F
|
||||||
|
///
|
||||||
|
/// \brief A macro to place in user code to define a UserBenchmark function for a benchmark containing a test fixture.
|
||||||
|
///
|
||||||
|
/// Using the BENCHMARK_ macro, this effectivly fills in a class's UserBenchmark() function.
|
||||||
|
///
|
||||||
|
#define BENCHMARK_TEST_F(groupName, benchmarkName, fixtureName, samples, iterations, target) \
|
||||||
|
BENCHMARK_TEST_IMPL(groupName, benchmarkName, fixtureName, samples, iterations, 1, target)
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BENCHMARK_TEST_T
|
||||||
|
///
|
||||||
|
/// \brief A macro to place in user code to define a UserBenchmark function for a benchmark containing a threaded test fixture.
|
||||||
|
///
|
||||||
|
/// Using the BENCHMARK_ macro, this effectivly fills in a class's UserBenchmark() function.
|
||||||
|
///
|
||||||
|
#define BENCHMARK_TEST_T(groupName, benchmarkName, fixtureName, samples, iterations, threads, target) \
|
||||||
|
BENCHMARK_TEST_IMPL(groupName, benchmarkName, fixtureName, samples, iterations, threads, target)
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BENCHMARK
|
||||||
|
///
|
||||||
|
/// \brief A macro to place in user code to define a UserBenchmark function for a benchmark.
|
||||||
|
///
|
||||||
|
/// Using the BENCHMARK_ macro, this effectivly fills in a class's UserBenchmark() function.
|
||||||
|
///
|
||||||
|
#define BENCHMARK(groupName, benchmarkName, samples, iterations) \
|
||||||
|
BENCHMARK_IMPL(groupName, benchmarkName, ::celero::TestFixture, samples, iterations, 1)
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BENCHMARK
|
||||||
|
///
|
||||||
|
/// \brief A macro to place in user code to define a UserBenchmark function for a benchmark.
|
||||||
|
///
|
||||||
|
/// Using the BENCHMARK_ macro, this effectivly fills in a class's UserBenchmark() function.
|
||||||
|
///
|
||||||
|
#define BENCHMARK_TEST(groupName, benchmarkName, samples, iterations, target) \
|
||||||
|
BENCHMARK_TEST_IMPL(groupName, benchmarkName, ::celero::TestFixture, samples, iterations, 1, target)
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BASELINE_CLASS_NAME
|
||||||
|
///
|
||||||
|
/// \brief A macro to build a class name based on the test group and baseline names.
|
||||||
|
///
|
||||||
|
#define BASELINE_CLASS_NAME(groupName, baselineName) CeleroUserBaseline##_##groupName##_##baselineName
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BASELINE_IMPL
|
||||||
|
///
|
||||||
|
/// A macro to create a class of a unique name which can be used to register and execute a baseline benchmark test.
|
||||||
|
///
|
||||||
|
#define BASELINE_IMPL(groupName, baselineName, fixtureName, samples, iterations, threads, useconds) \
|
||||||
|
class BASELINE_CLASS_NAME(groupName, baselineName) : public fixtureName \
|
||||||
|
{ \
|
||||||
|
public: \
|
||||||
|
BASELINE_CLASS_NAME(groupName, baselineName)() : fixtureName() \
|
||||||
|
{ \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
protected: \
|
||||||
|
virtual void UserBenchmark() override; \
|
||||||
|
virtual uint64_t HardCodedMeasurement() const override \
|
||||||
|
{ \
|
||||||
|
return uint64_t(useconds); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
private: \
|
||||||
|
static const std::shared_ptr<::celero::Benchmark> info; \
|
||||||
|
}; \
|
||||||
|
\
|
||||||
|
const std::shared_ptr<::celero::Benchmark> BASELINE_CLASS_NAME(groupName, baselineName)::info = \
|
||||||
|
::celero::RegisterBaseline(#groupName, #baselineName, samples, iterations, threads, \
|
||||||
|
std::make_shared<::celero::GenericFactory<BASELINE_CLASS_NAME(groupName, baselineName)>>()); \
|
||||||
|
\
|
||||||
|
void BASELINE_CLASS_NAME(groupName, baselineName)::UserBenchmark()
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BASELINE_F
|
||||||
|
///
|
||||||
|
/// \brief A macro to place in user code to define a UserBenchmark function for a benchmark containing a test fixture.
|
||||||
|
///
|
||||||
|
/// Using the BASELINE_ macro, this effectivly fills in a class's UserBenchmark() function.
|
||||||
|
///
|
||||||
|
#define BASELINE_F(groupName, baselineName, fixtureName, samples, iterations) \
|
||||||
|
BASELINE_IMPL(groupName, baselineName, fixtureName, samples, iterations, 1, 0)
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BASELINE_T
|
||||||
|
///
|
||||||
|
/// \brief A macro to place in user code to define a UserBenchmark function for a benchmark containing a threaded test fixture.
|
||||||
|
///
|
||||||
|
/// Using the BASELINE_ macro, this effectivly fills in a class's UserBenchmark() function.
|
||||||
|
///
|
||||||
|
#define BASELINE_T(groupName, baselineName, fixtureName, samples, iterations, threads) \
|
||||||
|
BASELINE_IMPL(groupName, baselineName, fixtureName, samples, iterations, threads, 0)
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BASELINE
|
||||||
|
///
|
||||||
|
/// \brief A macro to place in user code to define a UserBenchmark function for a benchmark.
|
||||||
|
///
|
||||||
|
/// Using the BASELINE_ macro, this effectivly fills in a class's UserBenchmark() function.
|
||||||
|
///
|
||||||
|
#define BASELINE(groupName, baselineName, samples, iterations) \
|
||||||
|
BASELINE_IMPL(groupName, baselineName, ::celero::TestFixture, samples, iterations, 1, 0)
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \define BASELINE_FIXED
|
||||||
|
///
|
||||||
|
/// \brief A macro to place in user code to define a UserBenchmark function for a benchmark with a hard-coded timing.
|
||||||
|
///
|
||||||
|
/// This will NOT perform any timing measurments but will instead use the number of microseconds passed in as the measured time.
|
||||||
|
///
|
||||||
|
/// Using the BASELINE_ macro, this effectivly fills in a class's UserBenchmark() function.
|
||||||
|
///
|
||||||
|
#define BASELINE_FIXED(groupName, baselineName, samples, iterations, useconds) \
|
||||||
|
BASELINE_IMPL(groupName, baselineName, ::celero::TestFixture, samples, iterations, 1, useconds)
|
||||||
|
#define BASELINE_FIXED_F(groupName, baselineName, fixtureName, samples, iterations, useconds) \
|
||||||
|
BASELINE_IMPL(groupName, baselineName, fixtureName, samples, iterations, 1, useconds)
|
||||||
|
#define BASELINE_FIXED_T(groupName, baselineName, fixtureName, samples, iterations, threads, useconds) \
|
||||||
|
BASELINE_IMPL(groupName, baselineName, fixtureName, samples, iterations, threads, useconds)
|
||||||
|
|
||||||
|
#endif
|
942
lib/celero/CommandLine.h
Normal file
942
lib/celero/CommandLine.h
Normal file
@ -0,0 +1,942 @@
|
|||||||
|
/*
|
||||||
|
Copyright (c) 2009, Hideyuki Tanaka
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in the
|
||||||
|
documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the <organization> nor the
|
||||||
|
names of its contributors may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY <copyright holder> ''AS IS'' AND ANY
|
||||||
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
|
||||||
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <typeinfo>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
#include <cxxabi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace cmdline
|
||||||
|
{
|
||||||
|
namespace detail
|
||||||
|
{
|
||||||
|
template <typename Target, typename Source, bool Same>
|
||||||
|
class lexical_cast_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Target cast(const Source &arg)
|
||||||
|
{
|
||||||
|
Target ret;
|
||||||
|
std::stringstream ss;
|
||||||
|
if(!(ss << arg && ss >> ret && ss.eof()))
|
||||||
|
throw std::bad_cast();
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Target, typename Source>
|
||||||
|
class lexical_cast_t<Target, Source, true>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Target cast(const Source &arg)
|
||||||
|
{
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Source>
|
||||||
|
class lexical_cast_t<std::string, Source, false>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static std::string cast(const Source &arg)
|
||||||
|
{
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << arg;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Target>
|
||||||
|
class lexical_cast_t<Target, std::string, false>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static Target cast(const std::string &arg)
|
||||||
|
{
|
||||||
|
Target ret;
|
||||||
|
std::istringstream ss(arg);
|
||||||
|
if(!(ss >> ret && ss.eof()))
|
||||||
|
throw std::bad_cast();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
struct is_same
|
||||||
|
{
|
||||||
|
static const bool value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_same<T, T>
|
||||||
|
{
|
||||||
|
static const bool value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Target, typename Source>
|
||||||
|
Target lexical_cast(const Source &arg)
|
||||||
|
{
|
||||||
|
return lexical_cast_t<Target, Source, detail::is_same<Target, Source>::value>::cast(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
static inline std::string demangle(const std::string &)
|
||||||
|
{
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline std::string demangle(const std::string &name)
|
||||||
|
{
|
||||||
|
int status = 0;
|
||||||
|
char *p = abi::__cxa_demangle(name.c_str(), 0, 0, &status);
|
||||||
|
std::string ret(p);
|
||||||
|
free(p);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::string readable_typename()
|
||||||
|
{
|
||||||
|
return demangle(typeid(T).name());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
std::string default_value(T def)
|
||||||
|
{
|
||||||
|
return detail::lexical_cast<std::string>(def);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline std::string readable_typename<std::string>()
|
||||||
|
{
|
||||||
|
return "string";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
//-----
|
||||||
|
|
||||||
|
class cmdline_error : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
cmdline_error(const std::string &msg) : msg(msg)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~cmdline_error() throw()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
const char *what() const throw()
|
||||||
|
{
|
||||||
|
return msg.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string msg;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct default_reader
|
||||||
|
{
|
||||||
|
T operator()(const std::string &str)
|
||||||
|
{
|
||||||
|
return detail::lexical_cast<T>(str);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct range_reader
|
||||||
|
{
|
||||||
|
range_reader(const T &low, const T &high) : low(low), high(high)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
T operator()(const std::string &s) const
|
||||||
|
{
|
||||||
|
T ret = default_reader<T>()(s);
|
||||||
|
if(!(ret >= low && ret <= high))
|
||||||
|
throw cmdline::cmdline_error("range_error");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T low, high;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
range_reader<T> range(const T &low, const T &high)
|
||||||
|
{
|
||||||
|
return range_reader<T>(low, high);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct oneof_reader
|
||||||
|
{
|
||||||
|
T operator()(const std::string &s)
|
||||||
|
{
|
||||||
|
T ret = default_reader<T>()(s);
|
||||||
|
if(std::find(alt.begin(), alt.end(), ret) == alt.end())
|
||||||
|
throw cmdline_error("");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
void add(const T &v)
|
||||||
|
{
|
||||||
|
alt.push_back(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<T> alt;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
oneof_reader<T> oneof(T a1)
|
||||||
|
{
|
||||||
|
oneof_reader<T> ret;
|
||||||
|
ret.add(a1);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
oneof_reader<T> oneof(T a1, T a2)
|
||||||
|
{
|
||||||
|
oneof_reader<T> ret;
|
||||||
|
ret.add(a1);
|
||||||
|
ret.add(a2);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
oneof_reader<T> oneof(T a1, T a2, T a3)
|
||||||
|
{
|
||||||
|
oneof_reader<T> ret;
|
||||||
|
ret.add(a1);
|
||||||
|
ret.add(a2);
|
||||||
|
ret.add(a3);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
oneof_reader<T> oneof(T a1, T a2, T a3, T a4)
|
||||||
|
{
|
||||||
|
oneof_reader<T> ret;
|
||||||
|
ret.add(a1);
|
||||||
|
ret.add(a2);
|
||||||
|
ret.add(a3);
|
||||||
|
ret.add(a4);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5)
|
||||||
|
{
|
||||||
|
oneof_reader<T> ret;
|
||||||
|
ret.add(a1);
|
||||||
|
ret.add(a2);
|
||||||
|
ret.add(a3);
|
||||||
|
ret.add(a4);
|
||||||
|
ret.add(a5);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6)
|
||||||
|
{
|
||||||
|
oneof_reader<T> ret;
|
||||||
|
ret.add(a1);
|
||||||
|
ret.add(a2);
|
||||||
|
ret.add(a3);
|
||||||
|
ret.add(a4);
|
||||||
|
ret.add(a5);
|
||||||
|
ret.add(a6);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7)
|
||||||
|
{
|
||||||
|
oneof_reader<T> ret;
|
||||||
|
ret.add(a1);
|
||||||
|
ret.add(a2);
|
||||||
|
ret.add(a3);
|
||||||
|
ret.add(a4);
|
||||||
|
ret.add(a5);
|
||||||
|
ret.add(a6);
|
||||||
|
ret.add(a7);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8)
|
||||||
|
{
|
||||||
|
oneof_reader<T> ret;
|
||||||
|
ret.add(a1);
|
||||||
|
ret.add(a2);
|
||||||
|
ret.add(a3);
|
||||||
|
ret.add(a4);
|
||||||
|
ret.add(a5);
|
||||||
|
ret.add(a6);
|
||||||
|
ret.add(a7);
|
||||||
|
ret.add(a8);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9)
|
||||||
|
{
|
||||||
|
oneof_reader<T> ret;
|
||||||
|
ret.add(a1);
|
||||||
|
ret.add(a2);
|
||||||
|
ret.add(a3);
|
||||||
|
ret.add(a4);
|
||||||
|
ret.add(a5);
|
||||||
|
ret.add(a6);
|
||||||
|
ret.add(a7);
|
||||||
|
ret.add(a8);
|
||||||
|
ret.add(a9);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
oneof_reader<T> oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, T a10)
|
||||||
|
{
|
||||||
|
oneof_reader<T> ret;
|
||||||
|
ret.add(a1);
|
||||||
|
ret.add(a2);
|
||||||
|
ret.add(a3);
|
||||||
|
ret.add(a4);
|
||||||
|
ret.add(a5);
|
||||||
|
ret.add(a6);
|
||||||
|
ret.add(a7);
|
||||||
|
ret.add(a8);
|
||||||
|
ret.add(a9);
|
||||||
|
ret.add(a10);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----
|
||||||
|
|
||||||
|
class parser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~parser()
|
||||||
|
{
|
||||||
|
for(std::map<std::string, option_base *>::iterator p = options.begin(); p != options.end(); p++)
|
||||||
|
delete p->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void add(const std::string &name, char short_name = 0, const std::string &desc = "")
|
||||||
|
{
|
||||||
|
if(options.count(name))
|
||||||
|
throw cmdline_error("multiple definition: " + name);
|
||||||
|
options[name] = new option_without_value(name, short_name, desc);
|
||||||
|
ordered.push_back(options[name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void add(const std::string &name, char short_name = 0, const std::string &desc = "", bool need = true, const T def = T())
|
||||||
|
{
|
||||||
|
add(name, short_name, desc, need, def, default_reader<T>());
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T, class F>
|
||||||
|
void add(const std::string &name, char short_name = 0, const std::string &desc = "", bool need = true, const T def = T(), F reader = F())
|
||||||
|
{
|
||||||
|
if(options.count(name))
|
||||||
|
throw cmdline_error("multiple definition: " + name);
|
||||||
|
options[name] = new option_with_value_with_reader<T, F>(name, short_name, need, def, desc, reader);
|
||||||
|
ordered.push_back(options[name]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void footer(const std::string &f)
|
||||||
|
{
|
||||||
|
ftr = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_program_name(const std::string &name)
|
||||||
|
{
|
||||||
|
prog_name = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool exist(const std::string &name) const
|
||||||
|
{
|
||||||
|
if(options.count(name) == 0)
|
||||||
|
throw cmdline_error("there is no flag: --" + name);
|
||||||
|
return options.find(name)->second->has_set();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
const T &get(const std::string &name) const
|
||||||
|
{
|
||||||
|
if(options.count(name) == 0)
|
||||||
|
throw cmdline_error("there is no flag: --" + name);
|
||||||
|
const option_with_value<T> *p = dynamic_cast<const option_with_value<T> *>(options.find(name)->second);
|
||||||
|
if(p == NULL)
|
||||||
|
throw cmdline_error("type mismatch flag '" + name + "'");
|
||||||
|
return p->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> &rest() const
|
||||||
|
{
|
||||||
|
return others;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse(const std::string &arg)
|
||||||
|
{
|
||||||
|
std::vector<std::string> args;
|
||||||
|
|
||||||
|
std::string buf;
|
||||||
|
bool in_quote = false;
|
||||||
|
for(std::string::size_type i = 0; i < arg.length(); i++)
|
||||||
|
{
|
||||||
|
if(arg[i] == '\"')
|
||||||
|
{
|
||||||
|
in_quote = !in_quote;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(arg[i] == ' ' && !in_quote)
|
||||||
|
{
|
||||||
|
args.push_back(buf);
|
||||||
|
buf = "";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(arg[i] == '\\')
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
if(i >= arg.length())
|
||||||
|
{
|
||||||
|
errors.push_back("unexpected occurrence of '\\' at end of string");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf += arg[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(in_quote)
|
||||||
|
{
|
||||||
|
errors.push_back("quote is not closed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(buf.length() > 0)
|
||||||
|
args.push_back(buf);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < args.size(); i++)
|
||||||
|
std::cout << "\"" << args[i] << "\"" << std::endl;
|
||||||
|
|
||||||
|
return parse(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
int argc = static_cast<int>(args.size());
|
||||||
|
std::vector<const char *> argv(static_cast<size_t>(argc));
|
||||||
|
|
||||||
|
for(int i = 0; i < argc; i++)
|
||||||
|
argv[i] = args[i].c_str();
|
||||||
|
|
||||||
|
return parse(argc, &argv[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool parse(int argc, const char *const argv[])
|
||||||
|
{
|
||||||
|
errors.clear();
|
||||||
|
others.clear();
|
||||||
|
|
||||||
|
if(argc < 1)
|
||||||
|
{
|
||||||
|
errors.push_back("argument number must be longer than 0");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(prog_name == "")
|
||||||
|
prog_name = argv[0];
|
||||||
|
|
||||||
|
std::map<char, std::string> lookup;
|
||||||
|
for(std::map<std::string, option_base *>::iterator p = options.begin(); p != options.end(); p++)
|
||||||
|
{
|
||||||
|
if(p->first.length() == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char initial = p->second->short_name();
|
||||||
|
if(initial)
|
||||||
|
{
|
||||||
|
if(lookup.count(initial) > 0)
|
||||||
|
{
|
||||||
|
lookup[initial] = "";
|
||||||
|
errors.push_back(std::string("short option '") + initial + "' is ambiguous");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
lookup[initial] = p->first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 1; i < argc; i++)
|
||||||
|
{
|
||||||
|
if(strncmp(argv[i], "--", 2) == 0)
|
||||||
|
{
|
||||||
|
const char *p = strchr(argv[i] + 2, '=');
|
||||||
|
if(p)
|
||||||
|
{
|
||||||
|
std::string name(argv[i] + 2, p);
|
||||||
|
std::string val(p + 1);
|
||||||
|
set_option(name, val);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string name(argv[i] + 2);
|
||||||
|
if(options.count(name) == 0)
|
||||||
|
{
|
||||||
|
errors.push_back("undefined option: --" + name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(options[name]->has_value())
|
||||||
|
{
|
||||||
|
if(i + 1 >= argc)
|
||||||
|
{
|
||||||
|
errors.push_back("option needs value: --" + name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
set_option(name, argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set_option(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(strncmp(argv[i], "-", 1) == 0)
|
||||||
|
{
|
||||||
|
if(!argv[i][1])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char last = argv[i][1];
|
||||||
|
for(int j = 2; argv[i][j]; j++)
|
||||||
|
{
|
||||||
|
last = argv[i][j];
|
||||||
|
if(lookup.count(argv[i][j - 1]) == 0)
|
||||||
|
{
|
||||||
|
errors.push_back(std::string("undefined short option: -") + argv[i][j - 1]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lookup[argv[i][j - 1]] == "")
|
||||||
|
{
|
||||||
|
errors.push_back(std::string("ambiguous short option: -") + argv[i][j - 1]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
set_option(lookup[argv[i][j - 1]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lookup.count(last) == 0)
|
||||||
|
{
|
||||||
|
errors.push_back(std::string("undefined short option: -") + last);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(lookup[last] == "")
|
||||||
|
{
|
||||||
|
errors.push_back(std::string("ambiguous short option: -") + last);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(i + 1 < argc && options[lookup[last]]->has_value())
|
||||||
|
{
|
||||||
|
set_option(lookup[last], argv[i + 1]);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
set_option(lookup[last]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
others.push_back(argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(std::map<std::string, option_base *>::iterator p = options.begin(); p != options.end(); p++)
|
||||||
|
{
|
||||||
|
if(!p->second->valid())
|
||||||
|
{
|
||||||
|
errors.push_back("need option: --" + std::string(p->first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_check(const std::string &arg)
|
||||||
|
{
|
||||||
|
if(!options.count("help"))
|
||||||
|
add("help", '?', "print this message");
|
||||||
|
check(0, parse(arg));
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_check(const std::vector<std::string> &args)
|
||||||
|
{
|
||||||
|
if(!options.count("help"))
|
||||||
|
add("help", '?', "print this message");
|
||||||
|
check(static_cast<int>(args.size()), parse(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
void parse_check(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if(!options.count("help"))
|
||||||
|
add("help", '?', "print this message");
|
||||||
|
check(argc, parse(argc, argv));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string error() const
|
||||||
|
{
|
||||||
|
return errors.size() > 0 ? errors[0] : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string error_full() const
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
for(size_t i = 0; i < errors.size(); i++)
|
||||||
|
oss << errors[i] << std::endl;
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string usage() const
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "usage: " << prog_name << " ";
|
||||||
|
for(size_t i = 0; i < ordered.size(); i++)
|
||||||
|
{
|
||||||
|
if(ordered[i]->must())
|
||||||
|
oss << ordered[i]->short_description() << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
oss << "[options] ... " << ftr << std::endl;
|
||||||
|
oss << "options:" << std::endl;
|
||||||
|
|
||||||
|
size_t max_width = 0;
|
||||||
|
for(size_t i = 0; i < ordered.size(); i++)
|
||||||
|
{
|
||||||
|
max_width = std::max(max_width, ordered[i]->name().length());
|
||||||
|
}
|
||||||
|
for(size_t i = 0; i < ordered.size(); i++)
|
||||||
|
{
|
||||||
|
if(ordered[i]->short_name())
|
||||||
|
{
|
||||||
|
oss << " -" << ordered[i]->short_name() << ", ";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
oss << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
oss << "--" << ordered[i]->name();
|
||||||
|
for(size_t j = ordered[i]->name().length(); j < max_width + 4; j++)
|
||||||
|
oss << ' ';
|
||||||
|
oss << ordered[i]->description() << std::endl;
|
||||||
|
}
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void check(int argc, bool ok)
|
||||||
|
{
|
||||||
|
if((argc == 1 && !ok) || exist("help"))
|
||||||
|
{
|
||||||
|
std::cerr << usage();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!ok)
|
||||||
|
{
|
||||||
|
std::cerr << error() << std::endl << usage();
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_option(const std::string &name)
|
||||||
|
{
|
||||||
|
if(options.count(name) == 0)
|
||||||
|
{
|
||||||
|
errors.push_back("undefined option: --" + name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!options[name]->set())
|
||||||
|
{
|
||||||
|
errors.push_back("option needs value: --" + name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_option(const std::string &name, const std::string &value)
|
||||||
|
{
|
||||||
|
if(options.count(name) == 0)
|
||||||
|
{
|
||||||
|
errors.push_back("undefined option: --" + name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(!options[name]->set(value))
|
||||||
|
{
|
||||||
|
errors.push_back("option value is invalid: --" + name + "=" + value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class option_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~option_base()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool has_value() const = 0;
|
||||||
|
virtual bool set() = 0;
|
||||||
|
virtual bool set(const std::string &value) = 0;
|
||||||
|
virtual bool has_set() const = 0;
|
||||||
|
virtual bool valid() const = 0;
|
||||||
|
virtual bool must() const = 0;
|
||||||
|
|
||||||
|
virtual const std::string &name() const = 0;
|
||||||
|
virtual char short_name() const = 0;
|
||||||
|
virtual const std::string &description() const = 0;
|
||||||
|
virtual std::string short_description() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class option_without_value : public option_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
option_without_value(const std::string &name, char short_name, const std::string &desc)
|
||||||
|
: nam(name), snam(short_name), desc(desc), has(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
~option_without_value()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_value() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set()
|
||||||
|
{
|
||||||
|
has = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set(const std::string &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_set() const
|
||||||
|
{
|
||||||
|
return has;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool must() const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &name() const
|
||||||
|
{
|
||||||
|
return nam;
|
||||||
|
}
|
||||||
|
|
||||||
|
char short_name() const
|
||||||
|
{
|
||||||
|
return snam;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &description() const
|
||||||
|
{
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string short_description() const
|
||||||
|
{
|
||||||
|
return "--" + nam;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string nam;
|
||||||
|
char snam;
|
||||||
|
std::string desc;
|
||||||
|
bool has;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class option_with_value : public option_base
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
option_with_value(const std::string &name, char short_name, bool need, const T &def, const std::string &desc)
|
||||||
|
: nam(name), snam(short_name), need(need), has(false), def(def), actual(def)
|
||||||
|
{
|
||||||
|
this->desc = full_description(desc);
|
||||||
|
}
|
||||||
|
~option_with_value()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &get() const
|
||||||
|
{
|
||||||
|
return actual;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_value() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set(const std::string &value)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
actual = read(value);
|
||||||
|
has = true;
|
||||||
|
}
|
||||||
|
catch(const std::exception &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_set() const
|
||||||
|
{
|
||||||
|
return has;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool valid() const
|
||||||
|
{
|
||||||
|
if(need && !has)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool must() const
|
||||||
|
{
|
||||||
|
return need;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &name() const
|
||||||
|
{
|
||||||
|
return nam;
|
||||||
|
}
|
||||||
|
|
||||||
|
char short_name() const
|
||||||
|
{
|
||||||
|
return snam;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &description() const
|
||||||
|
{
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string short_description() const
|
||||||
|
{
|
||||||
|
return "--" + nam + "=" + detail::readable_typename<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string full_description(const std::string &description)
|
||||||
|
{
|
||||||
|
return description + " (" + detail::readable_typename<T>() + (need ? "" : " [=" + detail::default_value<T>(def) + "]") + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual T read(const std::string &s) = 0;
|
||||||
|
|
||||||
|
std::string nam;
|
||||||
|
char snam;
|
||||||
|
bool need;
|
||||||
|
std::string desc;
|
||||||
|
|
||||||
|
bool has;
|
||||||
|
T def;
|
||||||
|
T actual;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class F>
|
||||||
|
class option_with_value_with_reader : public option_with_value<T>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
option_with_value_with_reader(const std::string &name, char short_name, bool need, const T def, const std::string &desc, F reader)
|
||||||
|
: option_with_value<T>(name, short_name, need, def, desc), reader(reader)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T read(const std::string &s)
|
||||||
|
{
|
||||||
|
return reader(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
F reader;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<std::string, option_base *> options;
|
||||||
|
std::vector<option_base *> ordered;
|
||||||
|
std::string ftr;
|
||||||
|
|
||||||
|
std::string prog_name;
|
||||||
|
std::vector<std::string> others;
|
||||||
|
|
||||||
|
std::vector<std::string> errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace cmdline
|
253
lib/celero/Console.cpp
Normal file
253
lib/celero/Console.cpp
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Console.h>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#else
|
||||||
|
#include <iostream>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
auto WinColor() -> decltype(GetStdHandle(STD_OUTPUT_HANDLE))
|
||||||
|
{
|
||||||
|
auto h = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||||
|
CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
|
||||||
|
GetConsoleScreenBufferInfo(h, &csbiInfo);
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Red()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_RED);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[31m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void RedBold()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_RED | FOREGROUND_INTENSITY);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[1;31m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Green()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_GREEN);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[32m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void GreenBold()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_GREEN | FOREGROUND_INTENSITY);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[1;32m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Blue()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_BLUE);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[34m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlueBold()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[1;34m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Cyan()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_BLUE | FOREGROUND_GREEN);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[36m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void CyanBold()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[1;36m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Yellow()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_RED | FOREGROUND_GREEN);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[33m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void YellowBold()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[1;33m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void White()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[37m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void WhiteBold()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[1;37m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void WhiteOnRed()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, BACKGROUND_RED | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[41m\033[37m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void WhiteOnRedBold()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, BACKGROUND_RED | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[41m\033[1;37m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void PurpleBold()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
auto h = WinColor();
|
||||||
|
SetConsoleTextAttribute(h, FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||||
|
#else
|
||||||
|
std::cout << "\033[49m\033[1;38m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Default()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
White();
|
||||||
|
#else
|
||||||
|
std::cout << "\033[0m";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void celero::console::SetConsoleColor(const celero::console::ConsoleColor x)
|
||||||
|
{
|
||||||
|
switch(x)
|
||||||
|
{
|
||||||
|
case console::ConsoleColor::Red:
|
||||||
|
Red();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::Red_Bold:
|
||||||
|
RedBold();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::Green:
|
||||||
|
Green();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::Green_Bold:
|
||||||
|
GreenBold();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::Blue:
|
||||||
|
Blue();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::Blue_Bold:
|
||||||
|
BlueBold();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::Cyan:
|
||||||
|
Cyan();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::Cyan_Bold:
|
||||||
|
CyanBold();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::Yellow:
|
||||||
|
Yellow();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::Yellow_Bold:
|
||||||
|
YellowBold();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::White:
|
||||||
|
White();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::White_Bold:
|
||||||
|
WhiteBold();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::WhiteOnRed:
|
||||||
|
WhiteOnRed();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::WhiteOnRed_Bold:
|
||||||
|
WhiteOnRedBold();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::Purple_Bold:
|
||||||
|
PurpleBold();
|
||||||
|
break;
|
||||||
|
case console::ConsoleColor::Default:
|
||||||
|
default:
|
||||||
|
Default();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
65
lib/celero/Console.h
Normal file
65
lib/celero/Console.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#ifndef H_CELERO_CONSOLE_H
|
||||||
|
#define H_CELERO_CONSOLE_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Export.h>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \namespace console
|
||||||
|
///
|
||||||
|
/// \author John farrier
|
||||||
|
///
|
||||||
|
namespace console
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \enum ConsoleColor
|
||||||
|
///
|
||||||
|
/// \author John farrier
|
||||||
|
///
|
||||||
|
enum class ConsoleColor
|
||||||
|
{
|
||||||
|
Default,
|
||||||
|
Red,
|
||||||
|
Red_Bold,
|
||||||
|
Green,
|
||||||
|
Green_Bold,
|
||||||
|
Blue,
|
||||||
|
Blue_Bold,
|
||||||
|
Cyan,
|
||||||
|
Cyan_Bold,
|
||||||
|
Yellow,
|
||||||
|
Yellow_Bold,
|
||||||
|
White,
|
||||||
|
White_Bold,
|
||||||
|
WhiteOnRed,
|
||||||
|
WhiteOnRed_Bold,
|
||||||
|
Purple_Bold
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Set the color of std::out on the console.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void SetConsoleColor(const celero::console::ConsoleColor x);
|
||||||
|
} // namespace console
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
165
lib/celero/Distribution.cpp
Normal file
165
lib/celero/Distribution.cpp
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Distribution.h>
|
||||||
|
#include <celero/Print.h>
|
||||||
|
#include <celero/Utilities.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
std::vector<uint64_t> celero::BuildDistribution(uint64_t numberOfSamples, uint64_t iterationsPerSample)
|
||||||
|
{
|
||||||
|
std::vector<uint64_t> measurements;
|
||||||
|
|
||||||
|
while(numberOfSamples--)
|
||||||
|
{
|
||||||
|
// Dummy variable
|
||||||
|
auto dummy = uint64_t(0);
|
||||||
|
auto cps = iterationsPerSample;
|
||||||
|
|
||||||
|
// Get the starting time.
|
||||||
|
const auto startTime = celero::timer::GetSystemTime();
|
||||||
|
|
||||||
|
while(cps--)
|
||||||
|
{
|
||||||
|
celero::DoNotOptimizeAway(dummy++);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto endTime = celero::timer::GetSystemTime();
|
||||||
|
|
||||||
|
measurements.push_back(endTime - startTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return measurements;
|
||||||
|
}
|
||||||
|
|
||||||
|
void celero::RunDistribution(uint64_t intArgument)
|
||||||
|
{
|
||||||
|
std::vector<double> series1Normalized(intArgument);
|
||||||
|
std::vector<double> series2Normalized(intArgument);
|
||||||
|
std::vector<double> series3Normalized(intArgument);
|
||||||
|
std::vector<double> series4Normalized(intArgument);
|
||||||
|
|
||||||
|
const auto series1 = celero::BuildDistribution(intArgument, uint64_t(64));
|
||||||
|
const auto series2 = celero::BuildDistribution(intArgument, uint64_t(256));
|
||||||
|
const auto series3 = celero::BuildDistribution(intArgument, uint64_t(1024));
|
||||||
|
const auto series4 = celero::BuildDistribution(intArgument, uint64_t(4096));
|
||||||
|
|
||||||
|
if(series1.empty() == true || series2.empty() == true || series3.empty() == true || series4.empty() == true)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<std::map<double, uint64_t>, 4> histograms;
|
||||||
|
|
||||||
|
// Find the global max for all tests:
|
||||||
|
auto maxVal = std::max(*(std::max_element(std::begin(series1), std::end(series1))), *(std::max_element(std::begin(series2), std::end(series2))));
|
||||||
|
maxVal = std::max(maxVal, *(std::max_element(std::begin(series3), std::end(series3))));
|
||||||
|
maxVal = std::max(maxVal, *(std::max_element(std::begin(series4), std::end(series4))));
|
||||||
|
|
||||||
|
// Find the global min for all tests:
|
||||||
|
auto minVal = std::min(*(std::min_element(std::begin(series1), std::end(series1))), *(std::min_element(std::begin(series2), std::end(series2))));
|
||||||
|
minVal = std::min(minVal, *(std::min_element(std::begin(series3), std::end(series3))));
|
||||||
|
minVal = std::min(minVal, *(std::min_element(std::begin(series4), std::end(series4))));
|
||||||
|
|
||||||
|
// Normalize all vectors:
|
||||||
|
auto normalize = [minVal, maxVal](uint64_t val) -> double {
|
||||||
|
if(val >= minVal)
|
||||||
|
{
|
||||||
|
if(val <= maxVal)
|
||||||
|
{
|
||||||
|
const auto delta = maxVal - minVal;
|
||||||
|
val -= minVal;
|
||||||
|
return static_cast<double>(val) / static_cast<double>(delta);
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<double>(maxVal);
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<double>(minVal);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::transform(std::begin(series1), std::end(series1), std::begin(series1Normalized),
|
||||||
|
[normalize](const uint64_t val) -> double { return normalize(val); });
|
||||||
|
|
||||||
|
std::transform(std::begin(series2), std::end(series2), std::begin(series2Normalized),
|
||||||
|
[normalize](const uint64_t val) -> double { return normalize(val); });
|
||||||
|
|
||||||
|
std::transform(std::begin(series3), std::end(series3), std::begin(series3Normalized),
|
||||||
|
[normalize](const uint64_t val) -> double { return normalize(val); });
|
||||||
|
|
||||||
|
std::transform(std::begin(series4), std::end(series4), std::begin(series4Normalized),
|
||||||
|
[normalize](const uint64_t val) -> double { return normalize(val); });
|
||||||
|
|
||||||
|
// Build histograms of each of the series:
|
||||||
|
std::for_each(std::begin(series1Normalized), std::end(series1Normalized),
|
||||||
|
[&histograms](const double val) { histograms[0][static_cast<int>(val * 1024)]++; });
|
||||||
|
|
||||||
|
std::for_each(std::begin(series2Normalized), std::end(series2Normalized),
|
||||||
|
[&histograms](const double val) { histograms[1][static_cast<int>(val * 1024)]++; });
|
||||||
|
|
||||||
|
std::for_each(std::begin(series3Normalized), std::end(series3Normalized),
|
||||||
|
[&histograms](const double val) { histograms[2][static_cast<int>(val * 1024)]++; });
|
||||||
|
|
||||||
|
std::for_each(std::begin(series4Normalized), std::end(series4Normalized),
|
||||||
|
[&histograms](const double val) { histograms[3][static_cast<int>(val * 1024)]++; });
|
||||||
|
|
||||||
|
// Find the maximum length of all histograms:
|
||||||
|
auto maxLen = size_t(0);
|
||||||
|
maxLen = std::max(maxLen, histograms[0].size());
|
||||||
|
maxLen = std::max(maxLen, histograms[1].size());
|
||||||
|
maxLen = std::max(maxLen, histograms[2].size());
|
||||||
|
maxLen = std::max(maxLen, histograms[3].size());
|
||||||
|
|
||||||
|
// Write out a CSV file that contains all four series:
|
||||||
|
std::ofstream os;
|
||||||
|
os.open("celeroDistribution.csv");
|
||||||
|
|
||||||
|
os << "64,,256,,1024,,4096,," << std::endl;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < maxLen; ++i)
|
||||||
|
{
|
||||||
|
for(size_t j = 0; j < histograms.size(); j++)
|
||||||
|
{
|
||||||
|
if(i < histograms[j].size())
|
||||||
|
{
|
||||||
|
auto element = std::begin(histograms[j]);
|
||||||
|
for(size_t k = 0; k < i; k++)
|
||||||
|
{
|
||||||
|
++element;
|
||||||
|
}
|
||||||
|
|
||||||
|
os << element->first << "," << element->second << ",";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
os << ",,";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
os.close();
|
||||||
|
}
|
39
lib/celero/Distribution.h
Normal file
39
lib/celero/Distribution.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#ifndef H_CELERO_DISTRIBUTION_H
|
||||||
|
#define H_CELERO_DISTRIBUTION_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Export.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// Collects results from Celero for analysis of a hard-coded internal trivial measurement case.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT std::vector<uint64_t> BuildDistribution(uint64_t numberOfSamples, uint64_t iterationsPerSample);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Builds a .csv file to help determine Celero's measurement distribution.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void RunDistribution(uint64_t iterationsPerSample);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
251
lib/celero/Exceptions.cpp
Normal file
251
lib/celero/Exceptions.cpp
Normal file
@ -0,0 +1,251 @@
|
|||||||
|
///
|
||||||
|
/// \author Peter Azmanov
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2016, 2017, 2018 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Exceptions.h>
|
||||||
|
|
||||||
|
#include <celero/Console.h>
|
||||||
|
#include <celero/TestFixture.h>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#endif // WIN32
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
//
|
||||||
|
// Macros and general logics below taken from Google Test code,
|
||||||
|
// see gtest/internal/gtest-port.h
|
||||||
|
// gtest/src/gtest.cc
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef CELERO_HAS_EXCEPTIONS
|
||||||
|
// The user didn't tell us whether exceptions are enabled, so we need
|
||||||
|
// to figure it out.
|
||||||
|
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||||
|
// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS
|
||||||
|
// macro to enable exceptions, so we'll do the same.
|
||||||
|
// Assumes that exceptions are enabled by default.
|
||||||
|
#ifndef _HAS_EXCEPTIONS
|
||||||
|
#define _HAS_EXCEPTIONS 1
|
||||||
|
#endif // _HAS_EXCEPTIONS
|
||||||
|
#define CELERO_HAS_EXCEPTIONS _HAS_EXCEPTIONS
|
||||||
|
#elif defined(__GNUC__) && __EXCEPTIONS
|
||||||
|
// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled.
|
||||||
|
#define CELERO_HAS_EXCEPTIONS 1
|
||||||
|
#elif defined(__SUNPRO_CC)
|
||||||
|
// Sun Pro CC supports exceptions. However, there is no compile-time way of
|
||||||
|
// detecting whether they are enabled or not. Therefore, we assume that
|
||||||
|
// they are enabled unless the user tells us otherwise.
|
||||||
|
#define CELERO_HAS_EXCEPTIONS 1
|
||||||
|
#elif defined(__IBMCPP__) && __EXCEPTIONS
|
||||||
|
// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled.
|
||||||
|
#define CELERO_HAS_EXCEPTIONS 1
|
||||||
|
#elif defined(__HP_aCC)
|
||||||
|
// Exception handling is in effect by default in HP aCC compiler. It has to
|
||||||
|
// be turned of by +noeh compiler option if desired.
|
||||||
|
#define CELERO_HAS_EXCEPTIONS 1
|
||||||
|
#else
|
||||||
|
// For other compilers, we assume exceptions are disabled to be
|
||||||
|
// conservative.
|
||||||
|
#define CELERO_HAS_EXCEPTIONS 0
|
||||||
|
#endif // defined(_MSC_VER) || defined(__BORLANDC__)
|
||||||
|
#endif // CELERO_HAS_EXCEPTIONS
|
||||||
|
|
||||||
|
// Determine whether the compiler supports Microsoft's Structured Exception
|
||||||
|
// Handling. This is supported by several Windows compilers but generally
|
||||||
|
// does not exist on any other system.
|
||||||
|
#ifndef CELERO_HAS_SEH
|
||||||
|
// The user didn't tell us, so we need to figure it out.
|
||||||
|
|
||||||
|
#if defined(_MSC_VER) || defined(__BORLANDC__)
|
||||||
|
// These two compilers are known to support SEH.
|
||||||
|
#define CELERO_HAS_SEH 1
|
||||||
|
#else
|
||||||
|
// Assume no SEH.
|
||||||
|
#define CELERO_HAS_SEH 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CELERO_HAS_SEH
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
bool ExceptionSettings::GetCatchExceptions()
|
||||||
|
{
|
||||||
|
return ExceptionSettings::instance().catchExceptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExceptionSettings::SetCatchExceptions(bool x)
|
||||||
|
{
|
||||||
|
ExceptionSettings::instance().catchExceptions = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExceptionSettings& ExceptionSettings::instance()
|
||||||
|
{
|
||||||
|
static ExceptionSettings settings;
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CELERO_HAS_SEH
|
||||||
|
int HandleSEH(DWORD exceptionCode)
|
||||||
|
{
|
||||||
|
// see https://support.microsoft.com/en-us/kb/185294
|
||||||
|
const DWORD cppExceptionCode = 0xe06d7363;
|
||||||
|
|
||||||
|
if((exceptionCode == EXCEPTION_BREAKPOINT) || (exceptionCode == cppExceptionCode))
|
||||||
|
{
|
||||||
|
return EXCEPTION_EXECUTE_HANDLER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ExceptionCodeToStr(DWORD exceptionCode)
|
||||||
|
{
|
||||||
|
switch(exceptionCode)
|
||||||
|
{
|
||||||
|
case EXCEPTION_ACCESS_VIOLATION:
|
||||||
|
return "EXCEPTION_ACCESS_VIOLATION";
|
||||||
|
|
||||||
|
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||||
|
return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
|
||||||
|
|
||||||
|
case EXCEPTION_BREAKPOINT:
|
||||||
|
return "EXCEPTION_BREAKPOINT";
|
||||||
|
|
||||||
|
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||||
|
return "EXCEPTION_DATATYPE_MISALIGNMENT";
|
||||||
|
|
||||||
|
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||||
|
return "EXCEPTION_FLT_DENORMAL_OPERAND";
|
||||||
|
|
||||||
|
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||||
|
return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
|
||||||
|
|
||||||
|
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||||
|
return "EXCEPTION_FLT_INEXACT_RESULT";
|
||||||
|
|
||||||
|
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||||
|
return "EXCEPTION_FLT_INVALID_OPERATION";
|
||||||
|
|
||||||
|
case EXCEPTION_FLT_OVERFLOW:
|
||||||
|
return "EXCEPTION_FLT_OVERFLOW";
|
||||||
|
|
||||||
|
case EXCEPTION_FLT_STACK_CHECK:
|
||||||
|
return "EXCEPTION_FLT_STACK_CHECK";
|
||||||
|
|
||||||
|
case EXCEPTION_FLT_UNDERFLOW:
|
||||||
|
return "EXCEPTION_FLT_UNDERFLOW";
|
||||||
|
|
||||||
|
case EXCEPTION_GUARD_PAGE:
|
||||||
|
return "EXCEPTION_GUARD_PAGE";
|
||||||
|
|
||||||
|
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||||
|
return "EXCEPTION_ILLEGAL_INSTRUCTION";
|
||||||
|
|
||||||
|
case EXCEPTION_IN_PAGE_ERROR:
|
||||||
|
return "EXCEPTION_IN_PAGE_ERROR";
|
||||||
|
|
||||||
|
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||||
|
return "EXCEPTION_INT_DIVIDE_BY_ZERO";
|
||||||
|
|
||||||
|
case EXCEPTION_INT_OVERFLOW:
|
||||||
|
return "EXCEPTION_INT_OVERFLOW";
|
||||||
|
|
||||||
|
case EXCEPTION_INVALID_DISPOSITION:
|
||||||
|
return "EXCEPTION_INVALID_DISPOSITION";
|
||||||
|
|
||||||
|
case EXCEPTION_INVALID_HANDLE:
|
||||||
|
return "EXCEPTION_INVALID_HANDLE";
|
||||||
|
|
||||||
|
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||||
|
return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
|
||||||
|
|
||||||
|
case EXCEPTION_PRIV_INSTRUCTION:
|
||||||
|
return "EXCEPTION_PRIV_INSTRUCTION";
|
||||||
|
|
||||||
|
case EXCEPTION_SINGLE_STEP:
|
||||||
|
return "EXCEPTION_SINGLE_STEP";
|
||||||
|
|
||||||
|
case EXCEPTION_STACK_OVERFLOW:
|
||||||
|
return "EXCEPTION_STACK_OVERFLOW";
|
||||||
|
|
||||||
|
case STATUS_UNWIND_CONSOLIDATE:
|
||||||
|
return "STATUS_UNWIND_CONSOLIDATE";
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "Unknown exception code.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // CELERO_HAS_SEH
|
||||||
|
|
||||||
|
std::pair<bool, uint64_t> RunAndCatchSEHExc(TestFixture& test, uint64_t threads, uint64_t calls,
|
||||||
|
const celero::TestFixture::ExperimentValue& experimentValue)
|
||||||
|
{
|
||||||
|
#if CELERO_HAS_SEH
|
||||||
|
__try
|
||||||
|
{
|
||||||
|
return std::make_pair(true, test.run(threads, calls, experimentValue));
|
||||||
|
}
|
||||||
|
__except(HandleSEH(GetExceptionCode()))
|
||||||
|
{
|
||||||
|
const auto exceptionCode = GetExceptionCode();
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Red);
|
||||||
|
std::cout << "SEH exception " << ExceptionCodeToStr(exceptionCode) << std::endl;
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Default);
|
||||||
|
return std::make_pair(false, 0);
|
||||||
|
}
|
||||||
|
#else // CELERO_HAS_SEH
|
||||||
|
return std::make_pair(true, test.run(threads, calls, experimentValue));
|
||||||
|
#endif // CELERO_HAS_SEH
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<bool, uint64_t> RunAndCatchExc(TestFixture& test, uint64_t threads, uint64_t calls,
|
||||||
|
const celero::TestFixture::ExperimentValue& experimentValue)
|
||||||
|
{
|
||||||
|
if(ExceptionSettings::GetCatchExceptions() == true)
|
||||||
|
{
|
||||||
|
#if CELERO_HAS_EXCEPTIONS
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return RunAndCatchSEHExc(test, threads, calls, experimentValue);
|
||||||
|
}
|
||||||
|
catch(const std::exception& e)
|
||||||
|
{
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Red);
|
||||||
|
std::cout << "C++ exception \"" << e.what() << "\"" << std::endl;
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Default);
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Red);
|
||||||
|
std::cout << "Unknown C++ exception" << std::endl;
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(false, 0);
|
||||||
|
#else // CELERO_HAS_EXCEPTIONS
|
||||||
|
return RunAndCatchSEHExc(test, threads, calls, experimentValue);
|
||||||
|
#endif // CELERO_HAS_EXCEPTIONS
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return std::make_pair(true, test.run(threads, calls, experimentValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace celero
|
64
lib/celero/Exceptions.h
Normal file
64
lib/celero/Exceptions.h
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#ifndef H_CELERO_EXCEPTIONS_H
|
||||||
|
#define H_CELERO_EXCEPTIONS_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author Peter Azmanov
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/TestFixture.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// A Singleton storing exception settings (currently only one flag)
|
||||||
|
///
|
||||||
|
class ExceptionSettings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Get a flag indicating whether Celero should catch exceptions or not
|
||||||
|
///
|
||||||
|
static bool GetCatchExceptions();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Set a flag indicating whether Celero should catch exceptions or not
|
||||||
|
///
|
||||||
|
static void SetCatchExceptions(bool catchExceptions);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static ExceptionSettings& instance();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool catchExceptions{true};
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run test and catch SEH exceptions if they are supported by compiler
|
||||||
|
///
|
||||||
|
std::pair<bool, uint64_t> RunAndCatchSEHExc(TestFixture& test, uint64_t threads, uint64_t calls,
|
||||||
|
const celero::TestFixture::ExperimentValue& experimentValue);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run test and catch all exceptions we can
|
||||||
|
///
|
||||||
|
std::pair<bool, uint64_t> RunAndCatchExc(TestFixture& test, uint64_t threads, uint64_t calls,
|
||||||
|
const celero::TestFixture::ExperimentValue& experimentValue);
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
399
lib/celero/Executor.cpp
Normal file
399
lib/celero/Executor.cpp
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Benchmark.h>
|
||||||
|
#include <celero/Callbacks.h>
|
||||||
|
#include <celero/Celero.h>
|
||||||
|
#include <celero/Console.h>
|
||||||
|
#include <celero/Exceptions.h>
|
||||||
|
#include <celero/Executor.h>
|
||||||
|
#include <celero/Print.h>
|
||||||
|
#include <celero/TestVector.h>
|
||||||
|
#include <celero/UserDefinedMeasurementCollector.h>
|
||||||
|
#include <celero/Utilities.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// A local function to figure out how many iterations and samples are required when the user doesn't specify any.
|
||||||
|
///
|
||||||
|
bool AdjustSampleAndIterationSize(std::shared_ptr<celero::ExperimentResult> r)
|
||||||
|
{
|
||||||
|
if((r->getExperiment()->getSamples() == 0) || (r->getExperiment()->getIterations() == 0))
|
||||||
|
{
|
||||||
|
// The smallest test should take at least 10x as long as our timer's resolution.
|
||||||
|
// I chose "2x" arbitrarily.
|
||||||
|
const auto minTestTime = static_cast<int64_t>(celero::timer::CachePerformanceFrequency(true) * 1e6) * 2;
|
||||||
|
|
||||||
|
// Compute a good number to use for iterations and set the sample size to 30.
|
||||||
|
auto test = r->getExperiment()->getFactory()->Create();
|
||||||
|
auto testTime = int64_t(0);
|
||||||
|
auto testIterations = int64_t(1);
|
||||||
|
|
||||||
|
while(testTime < minTestTime)
|
||||||
|
{
|
||||||
|
const auto runResult = RunAndCatchExc(*test, r->getExperiment()->getThreads(), testIterations, r->getProblemSpaceValue());
|
||||||
|
|
||||||
|
if(runResult.first == false)
|
||||||
|
{
|
||||||
|
return false; // something bad happened
|
||||||
|
}
|
||||||
|
|
||||||
|
testTime = runResult.second;
|
||||||
|
|
||||||
|
if(testTime < minTestTime)
|
||||||
|
{
|
||||||
|
testIterations *= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto iterations = static_cast<uint64_t>(std::max(static_cast<double>(testIterations), 1000000.0 / testTime));
|
||||||
|
auto experiment = r->getExperiment();
|
||||||
|
|
||||||
|
if(experiment->getIterations() == 0)
|
||||||
|
{
|
||||||
|
experiment->setIterations(std::max(iterations, uint64_t(30)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(experiment->getSamples() == 0)
|
||||||
|
{
|
||||||
|
experiment->setSamples(30);
|
||||||
|
}
|
||||||
|
|
||||||
|
r->setProblemSpaceValue(r->getProblemSpaceValue(), r->getProblemSpaceValueScale(), iterations);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// A local function to support running an individual user-defined function for measurement.
|
||||||
|
///
|
||||||
|
bool ExecuteProblemSpace(std::shared_ptr<celero::ExperimentResult> r)
|
||||||
|
{
|
||||||
|
// Define a small internal function object to use to uniformly execute the tests.
|
||||||
|
auto testRunner = [r](const bool record, std::shared_ptr<UserDefinedMeasurementCollector> udmCollector) {
|
||||||
|
auto test = r->getExperiment()->getFactory()->Create();
|
||||||
|
|
||||||
|
const auto runResult = RunAndCatchExc(*test, r->getExperiment()->getThreads(), r->getProblemSpaceIterations(), r->getProblemSpaceValue());
|
||||||
|
|
||||||
|
if(runResult.first == false)
|
||||||
|
{
|
||||||
|
// something bad happened
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto testTime = runResult.second;
|
||||||
|
|
||||||
|
// Save test results
|
||||||
|
if(record == true)
|
||||||
|
{
|
||||||
|
r->getTimeStatistics()->addSample(testTime);
|
||||||
|
r->getExperiment()->incrementTotalRunTime(testTime);
|
||||||
|
|
||||||
|
if(udmCollector != nullptr)
|
||||||
|
{
|
||||||
|
udmCollector->collect(test);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
if(r->getExperiment()->getSamples() > 0)
|
||||||
|
{
|
||||||
|
// make a first pass to maybe cache instructions/data or other kinds of fist-run-only costs
|
||||||
|
if(testRunner(false, nullptr) == false)
|
||||||
|
{
|
||||||
|
r->setFailure(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto udmCollector = std::make_shared<UserDefinedMeasurementCollector>(r->getExperiment()->getFactory()->Create());
|
||||||
|
|
||||||
|
for(auto i = r->getExperiment()->getSamples(); i > 0; --i)
|
||||||
|
{
|
||||||
|
if(testRunner(true, udmCollector) == false)
|
||||||
|
{
|
||||||
|
r->setFailure(true);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r->setUserDefinedMeasurements(udmCollector);
|
||||||
|
r->setComplete(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "Celero: Test \"" << r->getExperiment()->getBenchmark()->getName() << "::" << r->getExperiment()->getName()
|
||||||
|
<< "\" must have at least 1 sample." << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::RunAll()
|
||||||
|
{
|
||||||
|
executor::RunAllBaselines();
|
||||||
|
executor::RunAllExperiments();
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::RunAllBaselines()
|
||||||
|
{
|
||||||
|
// Run through all the tests in ascending order.
|
||||||
|
for(size_t i = 0; i < celero::TestVector::Instance().size(); i++)
|
||||||
|
{
|
||||||
|
auto bmark = celero::TestVector::Instance()[i];
|
||||||
|
executor::RunBaseline(bmark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::RunAllExperiments()
|
||||||
|
{
|
||||||
|
// Run through all the tests in ascending order.
|
||||||
|
for(size_t i = 0; i < celero::TestVector::Instance().size(); i++)
|
||||||
|
{
|
||||||
|
auto bmark = celero::TestVector::Instance()[i];
|
||||||
|
executor::RunExperiments(bmark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::RunBaseline(std::shared_ptr<Benchmark> bmark)
|
||||||
|
{
|
||||||
|
if(bmark == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto baselineExperiment = bmark->getBaseline();
|
||||||
|
|
||||||
|
if(baselineExperiment != nullptr)
|
||||||
|
{
|
||||||
|
// Populate the problem space with a test fixture instantiation.
|
||||||
|
{
|
||||||
|
const auto testValues = baselineExperiment->getFactory()->Create()->getExperimentValues();
|
||||||
|
const auto valueResultScale = baselineExperiment->getFactory()->Create()->getExperimentValueResultScale();
|
||||||
|
|
||||||
|
for(auto i : testValues)
|
||||||
|
{
|
||||||
|
if(i.Iterations > 0)
|
||||||
|
{
|
||||||
|
baselineExperiment->addProblemSpace(i.Value, static_cast<double>(valueResultScale), i.Iterations);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
baselineExperiment->addProblemSpace(i.Value, static_cast<double>(valueResultScale), baselineExperiment->getIterations());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a single default problem space if none was specified.
|
||||||
|
// This is needed to get the result size later.
|
||||||
|
if(baselineExperiment->getResultSize() == 0)
|
||||||
|
{
|
||||||
|
baselineExperiment->addProblemSpace(static_cast<int64_t>(TestFixture::Constants::NoProblemSpaceValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < baselineExperiment->getResultSize(); i++)
|
||||||
|
{
|
||||||
|
auto r = baselineExperiment->getResult(i);
|
||||||
|
assert(r != nullptr);
|
||||||
|
|
||||||
|
Printer::get().TableRowExperimentHeader(r->getExperiment());
|
||||||
|
|
||||||
|
// Do a quick sample, if necessary, and adjust sample and iteration sizes, if necessary.
|
||||||
|
if(AdjustSampleAndIterationSize(r) == true)
|
||||||
|
{
|
||||||
|
// Describe the beginning of the run.
|
||||||
|
Printer::get().TableRowProblemSpaceHeader(r);
|
||||||
|
|
||||||
|
if(ExecuteProblemSpace(r))
|
||||||
|
{
|
||||||
|
// Describe the end of the run.
|
||||||
|
Printer::get().TableResult(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r->setFailure(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
celero::impl::ExperimentResultComplete(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
celero::impl::ExperimentComplete(baselineExperiment);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "No Baseline case defined for \"" + bmark->getName() + "\". Exiting.";
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::RunExperiments(std::shared_ptr<Benchmark> bmark)
|
||||||
|
{
|
||||||
|
if(bmark == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto experimentSize = bmark->getExperimentSize();
|
||||||
|
|
||||||
|
for(size_t i = 0; i < experimentSize; i++)
|
||||||
|
{
|
||||||
|
auto e = bmark->getExperiment(i);
|
||||||
|
assert(e != nullptr);
|
||||||
|
|
||||||
|
executor::Run(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::Run(std::shared_ptr<Experiment> e)
|
||||||
|
{
|
||||||
|
if(e == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bmark = e->getBenchmark();
|
||||||
|
auto baseline = bmark->getBaseline();
|
||||||
|
|
||||||
|
if(baseline->getResultSize() == 0 || baseline->getResult(0)->getComplete() == false)
|
||||||
|
{
|
||||||
|
if(baseline->getResultSize() != 0 && baseline->getResult(0)->getFailure())
|
||||||
|
{
|
||||||
|
Printer::get().TableRowExperimentHeader(e.get());
|
||||||
|
Printer::get().TableRowFailure("Baseline failure, skip");
|
||||||
|
|
||||||
|
// Add result output failed result
|
||||||
|
e->addProblemSpace(0);
|
||||||
|
|
||||||
|
auto r = e->getResult(0);
|
||||||
|
r->setFailure(true);
|
||||||
|
celero::impl::ExperimentResultComplete(r);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
executor::RunBaseline(bmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate the problem space with a fake test fixture instantiation.
|
||||||
|
{
|
||||||
|
auto factory = e->getFactory();
|
||||||
|
|
||||||
|
if(factory == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto factoryCreate = factory->Create();
|
||||||
|
|
||||||
|
if(factoryCreate == nullptr)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto testValues = factoryCreate->getExperimentValues();
|
||||||
|
|
||||||
|
factoryCreate = factory->Create();
|
||||||
|
const auto valueResultScale = factoryCreate->getExperimentValueResultScale();
|
||||||
|
|
||||||
|
for(auto i : testValues)
|
||||||
|
{
|
||||||
|
if(i.Iterations > 0)
|
||||||
|
{
|
||||||
|
e->addProblemSpace(i.Value, valueResultScale, i.Iterations);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
e->addProblemSpace(i.Value, valueResultScale, e->getIterations());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a single default problem space if none was specified.
|
||||||
|
// This is needed to get the result size later.
|
||||||
|
if(e->getResultSize() == 0)
|
||||||
|
{
|
||||||
|
e->addProblemSpace(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Result size will grow based on the problem spaces added above.
|
||||||
|
for(size_t i = 0; i < e->getResultSize(); i++)
|
||||||
|
{
|
||||||
|
auto r = e->getResult(i);
|
||||||
|
|
||||||
|
Printer::get().TableRowExperimentHeader(r->getExperiment());
|
||||||
|
|
||||||
|
// Do a quick sample, if necessary, and adjust sample and iteration sizes, if necessary.
|
||||||
|
const auto adjustSuccess = AdjustSampleAndIterationSize(r);
|
||||||
|
|
||||||
|
if(adjustSuccess == true)
|
||||||
|
{
|
||||||
|
// Describe the beginning of the run.
|
||||||
|
Printer::get().TableRowProblemSpaceHeader(r);
|
||||||
|
|
||||||
|
if(ExecuteProblemSpace(r))
|
||||||
|
{
|
||||||
|
// Describe the end of the run.
|
||||||
|
Printer::get().TableResult(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r->setFailure(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
celero::impl::ExperimentResultComplete(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
celero::impl::ExperimentComplete(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::Run(std::shared_ptr<Benchmark> bmark)
|
||||||
|
{
|
||||||
|
executor::RunBaseline(bmark);
|
||||||
|
executor::RunExperiments(bmark);
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::Run(const std::string& benchmarkName)
|
||||||
|
{
|
||||||
|
auto bmark = celero::TestVector::Instance()[benchmarkName];
|
||||||
|
|
||||||
|
if(bmark != nullptr)
|
||||||
|
{
|
||||||
|
executor::Run(bmark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void executor::Run(const std::string& benchmarkName, const std::string& experimentName)
|
||||||
|
{
|
||||||
|
auto bmark = celero::TestVector::Instance()[benchmarkName];
|
||||||
|
|
||||||
|
if(bmark != nullptr)
|
||||||
|
{
|
||||||
|
auto e = bmark->getExperiment(experimentName);
|
||||||
|
assert(e != nullptr);
|
||||||
|
executor::Run(e);
|
||||||
|
}
|
||||||
|
}
|
82
lib/celero/Executor.h
Normal file
82
lib/celero/Executor.h
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
#ifndef H_CELERO_EXECUTOR_H
|
||||||
|
#define H_CELERO_EXECUTOR_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Benchmark.h>
|
||||||
|
#include <celero/Export.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
namespace executor
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// Run all baselines and experiments registered within the final application.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void RunAll();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run all baselines (but not experiments) registered within the final application.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void RunAllBaselines();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run a specific benchmark's baseline.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void RunBaseline(std::shared_ptr<Benchmark> x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run all experiments registered within the final application.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void RunAllExperiments();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run all experiments within a specific benchmark.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void RunExperiments(std::shared_ptr<Benchmark> x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run a specific benchmark.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void Run(std::shared_ptr<Benchmark> x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run a specific experiment.
|
||||||
|
///
|
||||||
|
/// If the baseline is not complete for the given experiment, it will be executed first.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void Run(std::shared_ptr<Experiment> x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run a specific benchmark with the specified name.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void Run(const std::string& group);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Run a specific benchmark with the specified name and one specific experiment within it.
|
||||||
|
///
|
||||||
|
/// If the baseline is not complete for the given experiment, it will be executed first.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT void Run(const std::string& group, const std::string& experiment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
315
lib/celero/Experiment.cpp
Normal file
315
lib/celero/Experiment.cpp
Normal file
@ -0,0 +1,315 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Benchmark.h>
|
||||||
|
#include <celero/Experiment.h>
|
||||||
|
#include <celero/Factory.h>
|
||||||
|
#include <celero/PimplImpl.h>
|
||||||
|
#include <celero/TestFixture.h>
|
||||||
|
#include <celero/Utilities.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
class Experiment::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl() :
|
||||||
|
results(),
|
||||||
|
benchmark(),
|
||||||
|
factory(),
|
||||||
|
name(),
|
||||||
|
baselineUnit(0),
|
||||||
|
baselineTarget(0),
|
||||||
|
samples(0),
|
||||||
|
iterations(0),
|
||||||
|
threads(1),
|
||||||
|
totalRunTime(0),
|
||||||
|
isBaselineCase(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Impl(std::weak_ptr<Benchmark> bm, const std::string& n, const uint64_t s, const uint64_t c, const uint64_t t, const double pBaselineTarget) :
|
||||||
|
results(),
|
||||||
|
benchmark(bm),
|
||||||
|
factory(),
|
||||||
|
name(n),
|
||||||
|
baselineUnit(0),
|
||||||
|
baselineTarget(pBaselineTarget),
|
||||||
|
samples(s),
|
||||||
|
iterations(c),
|
||||||
|
threads(t),
|
||||||
|
totalRunTime(0),
|
||||||
|
isBaselineCase(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Impl(std::weak_ptr<Benchmark> bm) :
|
||||||
|
results(),
|
||||||
|
benchmark(bm),
|
||||||
|
factory(),
|
||||||
|
name(),
|
||||||
|
baselineUnit(0),
|
||||||
|
baselineTarget(0),
|
||||||
|
samples(0),
|
||||||
|
iterations(0),
|
||||||
|
threads(1),
|
||||||
|
totalRunTime(0),
|
||||||
|
isBaselineCase(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// There is one result for each problem space value.
|
||||||
|
/// In the event there are not any problem spaces, there shal be a single result.
|
||||||
|
std::vector<std::shared_ptr<celero::ExperimentResult>> results;
|
||||||
|
|
||||||
|
/// The owning benchmark object which groups together all experiments.
|
||||||
|
std::weak_ptr<Benchmark> benchmark;
|
||||||
|
|
||||||
|
/// The factory to associate with this benchmark.
|
||||||
|
std::shared_ptr<Factory> factory;
|
||||||
|
|
||||||
|
/// The name of this experiment.
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
/// The number of microseconds per test (which makes up one baseline unit).
|
||||||
|
double baselineUnit{0};
|
||||||
|
|
||||||
|
/// Used to pass/fail benchmarks when outputting JUnit.
|
||||||
|
double baselineTarget{0};
|
||||||
|
|
||||||
|
/// Test samples to complete.
|
||||||
|
uint64_t samples{0};
|
||||||
|
|
||||||
|
/// Iterations per test run. (Size of each sample.)
|
||||||
|
uint64_t iterations{0};
|
||||||
|
|
||||||
|
/// Threads per test run.
|
||||||
|
uint64_t threads{0};
|
||||||
|
|
||||||
|
/// The best run time for this test
|
||||||
|
uint64_t totalRunTime{0};
|
||||||
|
|
||||||
|
bool isBaselineCase{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
Experiment::Experiment() : pimpl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Experiment::Experiment(std::weak_ptr<Benchmark> benchmark) : pimpl(benchmark)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Experiment::Experiment(std::weak_ptr<Benchmark> benchmark, const std::string& name, uint64_t samples, uint64_t iterations, uint64_t threads,
|
||||||
|
double baselineTarget) :
|
||||||
|
pimpl(benchmark, name, samples, iterations, threads, baselineTarget)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Experiment::Experiment(const Experiment&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Experiment::~Experiment()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Benchmark> Experiment::getBenchmark()
|
||||||
|
{
|
||||||
|
return this->pimpl->benchmark.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Experiment::setName(const std::string& x)
|
||||||
|
{
|
||||||
|
this->pimpl->name = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Experiment::getName() const
|
||||||
|
{
|
||||||
|
return this->pimpl->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Experiment::setSamples(uint64_t x)
|
||||||
|
{
|
||||||
|
this->pimpl->samples = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Experiment::getSamples() const
|
||||||
|
{
|
||||||
|
return this->pimpl->samples;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Experiment::setIterations(uint64_t x)
|
||||||
|
{
|
||||||
|
this->pimpl->iterations = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Experiment::getIterations() const
|
||||||
|
{
|
||||||
|
return this->pimpl->iterations;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Experiment::setThreads(uint64_t x)
|
||||||
|
{
|
||||||
|
this->pimpl->threads = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Experiment::getThreads() const
|
||||||
|
{
|
||||||
|
return this->pimpl->threads;
|
||||||
|
}
|
||||||
|
|
||||||
|
Experiment::operator std::string() const
|
||||||
|
{
|
||||||
|
auto output = this->getShort();
|
||||||
|
|
||||||
|
if(this->getSamples() > 0)
|
||||||
|
{
|
||||||
|
output += " -- " + std::to_string(this->getSamples());
|
||||||
|
|
||||||
|
if(this->getSamples() == 1)
|
||||||
|
{
|
||||||
|
output += " run, ";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output += " samples, ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output += " -- Auto Run, ";
|
||||||
|
}
|
||||||
|
|
||||||
|
output += std::to_string(this->getIterations());
|
||||||
|
|
||||||
|
if(this->getIterations() == 1)
|
||||||
|
{
|
||||||
|
output += " iteration per run,";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output += " iterations per run,";
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->getThreads() == 1)
|
||||||
|
{
|
||||||
|
output += " thread per run.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
output += " threads per run.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Experiment::getShort() const
|
||||||
|
{
|
||||||
|
auto bm = this->pimpl->benchmark.lock();
|
||||||
|
|
||||||
|
if(bm != nullptr)
|
||||||
|
{
|
||||||
|
return bm->getName() + "." + this->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Experiment::setBaselineTarget(double x)
|
||||||
|
{
|
||||||
|
this->pimpl->baselineTarget = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
double Experiment::getBaselineTarget() const
|
||||||
|
{
|
||||||
|
return this->pimpl->baselineTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Experiment::incrementTotalRunTime(const uint64_t x)
|
||||||
|
{
|
||||||
|
this->pimpl->totalRunTime += x;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t Experiment::getTotalRunTime() const
|
||||||
|
{
|
||||||
|
return this->pimpl->totalRunTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Experiment::setIsBaselineCase(bool x)
|
||||||
|
{
|
||||||
|
this->pimpl->isBaselineCase = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Experiment::getIsBaselineCase() const
|
||||||
|
{
|
||||||
|
return this->pimpl->isBaselineCase;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Experiment::setFactory(std::shared_ptr<Factory> x)
|
||||||
|
{
|
||||||
|
this->pimpl->factory = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Factory> Experiment::getFactory() const
|
||||||
|
{
|
||||||
|
return this->pimpl->factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Experiment::addProblemSpace(int64_t x, double scale, uint64_t iterations)
|
||||||
|
{
|
||||||
|
auto r = std::make_shared<celero::ExperimentResult>(this);
|
||||||
|
r->setProblemSpaceValue(x, scale, iterations);
|
||||||
|
this->pimpl->results.push_back(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Experiment::getResultSize()
|
||||||
|
{
|
||||||
|
if(this->pimpl->results.empty() == true)
|
||||||
|
{
|
||||||
|
auto defaultResult = std::make_shared<celero::ExperimentResult>(this);
|
||||||
|
defaultResult->setProblemSpaceValue(static_cast<int64_t>(TestFixture::Constants::NoProblemSpaceValue), 1.0, this->getIterations());
|
||||||
|
this->pimpl->results.push_back(defaultResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->pimpl->results.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<celero::ExperimentResult> Experiment::getResult(size_t x)
|
||||||
|
{
|
||||||
|
// get the result OR thrown an exception if the result list is empty;
|
||||||
|
return this->pimpl->results.at(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<celero::ExperimentResult> Experiment::getResultByValue(int64_t x)
|
||||||
|
{
|
||||||
|
std::shared_ptr<celero::ExperimentResult> r;
|
||||||
|
|
||||||
|
const auto found = std::find_if(std::begin(this->pimpl->results), std::end(this->pimpl->results),
|
||||||
|
[x](std::shared_ptr<celero::ExperimentResult> i) -> bool { return (i->getProblemSpaceValue() == x); });
|
||||||
|
|
||||||
|
if(found != std::end(this->pimpl->results))
|
||||||
|
{
|
||||||
|
r = (*found);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
197
lib/celero/Experiment.h
Normal file
197
lib/celero/Experiment.h
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
#ifndef H_CELERO_EXPERIMENT_H
|
||||||
|
#define H_CELERO_EXPERIMENT_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/ExperimentResult.h>
|
||||||
|
#include <celero/Factory.h>
|
||||||
|
#include <celero/Statistics.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
class Benchmark;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \class Experiment
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
class CELERO_EXPORT Experiment
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
explicit Experiment(std::weak_ptr<celero::Benchmark> benchmark);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
explicit Experiment(std::weak_ptr<celero::Benchmark> benchmark, const std::string& name, uint64_t samples, uint64_t iterations,
|
||||||
|
uint64_t threads,
|
||||||
|
double baselineTarget);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Default destructor.
|
||||||
|
///
|
||||||
|
~Experiment();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets a pointer to the owning Benchmark object.
|
||||||
|
///
|
||||||
|
std::shared_ptr<celero::Benchmark> getBenchmark();
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
void setName(const std::string& x);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
std::string getName() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
void setSamples(uint64_t x);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
uint64_t getSamples() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
void setIterations(uint64_t x);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
uint64_t getIterations() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
void setThreads(uint64_t x);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
uint64_t getThreads() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
operator std::string() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
std::string getShort() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
void setBaselineTarget(double x);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
double getBaselineTarget() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
void incrementTotalRunTime(const uint64_t x);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
uint64_t getTotalRunTime() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Used to set a flag indicating that this is a Baseline case, not a benchmark case.
|
||||||
|
///
|
||||||
|
void setIsBaselineCase(bool x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Used to get a flag indicating that this is a Baseline case, not a benchmark case.
|
||||||
|
///
|
||||||
|
bool getIsBaselineCase() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Sets the factory used to create this experiment's test fixtures.
|
||||||
|
///
|
||||||
|
void setFactory(std::shared_ptr<celero::Factory> x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets the factory used to create this experiment's test fixtures.
|
||||||
|
///
|
||||||
|
std::shared_ptr<celero::Factory> getFactory() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \param x Can be interpreted in any way be the test fixture (i.e. index into an array, etc.)
|
||||||
|
/// \param scale Used to format unit results.
|
||||||
|
/// \param iterations Override the default iterations with the number here when greater than zero.
|
||||||
|
///
|
||||||
|
void addProblemSpace(int64_t x, double scale = 1.0, uint64_t iterations = 0);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// There is one result for each problem space.
|
||||||
|
///
|
||||||
|
size_t getResultSize();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Get an ExperimentResult at a given index.
|
||||||
|
///
|
||||||
|
std::shared_ptr<celero::ExperimentResult> getResult(size_t x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Get the ExperimentResult for the given problem space value.
|
||||||
|
///
|
||||||
|
std::shared_ptr<celero::ExperimentResult> getResultByValue(int64_t x);
|
||||||
|
|
||||||
|
private:
|
||||||
|
///
|
||||||
|
/// Hide the default constructor
|
||||||
|
///
|
||||||
|
Experiment();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Hide the copy constructor
|
||||||
|
///
|
||||||
|
explicit Experiment(const celero::Experiment&);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
class Impl;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
Pimpl<Impl> pimpl;
|
||||||
|
};
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
206
lib/celero/ExperimentResult.cpp
Normal file
206
lib/celero/ExperimentResult.cpp
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Benchmark.h>
|
||||||
|
#include <celero/Experiment.h>
|
||||||
|
#include <celero/ExperimentResult.h>
|
||||||
|
#include <celero/PimplImpl.h>
|
||||||
|
#include <celero/Statistics.h>
|
||||||
|
#include <celero/Timer.h>
|
||||||
|
#include <celero/UserDefinedMeasurementCollector.h>
|
||||||
|
#include <celero/Utilities.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
class ExperimentResult::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit Impl(Experiment* const p) : parent(p)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Track statistics related to execution time about this specific experiment.
|
||||||
|
Statistics<int64_t> statsTime;
|
||||||
|
Statistics<int64_t> statsRAM;
|
||||||
|
|
||||||
|
std::shared_ptr<UserDefinedMeasurementCollector> udmCollector;
|
||||||
|
|
||||||
|
int64_t problemSpaceValue{0};
|
||||||
|
double problemSpaceValueScale{1.0};
|
||||||
|
uint64_t problemSpaceIterations{0};
|
||||||
|
|
||||||
|
/// A pointer back to our owning Experiment parent.
|
||||||
|
Experiment* parent{nullptr};
|
||||||
|
|
||||||
|
/// A "completed" flag.
|
||||||
|
bool complete{false};
|
||||||
|
|
||||||
|
/// A "failure" flag.
|
||||||
|
bool failure{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
ExperimentResult::ExperimentResult()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ExperimentResult::ExperimentResult(Experiment* x) : pimpl(x)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ExperimentResult::~ExperimentResult()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Experiment* ExperimentResult::getExperiment() const
|
||||||
|
{
|
||||||
|
return this->pimpl->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExperimentResult::setProblemSpaceValue(int64_t x, double scale, uint64_t iterations)
|
||||||
|
{
|
||||||
|
this->pimpl->problemSpaceValue = x;
|
||||||
|
this->pimpl->problemSpaceValueScale = scale;
|
||||||
|
this->pimpl->problemSpaceIterations = iterations;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t ExperimentResult::getProblemSpaceValue() const
|
||||||
|
{
|
||||||
|
return this->pimpl->problemSpaceValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ExperimentResult::getProblemSpaceValueScale() const
|
||||||
|
{
|
||||||
|
return this->pimpl->problemSpaceValueScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ExperimentResult::getProblemSpaceIterations() const
|
||||||
|
{
|
||||||
|
return this->pimpl->problemSpaceIterations;
|
||||||
|
}
|
||||||
|
|
||||||
|
Statistics<int64_t>* ExperimentResult::getTimeStatistics()
|
||||||
|
{
|
||||||
|
return &this->pimpl->statsTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExperimentResult::addRunTimeSample(const uint64_t runTime)
|
||||||
|
{
|
||||||
|
this->pimpl->statsTime.addSample(runTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ExperimentResult::getRunTime() const
|
||||||
|
{
|
||||||
|
return this->pimpl->statsTime.getMin();
|
||||||
|
}
|
||||||
|
|
||||||
|
double ExperimentResult::getUsPerCall() const
|
||||||
|
{
|
||||||
|
if(this->pimpl->failure == false)
|
||||||
|
{
|
||||||
|
return static_cast<double>(this->pimpl->statsTime.getMin()) / static_cast<double>(this->pimpl->problemSpaceIterations);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ExperimentResult::getCallsPerSecond() const
|
||||||
|
{
|
||||||
|
if(this->pimpl->failure == false)
|
||||||
|
{
|
||||||
|
return 1.0 / (this->getUsPerCall() * celero::UsToSec);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ExperimentResult::getUnitsPerSecond() const
|
||||||
|
{
|
||||||
|
return (this->pimpl->problemSpaceValueScale > 0.0)
|
||||||
|
? ((this->pimpl->problemSpaceValue * this->pimpl->problemSpaceIterations / this->pimpl->problemSpaceValueScale)
|
||||||
|
/ (this->pimpl->statsTime.getMin() * celero::UsToSec))
|
||||||
|
: 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ExperimentResult::getBaselineMeasurement() const
|
||||||
|
{
|
||||||
|
if(this->pimpl->parent->getIsBaselineCase() == false)
|
||||||
|
{
|
||||||
|
const auto bm = this->pimpl->parent->getBenchmark();
|
||||||
|
|
||||||
|
if(bm != nullptr)
|
||||||
|
{
|
||||||
|
const auto baselineExperiment = bm->getBaseline();
|
||||||
|
|
||||||
|
if(baselineExperiment != nullptr)
|
||||||
|
{
|
||||||
|
const auto baselineResult = baselineExperiment->getResultByValue(this->getProblemSpaceValue());
|
||||||
|
|
||||||
|
if(baselineResult != nullptr)
|
||||||
|
{
|
||||||
|
const auto baselineResultUs = baselineResult->getUsPerCall();
|
||||||
|
|
||||||
|
// Prevent possible divide by zero.
|
||||||
|
if(baselineResultUs > 0)
|
||||||
|
{
|
||||||
|
return this->getUsPerCall() / baselineResult->getUsPerCall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExperimentResult::setComplete(bool x)
|
||||||
|
{
|
||||||
|
this->pimpl->complete = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExperimentResult::getComplete() const
|
||||||
|
{
|
||||||
|
return this->pimpl->complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExperimentResult::setFailure(bool x)
|
||||||
|
{
|
||||||
|
this->pimpl->failure = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExperimentResult::getFailure() const
|
||||||
|
{
|
||||||
|
return this->pimpl->failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExperimentResult::setUserDefinedMeasurements(std::shared_ptr<UserDefinedMeasurementCollector> x)
|
||||||
|
{
|
||||||
|
this->pimpl->udmCollector = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<UserDefinedMeasurementCollector> ExperimentResult::getUserDefinedMeasurements() const
|
||||||
|
{
|
||||||
|
return this->pimpl->udmCollector;
|
||||||
|
}
|
161
lib/celero/ExperimentResult.h
Normal file
161
lib/celero/ExperimentResult.h
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
#ifndef H_CELERO_EXPERIMENTRESULT_H
|
||||||
|
#define H_CELERO_EXPERIMENTRESULT_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Export.h>
|
||||||
|
#include <celero/Pimpl.h>
|
||||||
|
#include <celero/Statistics.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
class Experiment;
|
||||||
|
class UserDefinedMeasurementCollector;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \class ExperimentResult
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
class CELERO_EXPORT ExperimentResult
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ExperimentResult(Experiment* x);
|
||||||
|
|
||||||
|
~ExperimentResult();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets a pointer to the owning experiment.
|
||||||
|
///
|
||||||
|
Experiment* getExperiment() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
void setProblemSpaceValue(int64_t x, double scale = 1.0, uint64_t iterations = 0);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
int64_t getProblemSpaceValue() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
double getProblemSpaceValueScale() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
uint64_t getProblemSpaceIterations() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Statistics<int64_t>* getTimeStatistics();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Adds a run time sample during experiment execution.
|
||||||
|
///
|
||||||
|
void addRunTimeSample(const uint64_t x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns the best run time sample observed.
|
||||||
|
///
|
||||||
|
uint64_t getRunTime() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Get the number of computed microseconds per iteration (i.e. a single call to the code under test.)
|
||||||
|
///
|
||||||
|
/// A call is defined as one iteration of one execution of the code under test.
|
||||||
|
///
|
||||||
|
double getUsPerCall() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Get the number of times the code under test could be called per second.
|
||||||
|
///
|
||||||
|
/// A call is defined as one iteration of one execution of the code under test.
|
||||||
|
///
|
||||||
|
double getCallsPerSecond() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Get the processing speed in units per second.
|
||||||
|
///
|
||||||
|
/// A call is defined as one iteration of one execution of the code under test.
|
||||||
|
///
|
||||||
|
double getUnitsPerSecond() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Calculate this experiments baseline value.
|
||||||
|
///
|
||||||
|
/// If this IS a baseline experiment, the function will return 1.0;
|
||||||
|
/// Returns -1 on error.
|
||||||
|
///
|
||||||
|
double getBaselineMeasurement() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Sets a flag indicating if this result is complete.
|
||||||
|
///
|
||||||
|
void setComplete(bool x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets a flag indicating if this result is complete.
|
||||||
|
///
|
||||||
|
bool getComplete() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Sets a flag indicating if failure happened during evaluation.
|
||||||
|
///
|
||||||
|
void setFailure(bool x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Gets a flag indicating if failure happened during evaluation.
|
||||||
|
///
|
||||||
|
bool getFailure() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
void setUserDefinedMeasurements(std::shared_ptr<UserDefinedMeasurementCollector> x);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
std::shared_ptr<UserDefinedMeasurementCollector> getUserDefinedMeasurements() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
///
|
||||||
|
/// Disable default constructor
|
||||||
|
///
|
||||||
|
ExperimentResult();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
class Impl;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
Pimpl<Impl> pimpl;
|
||||||
|
};
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
40
lib/celero/Export.h
Normal file
40
lib/celero/Export.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef H_CELERO_EXPORT_H
|
||||||
|
#define H_CELERO_EXPORT_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifdef CELERO_STATIC
|
||||||
|
#define CELERO_EXPORT
|
||||||
|
#define CELERO_EXPORT_C
|
||||||
|
#else
|
||||||
|
#ifdef WIN32
|
||||||
|
#if defined CELERO_EXPORTS
|
||||||
|
#define CELERO_EXPORT _declspec(dllexport)
|
||||||
|
#define CELERO_EXPORT_C extern "C" _declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define CELERO_EXPORT _declspec(dllimport)
|
||||||
|
#define CELERO_EXPORT_C extern "C" _declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define CELERO_EXPORT
|
||||||
|
#define CELERO_EXPORT_C extern "C"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
59
lib/celero/Factory.h
Normal file
59
lib/celero/Factory.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef H_CELERO_FACTORY_H
|
||||||
|
#define H_CELERO_FACTORY_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Export.h>
|
||||||
|
#include <celero/TestFixture.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \class Factory
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// Pure Virtual Base class for benchmarks.
|
||||||
|
///
|
||||||
|
class CELERO_EXPORT Factory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// \brief Default Constructor
|
||||||
|
///
|
||||||
|
Factory()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Virtual Destructor
|
||||||
|
///
|
||||||
|
virtual ~Factory()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Pure virtual function.
|
||||||
|
///
|
||||||
|
virtual std::shared_ptr<TestFixture> Create() = 0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
50
lib/celero/FileReader.h
Normal file
50
lib/celero/FileReader.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#ifndef H_CELERO_FILEREADER_H
|
||||||
|
#define H_CELERO_FILEREADER_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <locale>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \struct FileReader
|
||||||
|
///
|
||||||
|
/// A helper struct to aid in reading CSV files.
|
||||||
|
///
|
||||||
|
/// Classify commas as whitespace.
|
||||||
|
///
|
||||||
|
struct FieldReader : std::ctype<char>
|
||||||
|
{
|
||||||
|
FieldReader() : std::ctype<char>(FieldReader::GetTable())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::ctype_base::mask const* GetTable()
|
||||||
|
{
|
||||||
|
static std::vector<std::ctype_base::mask> rc(table_size, std::ctype_base::mask());
|
||||||
|
rc[','] = std::ctype_base::space;
|
||||||
|
rc['\n'] = std::ctype_base::space;
|
||||||
|
rc['\r'] = std::ctype_base::space;
|
||||||
|
return &rc[0];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
60
lib/celero/GenericFactory.h
Normal file
60
lib/celero/GenericFactory.h
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
#ifndef H_CELERO_GENERICFACTORY_H
|
||||||
|
#define H_CELERO_GENERICFACTORY_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Export.h>
|
||||||
|
#include <celero/Factory.h>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \class GenericFactory
|
||||||
|
///
|
||||||
|
/// \author John farrier
|
||||||
|
///
|
||||||
|
template <class T>
|
||||||
|
class GenericFactory : public Factory
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// \brief Default Constructor
|
||||||
|
///
|
||||||
|
GenericFactory() : Factory()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Virtual Destructor
|
||||||
|
///
|
||||||
|
virtual ~GenericFactory()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Overload the pure virtual base class function.
|
||||||
|
///
|
||||||
|
virtual std::shared_ptr<TestFixture> Create()
|
||||||
|
{
|
||||||
|
return std::make_shared<T>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
149
lib/celero/JUnit.cpp
Normal file
149
lib/celero/JUnit.cpp
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <assert.h>
|
||||||
|
#include <celero/Benchmark.h>
|
||||||
|
#include <celero/JUnit.h>
|
||||||
|
#include <celero/PimplImpl.h>
|
||||||
|
#include <celero/Timer.h>
|
||||||
|
#include <celero/Utilities.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \struct celero::JUnit::Impl
|
||||||
|
///
|
||||||
|
class celero::JUnit::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string fileName;
|
||||||
|
|
||||||
|
/// Store the test case size, measured baseline, objective baseline, and total run time in seconds.
|
||||||
|
std::map<std::string, std::vector<std::shared_ptr<celero::ExperimentResult>>> results;
|
||||||
|
|
||||||
|
double totalTime = {0.0};
|
||||||
|
};
|
||||||
|
|
||||||
|
JUnit& JUnit::Instance()
|
||||||
|
{
|
||||||
|
static JUnit singleton;
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JUnit::setFileName(const std::string& x)
|
||||||
|
{
|
||||||
|
assert(x.empty() == false);
|
||||||
|
this->pimpl->fileName = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JUnit::add(std::shared_ptr<celero::ExperimentResult> x)
|
||||||
|
{
|
||||||
|
this->pimpl->results[x->getExperiment()->getBenchmark()->getName()].push_back(x);
|
||||||
|
this->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
void JUnit::save()
|
||||||
|
{
|
||||||
|
std::ofstream ofs;
|
||||||
|
ofs.open(this->pimpl->fileName);
|
||||||
|
|
||||||
|
if(ofs.is_open() == true)
|
||||||
|
{
|
||||||
|
const auto os = &ofs;
|
||||||
|
|
||||||
|
*os << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" << std::endl;
|
||||||
|
|
||||||
|
for(auto i : this->pimpl->results)
|
||||||
|
{
|
||||||
|
uint64_t testSuiteTime = 0;
|
||||||
|
size_t testSuiteFailures = 0;
|
||||||
|
size_t testSuiteErrors = 0;
|
||||||
|
|
||||||
|
const auto runs = i.second;
|
||||||
|
|
||||||
|
for(auto j : runs)
|
||||||
|
{
|
||||||
|
if(j->getFailure())
|
||||||
|
{
|
||||||
|
testSuiteErrors++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if((j->getExperiment()->getBaselineTarget() > 0.0) && (j->getBaselineMeasurement() > j->getExperiment()->getBaselineTarget()))
|
||||||
|
{
|
||||||
|
testSuiteFailures++;
|
||||||
|
}
|
||||||
|
|
||||||
|
testSuiteTime += j->getRunTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
*os << "<testsuite errors=\"" << testSuiteErrors << "\" ";
|
||||||
|
*os << "tests=\"" << i.second.size() << "\" ";
|
||||||
|
*os << "time=\"" << celero::timer::ConvertSystemTime(testSuiteTime) << "\" ";
|
||||||
|
*os << "failures=\"" << testSuiteFailures << "\" ";
|
||||||
|
*os << "name=\"" << i.first << "\">" << std::endl;
|
||||||
|
|
||||||
|
for(auto j : runs)
|
||||||
|
{
|
||||||
|
*os << "\t<testcase ";
|
||||||
|
*os << "time=\"" << celero::timer::ConvertSystemTime(j->getFailure() ? 0 : j->getRunTime()) << "\" ";
|
||||||
|
*os << "name=\"" << j->getExperiment()->getName() << "#" << j->getProblemSpaceValue() << "\"";
|
||||||
|
|
||||||
|
// Compare measured to objective
|
||||||
|
if(j->getFailure())
|
||||||
|
{
|
||||||
|
// Error
|
||||||
|
*os << ">" << std::endl;
|
||||||
|
|
||||||
|
*os << "\t\t<error ";
|
||||||
|
*os << "type=\"exception\"";
|
||||||
|
*os << "/>" << std::endl;
|
||||||
|
|
||||||
|
*os << "\t</testcase>" << std::endl;
|
||||||
|
}
|
||||||
|
else if((j->getExperiment()->getBaselineTarget() > 0.0) && (j->getBaselineMeasurement() > j->getExperiment()->getBaselineTarget()))
|
||||||
|
{
|
||||||
|
// Failure
|
||||||
|
*os << ">" << std::endl;
|
||||||
|
|
||||||
|
*os << "\t\t<failure ";
|
||||||
|
*os << "type=\"Performance objective not met.\" ";
|
||||||
|
*os << "message=\"Measurement of " << j->getBaselineMeasurement() << " exceeds objective baseline of "
|
||||||
|
<< j->getExperiment()->getBaselineTarget() << "\" ";
|
||||||
|
*os << "/>" << std::endl;
|
||||||
|
|
||||||
|
*os << "\t</testcase>" << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Success
|
||||||
|
*os << "/>" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*os << "</testsuite>" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
ofs.close();
|
||||||
|
}
|
||||||
|
}
|
73
lib/celero/JUnit.h
Normal file
73
lib/celero/JUnit.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#ifndef H_CELERO_JUNIT_H
|
||||||
|
#define H_CELERO_JUNIT_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Experiment.h>
|
||||||
|
#include <celero/Pimpl.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \class JUnit
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
class JUnit
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Singleton
|
||||||
|
///
|
||||||
|
static JUnit& Instance();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Specify a file name for a results output file.
|
||||||
|
///
|
||||||
|
/// \param x The name of the output file in which to store Celero's results.
|
||||||
|
///
|
||||||
|
void setFileName(const std::string& x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Add a new result to the JUnit output XML.
|
||||||
|
///
|
||||||
|
/// This should re-save on every new result so that the output can be monitored externally.
|
||||||
|
///
|
||||||
|
void add(std::shared_ptr<celero::ExperimentResult> x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Save the JUnit (XUnit) formatted file to the given file name.
|
||||||
|
///
|
||||||
|
void save();
|
||||||
|
|
||||||
|
private:
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
class Impl;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
Pimpl<Impl> pimpl;
|
||||||
|
};
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
392
lib/celero/Memory.cpp
Normal file
392
lib/celero/Memory.cpp
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Memory.h>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#include <Psapi.h>
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <array>
|
||||||
|
#else
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/resource.h>
|
||||||
|
#include <sys/sysinfo.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
///
|
||||||
|
/// References:
|
||||||
|
/// http://blogs.microsoft.co.il/sasha/2016/01/05/windows-process-memory-usage-demystified/
|
||||||
|
/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366770(v=vs.85).aspx
|
||||||
|
/// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684877(v=vs.85).aspx
|
||||||
|
/// http://nadeausoftware.com/articles/2012/09/c_c_tip_how_get_physical_memory_size_system
|
||||||
|
/// http://nadeausoftware.com/articles/2012/07/c_c_tip_how_get_process_resident_set_size_physical_memory_use
|
||||||
|
/// https://stackoverflow.com/questions/669438/how-to-get-memory-usage-at-run-time-in-c
|
||||||
|
/// https://stackoverflow.com/questions/2513505/how-to-get-available-memory-c-g
|
||||||
|
///
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#else
|
||||||
|
constexpr int64_t Kilobytes2Bytes{1024};
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
namespace impl
|
||||||
|
{
|
||||||
|
int ParseLine(char* line)
|
||||||
|
{
|
||||||
|
const auto i = strlen(line);
|
||||||
|
|
||||||
|
while(*line < '0' || *line > '9')
|
||||||
|
{
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
line[i - 3] = '\0';
|
||||||
|
return atoi(line);
|
||||||
|
}
|
||||||
|
} // namespace impl
|
||||||
|
} // namespace celero
|
||||||
|
#endif
|
||||||
|
|
||||||
|
celero::RAMReport::operator std::string()
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
|
||||||
|
ss << "System Total: " << this->RamSystemTotal << std::endl;
|
||||||
|
ss << "System Available: " << this->RamSystemAvailable << std::endl;
|
||||||
|
ss << "System Used: " << this->RamSystemUsed << std::endl;
|
||||||
|
ss << "System UsedByCurrentProcess: " << this->RamSystemUsedByCurrentProcess << std::endl;
|
||||||
|
ss << "Physical Total: " << this->RamPhysicalTotal << std::endl;
|
||||||
|
ss << "Physical Available: " << this->RamPhysicalAvailable << std::endl;
|
||||||
|
ss << "Physical Used: " << this->RamPhysicalUsed << std::endl;
|
||||||
|
ss << "Physical UsedByCurrentProcess: " << this->RamPhysicalUsedByCurrentProcess << std::endl;
|
||||||
|
ss << "Physical UsedByCurrentProcessPeak: " << this->RamPhysicalUsedByCurrentProcessPeak << std::endl;
|
||||||
|
ss << "Virtual Total: " << this->RamVirtualTotal << std::endl;
|
||||||
|
ss << "Virtual Available: " << this->RamVirtualAvailable << std::endl;
|
||||||
|
ss << "Virtual Used: " << this->RamVirtualUsed << std::endl;
|
||||||
|
ss << "Virtual UsedByCurrentProcess: " << this->RamVirtualUsedByCurrentProcess << std::endl;
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
celero::RAMReport celero::RAMReport::operator-(const RAMReport& x)
|
||||||
|
{
|
||||||
|
celero::RAMReport r;
|
||||||
|
r.RamSystemTotal = this->RamSystemTotal - x.RamSystemTotal;
|
||||||
|
r.RamSystemAvailable = this->RamSystemAvailable - x.RamSystemAvailable;
|
||||||
|
r.RamSystemUsed = this->RamSystemUsed - x.RamSystemUsed;
|
||||||
|
r.RamSystemUsedByCurrentProcess = this->RamSystemUsedByCurrentProcess - x.RamSystemUsedByCurrentProcess;
|
||||||
|
r.RamPhysicalTotal = this->RamPhysicalTotal - x.RamPhysicalTotal;
|
||||||
|
r.RamPhysicalAvailable = this->RamPhysicalAvailable - x.RamPhysicalAvailable;
|
||||||
|
r.RamPhysicalUsed = this->RamPhysicalUsed - x.RamPhysicalUsed;
|
||||||
|
r.RamPhysicalUsedByCurrentProcess = this->RamPhysicalUsedByCurrentProcess - x.RamPhysicalUsedByCurrentProcess;
|
||||||
|
r.RamPhysicalUsedByCurrentProcessPeak = this->RamPhysicalUsedByCurrentProcessPeak - x.RamPhysicalUsedByCurrentProcessPeak;
|
||||||
|
r.RamVirtualTotal = this->RamVirtualTotal - x.RamVirtualTotal;
|
||||||
|
r.RamVirtualAvailable = this->RamVirtualAvailable - x.RamVirtualAvailable;
|
||||||
|
r.RamVirtualUsed = this->RamVirtualUsed - x.RamVirtualUsed;
|
||||||
|
r.RamVirtualUsedByCurrentProcess = this->RamVirtualUsedByCurrentProcess - x.RamVirtualUsedByCurrentProcess;
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMSystemTotal()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
MEMORYSTATUSEX memInfo;
|
||||||
|
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
|
||||||
|
GlobalMemoryStatusEx(&memInfo);
|
||||||
|
return static_cast<int64_t>(memInfo.ullTotalPhys) + static_cast<int64_t>(memInfo.ullTotalVirtual);
|
||||||
|
#elif defined(__unix__) || defined(__unix) || defined(unix)
|
||||||
|
// Prefer sysctl() over sysconf() except sysctl() HW_REALMEM and HW_PHYSMEM
|
||||||
|
// return static_cast<int64_t>(sysconf(_SC_PHYS_PAGES)) * static_cast<int64_t>(sysconf(_SC_PAGE_SIZE));
|
||||||
|
struct sysinfo memInfo;
|
||||||
|
sysinfo(&memInfo);
|
||||||
|
int64_t total = memInfo.totalram;
|
||||||
|
total += memInfo.totalswap;
|
||||||
|
total += memInfo.totalhigh;
|
||||||
|
return total * static_cast<int64_t>(memInfo.mem_unit);
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
int mib[2];
|
||||||
|
mib[0] = CTL_HW;
|
||||||
|
mib[1] = HW_MEMSIZE;
|
||||||
|
|
||||||
|
int64_t memInfo{0};
|
||||||
|
auto len = sizeof(memInfo);
|
||||||
|
|
||||||
|
if(sysctl(mib, 2, &memInfo, &len, nullptr, 0) == 0)
|
||||||
|
{
|
||||||
|
return memInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMSystemAvailable()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
MEMORYSTATUSEX memInfo;
|
||||||
|
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
|
||||||
|
GlobalMemoryStatusEx(&memInfo);
|
||||||
|
return static_cast<int64_t>(memInfo.ullAvailPhys) + static_cast<int64_t>(memInfo.ullAvailVirtual);
|
||||||
|
#else
|
||||||
|
return celero::GetRAMSystemTotal() - celero::GetRAMSystemUsed();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMSystemUsed()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
return celero::GetRAMSystemTotal() - celero::GetRAMSystemAvailable();
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
int mib[2];
|
||||||
|
mib[0] = CTL_HW;
|
||||||
|
mib[1] = HW_MEMSIZE;
|
||||||
|
|
||||||
|
std::array<int64_t, 2> memInfo{{0, 0}};
|
||||||
|
auto len = sizeof(memInfo[0]);
|
||||||
|
|
||||||
|
if(sysctl(mib, 2, &memInfo[0], &len, nullptr, 0) == 0)
|
||||||
|
{
|
||||||
|
if(sysctl(mib, 2, &memInfo[1], &len, nullptr, 0) == 0)
|
||||||
|
{
|
||||||
|
return memInfo[0] + memInfo[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
struct sysinfo memInfo;
|
||||||
|
sysinfo(&memInfo);
|
||||||
|
int64_t total = memInfo.totalram - memInfo.freeram;
|
||||||
|
total += memInfo.totalswap - memInfo.freeswap;
|
||||||
|
total += memInfo.totalhigh - memInfo.freehigh;
|
||||||
|
return total * static_cast<int64_t>(memInfo.mem_unit);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMSystemUsedByCurrentProcess()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
PROCESS_MEMORY_COUNTERS_EX pmc;
|
||||||
|
GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast<PPROCESS_MEMORY_COUNTERS>(&pmc), sizeof(pmc));
|
||||||
|
return static_cast<int64_t>(pmc.WorkingSetSize);
|
||||||
|
#else
|
||||||
|
return celero::GetRAMPhysicalUsedByCurrentProcess() + celero::GetRAMVirtualUsedByCurrentProcess();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMPhysicalTotal()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
MEMORYSTATUSEX memInfo;
|
||||||
|
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
|
||||||
|
GlobalMemoryStatusEx(&memInfo);
|
||||||
|
return static_cast<int64_t>(memInfo.ullTotalPhys);
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
struct sysinfo memInfo;
|
||||||
|
sysinfo(&memInfo);
|
||||||
|
return memInfo.totalram * memInfo.mem_unit;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMPhysicalAvailable()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
MEMORYSTATUSEX memInfo;
|
||||||
|
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
|
||||||
|
GlobalMemoryStatusEx(&memInfo);
|
||||||
|
return static_cast<int64_t>(memInfo.ullAvailPhys);
|
||||||
|
#else
|
||||||
|
return celero::GetRAMPhysicalTotal() - celero::GetRAMPhysicalUsed();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMPhysicalUsed()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
return celero::GetRAMPhysicalTotal() - celero::GetRAMPhysicalAvailable();
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
struct rusage rusage;
|
||||||
|
getrusage(RUSAGE_SELF, &rusage);
|
||||||
|
return (size_t)rusage.ru_maxrss;
|
||||||
|
#else
|
||||||
|
struct sysinfo memInfo;
|
||||||
|
sysinfo(&memInfo);
|
||||||
|
return (static_cast<int64_t>(memInfo.totalram) - static_cast<int64_t>(memInfo.freeram)) * static_cast<int64_t>(memInfo.mem_unit);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMPhysicalUsedByCurrentProcess()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
PROCESS_MEMORY_COUNTERS_EX pmc;
|
||||||
|
GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast<PPROCESS_MEMORY_COUNTERS>(&pmc), sizeof(pmc));
|
||||||
|
return static_cast<int64_t>(pmc.WorkingSetSize);
|
||||||
|
#else
|
||||||
|
constexpr int BufferSize{128};
|
||||||
|
int64_t result = 0;
|
||||||
|
auto file = fopen("/proc/self/status", "r");
|
||||||
|
char line[BufferSize];
|
||||||
|
|
||||||
|
while(fgets(line, BufferSize, file) != nullptr)
|
||||||
|
{
|
||||||
|
if(strncmp(line, "VmRSS:", 6) == 0)
|
||||||
|
{
|
||||||
|
result += celero::impl::ParseLine(line) * Kilobytes2Bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
return static_cast<int64_t>(result);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMPhysicalUsedByCurrentProcessPeak()
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
PROCESS_MEMORY_COUNTERS pmc;
|
||||||
|
GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
|
||||||
|
return static_cast<int64_t>(pmc.PeakWorkingSetSize);
|
||||||
|
#elif defined(__APPLE__) && defined(__MACH__)
|
||||||
|
struct rusage rusage;
|
||||||
|
getrusage(RUSAGE_SELF, &rusage);
|
||||||
|
return static_cast<int64_t>(rusage.ru_maxrss);
|
||||||
|
#else
|
||||||
|
constexpr int BufferSize{128};
|
||||||
|
int64_t result = 0;
|
||||||
|
auto file = fopen("/proc/self/status", "r");
|
||||||
|
char line[BufferSize];
|
||||||
|
|
||||||
|
while(fgets(line, BufferSize, file) != nullptr)
|
||||||
|
{
|
||||||
|
if(strncmp(line, "VmHWM:", 6) == 0)
|
||||||
|
{
|
||||||
|
result += celero::impl::ParseLine(line) * Kilobytes2Bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
return static_cast<int64_t>(result);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMVirtualTotal()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
MEMORYSTATUSEX memInfo;
|
||||||
|
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
|
||||||
|
GlobalMemoryStatusEx(&memInfo);
|
||||||
|
return memInfo.ullTotalPageFile;
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
struct sysinfo memInfo;
|
||||||
|
sysinfo(&memInfo);
|
||||||
|
return static_cast<int64_t>(memInfo.totalswap) * static_cast<int64_t>(memInfo.mem_unit);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMVirtualAvailable()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
MEMORYSTATUSEX memInfo;
|
||||||
|
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
|
||||||
|
GlobalMemoryStatusEx(&memInfo);
|
||||||
|
return memInfo.ullTotalPageFile;
|
||||||
|
#else
|
||||||
|
return celero::GetRAMVirtualTotal() - celero::GetRAMVirtualUsed();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMVirtualUsed()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
return celero::GetRAMVirtualTotal() - celero::GetRAMVirtualAvailable();
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
return -1;
|
||||||
|
#else
|
||||||
|
struct sysinfo memInfo;
|
||||||
|
sysinfo(&memInfo);
|
||||||
|
const int64_t total = memInfo.totalswap - memInfo.freeswap;
|
||||||
|
return total * static_cast<int64_t>(memInfo.mem_unit);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t celero::GetRAMVirtualUsedByCurrentProcess()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
PROCESS_MEMORY_COUNTERS_EX pmc;
|
||||||
|
GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast<PPROCESS_MEMORY_COUNTERS>(&pmc), sizeof(pmc));
|
||||||
|
return pmc.PrivateUsage;
|
||||||
|
#else
|
||||||
|
// Verified Correct.
|
||||||
|
constexpr int BufferSize{128};
|
||||||
|
int64_t result = 0;
|
||||||
|
FILE* file = fopen("/proc/self/status", "r");
|
||||||
|
char line[BufferSize];
|
||||||
|
|
||||||
|
while(fgets(line, BufferSize, file) != NULL)
|
||||||
|
{
|
||||||
|
if(strncmp(line, "VmSize:", 7) == 0)
|
||||||
|
{
|
||||||
|
result = celero::impl::ParseLine(line) * Kilobytes2Bytes;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
celero::RAMReport celero::GetRAMReport()
|
||||||
|
{
|
||||||
|
celero::RAMReport r;
|
||||||
|
r.RamSystemTotal = GetRAMSystemTotal();
|
||||||
|
r.RamSystemAvailable = GetRAMSystemAvailable();
|
||||||
|
r.RamSystemUsed = GetRAMSystemUsed();
|
||||||
|
r.RamSystemUsedByCurrentProcess = GetRAMSystemUsedByCurrentProcess();
|
||||||
|
r.RamPhysicalTotal = GetRAMPhysicalTotal();
|
||||||
|
r.RamPhysicalAvailable = GetRAMPhysicalAvailable();
|
||||||
|
r.RamPhysicalUsed = GetRAMPhysicalUsed();
|
||||||
|
r.RamPhysicalUsedByCurrentProcess = GetRAMPhysicalUsedByCurrentProcess();
|
||||||
|
r.RamVirtualTotal = GetRAMVirtualTotal();
|
||||||
|
r.RamVirtualAvailable = GetRAMVirtualAvailable();
|
||||||
|
r.RamVirtualUsed = GetRAMVirtualUsed();
|
||||||
|
r.RamVirtualUsedByCurrentProcess = GetRAMVirtualUsedByCurrentProcess();
|
||||||
|
return r;
|
||||||
|
}
|
142
lib/celero/Memory.h
Normal file
142
lib/celero/Memory.h
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
#ifndef H_CELERO_MEORY_H
|
||||||
|
#define H_CELERO_MEORY_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2018 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Export.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \struct RAMReport
|
||||||
|
///
|
||||||
|
/// Contans all Memory measurements (in bytes)
|
||||||
|
///
|
||||||
|
struct RAMReport
|
||||||
|
{
|
||||||
|
int64_t RamSystemTotal{0};
|
||||||
|
int64_t RamSystemAvailable{0};
|
||||||
|
int64_t RamSystemUsed{0};
|
||||||
|
int64_t RamSystemUsedByCurrentProcess{0};
|
||||||
|
int64_t RamPhysicalTotal{0};
|
||||||
|
int64_t RamPhysicalAvailable{0};
|
||||||
|
int64_t RamPhysicalUsed{0};
|
||||||
|
int64_t RamPhysicalUsedByCurrentProcess{0};
|
||||||
|
int64_t RamPhysicalUsedByCurrentProcessPeak{0};
|
||||||
|
int64_t RamVirtualTotal{0};
|
||||||
|
int64_t RamVirtualAvailable{0};
|
||||||
|
int64_t RamVirtualUsed{0};
|
||||||
|
int64_t RamVirtualUsedByCurrentProcess{0};
|
||||||
|
|
||||||
|
operator std::string();
|
||||||
|
celero::RAMReport operator-(const celero::RAMReport& x);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
// Physical + Virtual Memory
|
||||||
|
|
||||||
|
CELERO_EXPORT int64_t GetRAMSystemTotal();
|
||||||
|
CELERO_EXPORT int64_t GetRAMSystemAvailable();
|
||||||
|
CELERO_EXPORT int64_t GetRAMSystemUsed();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The sum of the physical RAM used by the current process and the virtual RAM used by the current process.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT int64_t GetRAMSystemUsedByCurrentProcess();
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
// Physical Memory
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The total physical RAM, in bytes.
|
||||||
|
/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366770(v=vs.85).aspx
|
||||||
|
///
|
||||||
|
CELERO_EXPORT int64_t GetRAMPhysicalTotal();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The total physical RAM available to the current process, in bytes.
|
||||||
|
///
|
||||||
|
/// On Windows, this is defined as "This is the amount of physical memory that can be immediately reused without having to write its contents to
|
||||||
|
/// disk first. It is the sum of the size of the standby, free, and zero lists."
|
||||||
|
/// https://msdn.microsoft.com/en-us/library/windows/desktop/aa366770(v=vs.85).aspx
|
||||||
|
///
|
||||||
|
CELERO_EXPORT int64_t GetRAMPhysicalAvailable();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The total amount of physical RAM minus the amount of physical RAM which is available.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT int64_t GetRAMPhysicalUsed();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// On Windows, this is defined by the Working Set Size. The working set size is defined as "The working set of a process is the set of pages in
|
||||||
|
/// the virtual address space of the process that are currently resident in physical memory. The working set contains only pageable memory
|
||||||
|
/// allocations; nonpageable memory allocations such as Address Windowing Extensions (AWE) or large page allocations are not included in the
|
||||||
|
/// working set."
|
||||||
|
/// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684877(v=vs.85).aspx
|
||||||
|
/// https://msdn.microsoft.com/en-us/library/windows/desktop/cc441804(v=vs.85).aspx
|
||||||
|
///
|
||||||
|
CELERO_EXPORT int64_t GetRAMPhysicalUsedByCurrentProcess();
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
CELERO_EXPORT int64_t GetRAMPhysicalUsedByCurrentProcessPeak();
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
// Virtual Memory
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The total amount of Virtual RAM (page file size).
|
||||||
|
///
|
||||||
|
/// On Windows, this is defined by the amount of page file that the current process has access to. It is not the total available on the system.
|
||||||
|
/// From the Windows documentation: "The current committed memory limit for the system or the current process, whichever is smaller, in bytes. To
|
||||||
|
/// get the system-wide committed memory limit, call GetPerformanceInfo."
|
||||||
|
///
|
||||||
|
CELERO_EXPORT int64_t GetRAMVirtualTotal();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The amount of non-physical memory (page file) available.
|
||||||
|
///
|
||||||
|
/// On Windows, this is defined by the amount of page file that the current process has access to. It is not the total available on the system.
|
||||||
|
/// From the Windows documentation: "The maximum amount of memory the current process can commit, in bytes. This value is equal to or smaller than
|
||||||
|
/// the system-wide available commit value."
|
||||||
|
///
|
||||||
|
CELERO_EXPORT int64_t GetRAMVirtualAvailable();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// The total virtual RAM minus the available virtual RAM.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT int64_t GetRAMVirtualUsed();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// On Windows, this is defined as the commit charge. "The Commit Charge value in bytes for this process. Commit Charge is the total amount of
|
||||||
|
/// memory that the memory manager has committed for a running process."
|
||||||
|
/// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684877(v=vs.85).aspx
|
||||||
|
///
|
||||||
|
CELERO_EXPORT int64_t GetRAMVirtualUsedByCurrentProcess();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Returns a RAMReport class containing all RAM measurements.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT celero::RAMReport GetRAMReport();
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
65
lib/celero/Pimpl.h
Normal file
65
lib/celero/Pimpl.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#ifndef H_CELERO_PIMPL_H
|
||||||
|
#define H_CELERO_PIMPL_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Export.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \class Pimpl
|
||||||
|
///
|
||||||
|
/// \author Herb Sutter
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// Classes using this must overload the assignment operator.
|
||||||
|
/// Original code by Herb Sutter. Adapted for more primitive compilers by John Farrier.
|
||||||
|
///
|
||||||
|
template <typename T>
|
||||||
|
class Pimpl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Pimpl();
|
||||||
|
// template<typename ...Args> Pimpl( Args&& ... );
|
||||||
|
template <typename Arg1>
|
||||||
|
Pimpl(Arg1&&);
|
||||||
|
template <typename Arg1, typename Arg2>
|
||||||
|
Pimpl(Arg1&&, Arg2&&);
|
||||||
|
template <typename Arg1, typename Arg2, typename Arg3>
|
||||||
|
Pimpl(Arg1&&, Arg2&&, Arg3&&);
|
||||||
|
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
|
||||||
|
Pimpl(Arg1&&, Arg2&&, Arg3&&, Arg4&&);
|
||||||
|
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
|
||||||
|
Pimpl(Arg1&&, Arg2&&, Arg3&&, Arg4&&, Arg5&&);
|
||||||
|
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6>
|
||||||
|
Pimpl(Arg1&&, Arg2&&, Arg3&&, Arg4&&, Arg5&&, Arg6&&);
|
||||||
|
~Pimpl();
|
||||||
|
|
||||||
|
T* operator->();
|
||||||
|
const T* operator->() const;
|
||||||
|
T& operator*();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<T> _pimpl;
|
||||||
|
};
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
98
lib/celero/PimplImpl.h
Normal file
98
lib/celero/PimplImpl.h
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#ifndef H_CELERO_PIMPLIMPL_H
|
||||||
|
#define H_CELERO_PIMPLIMPL_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <memory>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
Pimpl<T>::Pimpl() : _pimpl(new T())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
template <typename Arg1>
|
||||||
|
Pimpl<T>::Pimpl(Arg1&& arg1) : _pimpl(new T(std::forward<Arg1>(arg1)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
template <typename Arg1, typename Arg2>
|
||||||
|
Pimpl<T>::Pimpl(Arg1&& arg1, Arg2&& arg2) : _pimpl(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
template <typename Arg1, typename Arg2, typename Arg3>
|
||||||
|
Pimpl<T>::Pimpl(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3)
|
||||||
|
: _pimpl(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
|
||||||
|
Pimpl<T>::Pimpl(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4)
|
||||||
|
: _pimpl(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3), std::forward<Arg4>(arg4)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5>
|
||||||
|
Pimpl<T>::Pimpl(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5)
|
||||||
|
: _pimpl(
|
||||||
|
new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3), std::forward<Arg4>(arg4), std::forward<Arg5>(arg5)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
template <typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6>
|
||||||
|
Pimpl<T>::Pimpl(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5, Arg6&& arg6)
|
||||||
|
: _pimpl(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3), std::forward<Arg4>(arg4),
|
||||||
|
std::forward<Arg5>(arg5), std::forward<Arg6>(arg6)))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Pimpl<T>::~Pimpl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T* Pimpl<T>::operator->()
|
||||||
|
{
|
||||||
|
return _pimpl.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T* Pimpl<T>::operator->() const
|
||||||
|
{
|
||||||
|
return _pimpl.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
T& Pimpl<T>::operator*()
|
||||||
|
{
|
||||||
|
return *_pimpl.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
337
lib/celero/Print.cpp
Normal file
337
lib/celero/Print.cpp
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Benchmark.h>
|
||||||
|
#include <celero/Console.h>
|
||||||
|
#include <celero/Print.h>
|
||||||
|
#include <celero/TestVector.h>
|
||||||
|
#include <celero/Timer.h>
|
||||||
|
#include <celero/UserDefinedMeasurementCollector.h>
|
||||||
|
#include <celero/Utilities.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
enum PrintConstants : size_t
|
||||||
|
{
|
||||||
|
ColumnSeperatorWidth = 3,
|
||||||
|
DoubleDecimals = 5,
|
||||||
|
NumberOfColumns = 8,
|
||||||
|
ColumnWidth = 15
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// http://stackoverflow.com/questions/14765155/how-can-i-easily-format-my-data-table-in-c
|
||||||
|
/// Center-aligns string within a field of width w. Pads with blank spaces to enforce alignment.
|
||||||
|
///
|
||||||
|
std::string PrintCenter(const std::string& s, const size_t w = PrintConstants::ColumnWidth)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
std::stringstream spaces;
|
||||||
|
|
||||||
|
// count excess room to pad
|
||||||
|
auto padding = w - s.size();
|
||||||
|
|
||||||
|
for(size_t i = 0; i < padding / 2; ++i)
|
||||||
|
{
|
||||||
|
spaces << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
// format with padding
|
||||||
|
ss << spaces.str() << s << spaces.str();
|
||||||
|
|
||||||
|
// if odd #, add 1 space
|
||||||
|
if((padding > 0) && (padding % 2 != 0))
|
||||||
|
{
|
||||||
|
ss << " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << " | ";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// http://stackoverflow.com/questions/14765155/how-can-i-easily-format-my-data-table-in-c
|
||||||
|
/// Convert double to string with specified number of places after the decimal and left padding.
|
||||||
|
///
|
||||||
|
std::string PrintColumn(const double x, const size_t decDigits = PrintConstants::DoubleDecimals, const size_t width = PrintConstants::ColumnWidth)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::fixed << std::right;
|
||||||
|
|
||||||
|
// fill space around displayed #
|
||||||
|
ss.fill(' ');
|
||||||
|
|
||||||
|
// set width around displayed #
|
||||||
|
ss.width(width);
|
||||||
|
|
||||||
|
// set # places after decimal
|
||||||
|
ss.precision(decDigits);
|
||||||
|
ss << x;
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// http://stackoverflow.com/questions/14765155/how-can-i-easily-format-my-data-table-in-c
|
||||||
|
/// Convert double to string with specified number of places after the decimal.
|
||||||
|
///
|
||||||
|
std::string PrintColumn(const int64_t x, const size_t width = PrintConstants::ColumnWidth)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::fixed;
|
||||||
|
|
||||||
|
// fill space around displayed #
|
||||||
|
ss.fill(' ');
|
||||||
|
|
||||||
|
// set width around displayed #
|
||||||
|
ss.width(width);
|
||||||
|
|
||||||
|
ss << x << " | ";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// http://stackoverflow.com/questions/14765155/how-can-i-easily-format-my-data-table-in-c
|
||||||
|
/// Convert double to string with specified number of places after the decimal.
|
||||||
|
///
|
||||||
|
std::string PrintColumn(const uint64_t x, const size_t width = PrintConstants::ColumnWidth)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::fixed;
|
||||||
|
|
||||||
|
// fill space around displayed #
|
||||||
|
ss.fill(' ');
|
||||||
|
|
||||||
|
// set width around displayed #
|
||||||
|
ss.width(width);
|
||||||
|
|
||||||
|
ss << x << " | ";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// http://stackoverflow.com/questions/14765155/how-can-i-easily-format-my-data-table-in-c
|
||||||
|
/// Convert double to string with specified number of places after the decimal.
|
||||||
|
///
|
||||||
|
std::string PrintStrColumnAligned(const std::string& x, const size_t width = PrintConstants::ColumnWidth, bool alignLeft = true)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::fixed << (alignLeft ? std::left : std::right);
|
||||||
|
|
||||||
|
// fill space around displayed #
|
||||||
|
ss.fill(' ');
|
||||||
|
|
||||||
|
// set width around displayed #
|
||||||
|
ss.width(width);
|
||||||
|
|
||||||
|
if(x.length() > width)
|
||||||
|
{
|
||||||
|
// Truncate
|
||||||
|
std::string xTrunc = x;
|
||||||
|
xTrunc = xTrunc.substr(0, width);
|
||||||
|
ss << xTrunc << " | ";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ss << x << " | ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PrintColumn(const std::string& x, const size_t width = PrintConstants::ColumnWidth)
|
||||||
|
{
|
||||||
|
return PrintStrColumnAligned(x, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PrintColumnRight(const std::string& x, const size_t width = PrintConstants::ColumnWidth)
|
||||||
|
{
|
||||||
|
return PrintStrColumnAligned(x, width, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string PrintHRule(const size_t additionalColumns = 0)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
std::string column{":"};
|
||||||
|
|
||||||
|
while(column.length() < PrintConstants::ColumnWidth)
|
||||||
|
{
|
||||||
|
column += "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string firstColumn = column + ":|";
|
||||||
|
|
||||||
|
column += "-:|";
|
||||||
|
|
||||||
|
ss << "|" << firstColumn;
|
||||||
|
|
||||||
|
for(size_t i = 0; i < PrintConstants::NumberOfColumns + additionalColumns - 1; ++i)
|
||||||
|
{
|
||||||
|
ss << column;
|
||||||
|
}
|
||||||
|
|
||||||
|
ss << std::endl;
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
void Printer::Console(const std::string& x)
|
||||||
|
{
|
||||||
|
std::cout << "Celero: " << x << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Printer::TableBanner()
|
||||||
|
{
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Default);
|
||||||
|
|
||||||
|
std::cout << "|" << PrintCenter("Group") << PrintCenter("Experiment") << PrintCenter("Prob. Space") << PrintCenter("Samples")
|
||||||
|
<< PrintCenter("Iterations") << PrintCenter("Baseline") << PrintCenter("us/Iteration") << PrintCenter("Iterations/sec");
|
||||||
|
|
||||||
|
for(size_t i = PrintConstants::NumberOfColumns; i < this->columnWidths.size(); ++i)
|
||||||
|
{
|
||||||
|
std::cout << PrintCenter(this->userDefinedColumns[i - PrintConstants::NumberOfColumns], this->columnWidths[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\n";
|
||||||
|
std::cout << PrintHRule(this->userDefinedColumns.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Printer::TableRowExperimentHeader(Experiment* x)
|
||||||
|
{
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Default);
|
||||||
|
std::cout << "|" << PrintColumn(x->getBenchmark()->getName()) << PrintColumn(x->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Printer::TableRowFailure(const std::string& msg)
|
||||||
|
{
|
||||||
|
std::cout << PrintColumnRight("-") << PrintColumnRight("-") << PrintColumnRight("-");
|
||||||
|
|
||||||
|
for(size_t i = PrintConstants::NumberOfColumns; i < this->columnWidths.size(); ++i)
|
||||||
|
{
|
||||||
|
std::cout << PrintColumnRight("-", this->columnWidths[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Red);
|
||||||
|
std::cout << msg << std::endl;
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Printer::TableRowProblemSpaceHeader(std::shared_ptr<celero::ExperimentResult> x)
|
||||||
|
{
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Default);
|
||||||
|
|
||||||
|
if(x->getProblemSpaceValue() == static_cast<int64_t>(TestFixture::Constants::NoProblemSpaceValue))
|
||||||
|
{
|
||||||
|
std::cout << PrintColumnRight("Null");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << PrintColumn(x->getProblemSpaceValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << PrintColumn(x->getExperiment()->getSamples()) << PrintColumn(x->getProblemSpaceIterations());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Printer::TableRowHeader(std::shared_ptr<celero::ExperimentResult> x)
|
||||||
|
{
|
||||||
|
TableRowExperimentHeader(x->getExperiment());
|
||||||
|
TableRowProblemSpaceHeader(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Printer::TableResult(std::shared_ptr<celero::ExperimentResult> x)
|
||||||
|
{
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Default);
|
||||||
|
|
||||||
|
celero::console::ConsoleColor temp_color;
|
||||||
|
|
||||||
|
// Slower than Baseline
|
||||||
|
if(x->getBaselineMeasurement() > 1.0)
|
||||||
|
{
|
||||||
|
temp_color = celero::console::ConsoleColor::Yellow;
|
||||||
|
}
|
||||||
|
else if(x->getBaselineMeasurement() < 1.0)
|
||||||
|
{
|
||||||
|
temp_color = celero::console::ConsoleColor::Green;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
temp_color = celero::console::ConsoleColor::Cyan;
|
||||||
|
}
|
||||||
|
|
||||||
|
celero::console::SetConsoleColor(temp_color);
|
||||||
|
std::cout << PrintColumn(x->getBaselineMeasurement());
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Default);
|
||||||
|
std::cout << " | ";
|
||||||
|
|
||||||
|
celero::console::SetConsoleColor(temp_color);
|
||||||
|
std::cout << PrintColumn(x->getUsPerCall());
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Default);
|
||||||
|
std::cout << " | ";
|
||||||
|
|
||||||
|
celero::console::SetConsoleColor(temp_color);
|
||||||
|
std::cout << PrintColumn(x->getCallsPerSecond(), 2);
|
||||||
|
celero::console::SetConsoleColor(celero::console::ConsoleColor::Default);
|
||||||
|
std::cout << " | ";
|
||||||
|
|
||||||
|
std::unordered_map<std::string, double> udmValues;
|
||||||
|
|
||||||
|
auto udmCollector = x->getUserDefinedMeasurements();
|
||||||
|
for(const auto& entry : udmCollector->getAggregateValues())
|
||||||
|
{
|
||||||
|
udmValues[entry.first] = entry.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t i = 0; i < this->userDefinedColumns.size(); ++i)
|
||||||
|
{
|
||||||
|
const auto& fieldName = this->userDefinedColumns[i];
|
||||||
|
|
||||||
|
if(udmValues.find(fieldName) == udmValues.end())
|
||||||
|
{
|
||||||
|
std::cout << PrintCenter("---", this->columnWidths[i + PrintConstants::NumberOfColumns]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << PrintColumn(udmValues.at(fieldName), 2, this->columnWidths[i + PrintConstants::NumberOfColumns]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Printer::initialize(std::vector<std::string> userDefinedColumnsIn)
|
||||||
|
{
|
||||||
|
this->userDefinedColumns = userDefinedColumnsIn;
|
||||||
|
|
||||||
|
this->columnWidths.clear();
|
||||||
|
this->columnWidths.resize(PrintConstants::NumberOfColumns, PrintConstants::ColumnWidth);
|
||||||
|
|
||||||
|
for(const auto& name : this->userDefinedColumns)
|
||||||
|
{
|
||||||
|
this->columnWidths.push_back(std::max(name.size() + 2, (size_t)PrintConstants::ColumnWidth));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace celero
|
65
lib/celero/Print.h
Normal file
65
lib/celero/Print.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
#ifndef H_CELERO_PRINT_H
|
||||||
|
#define H_CELERO_PRINT_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Experiment.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \class Printer
|
||||||
|
///
|
||||||
|
/// \author John farrier
|
||||||
|
///
|
||||||
|
class Printer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Singleton implementation.
|
||||||
|
static Printer& get()
|
||||||
|
{
|
||||||
|
static Printer p;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Initialize the Printer object with specific user-defined columns.
|
||||||
|
///
|
||||||
|
void initialize(std::vector<std::string> userDefinedColumns);
|
||||||
|
|
||||||
|
void Console(const std::string& x);
|
||||||
|
void TableBanner();
|
||||||
|
void TableRowExperimentHeader(Experiment* x);
|
||||||
|
void TableRowFailure(const std::string& msg);
|
||||||
|
void TableRowProblemSpaceHeader(std::shared_ptr<celero::ExperimentResult> x);
|
||||||
|
void TableRowHeader(std::shared_ptr<celero::ExperimentResult> x);
|
||||||
|
void TableResult(std::shared_ptr<celero::ExperimentResult> x);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Printer() = default;
|
||||||
|
|
||||||
|
std::vector<std::string> userDefinedColumns;
|
||||||
|
std::vector<size_t> columnWidths;
|
||||||
|
};
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
125
lib/celero/ResultTable.cpp
Normal file
125
lib/celero/ResultTable.cpp
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Benchmark.h>
|
||||||
|
#include <celero/PimplImpl.h>
|
||||||
|
#include <celero/ResultTable.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \class Impl
|
||||||
|
///
|
||||||
|
class celero::ResultTable::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl() : precision(5)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~Impl()
|
||||||
|
{
|
||||||
|
closeFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void closeFile()
|
||||||
|
{
|
||||||
|
if(this->ofs.is_open() == true)
|
||||||
|
{
|
||||||
|
this->ofs.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFileName(const std::string& x)
|
||||||
|
{
|
||||||
|
if(this->ofs.is_open() == true)
|
||||||
|
{
|
||||||
|
this->ofs.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
this->ofs.open(x);
|
||||||
|
|
||||||
|
// Print the header for the table.
|
||||||
|
if(this->ofs.is_open() == true)
|
||||||
|
{
|
||||||
|
this->ofs << "Group,Experiment,Problem "
|
||||||
|
"Space,Samples,Iterations,Failure,Baseline,";
|
||||||
|
this->ofs << "us/Iteration,Iterations/sec,Min (us),Mean (us),Max "
|
||||||
|
"(us),Variance,Standard Deviation,Skewness,Kurtosis,Z Score"
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string format(double x)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::fixed;
|
||||||
|
ss.precision(this->precision);
|
||||||
|
ss << x;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ofstream ofs;
|
||||||
|
const size_t precision;
|
||||||
|
};
|
||||||
|
|
||||||
|
ResultTable::ResultTable() : pimpl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultTable::~ResultTable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultTable& ResultTable::Instance()
|
||||||
|
{
|
||||||
|
static ResultTable singleton;
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResultTable::setFileName(const std::string& x)
|
||||||
|
{
|
||||||
|
assert(x.empty() == false);
|
||||||
|
this->pimpl->setFileName(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResultTable::closeFile()
|
||||||
|
{
|
||||||
|
this->pimpl->closeFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResultTable::add(std::shared_ptr<celero::ExperimentResult> x)
|
||||||
|
{
|
||||||
|
if(this->pimpl->ofs.is_open() == true)
|
||||||
|
{
|
||||||
|
this->pimpl->ofs << x->getExperiment()->getBenchmark()->getName() << "," << x->getExperiment()->getName() << "," << x->getProblemSpaceValue()
|
||||||
|
<< "," << x->getExperiment()->getSamples() << "," << x->getProblemSpaceIterations() << "," << x->getFailure() << ",";
|
||||||
|
|
||||||
|
this->pimpl->ofs << x->getBaselineMeasurement() << "," << x->getUsPerCall() << "," << x->getCallsPerSecond() << ","
|
||||||
|
<< x->getTimeStatistics()->getMin() << "," << x->getTimeStatistics()->getMean() << "," << x->getTimeStatistics()->getMax()
|
||||||
|
<< "," << x->getTimeStatistics()->getVariance() << "," << x->getTimeStatistics()->getStandardDeviation() << ","
|
||||||
|
<< x->getTimeStatistics()->getSkewness() << "," << x->getTimeStatistics()->getKurtosis() << ","
|
||||||
|
<< x->getTimeStatistics()->getZScore() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
87
lib/celero/ResultTable.h
Normal file
87
lib/celero/ResultTable.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#ifndef H_CELERO_RESULTTABLE_H
|
||||||
|
#define H_CELERO_RESULTTABLE_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Experiment.h>
|
||||||
|
#include <celero/Pimpl.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \class ResultTable
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
class ResultTable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Singleton
|
||||||
|
///
|
||||||
|
static ResultTable& Instance();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Specify a file name for a results output file.
|
||||||
|
///
|
||||||
|
/// \param x The name of the output file in which to store Celero's results.
|
||||||
|
///
|
||||||
|
void setFileName(const std::string& x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Force the output file (if any) to close
|
||||||
|
///
|
||||||
|
void closeFile();
|
||||||
|
///
|
||||||
|
/// Add a new result to the result table.
|
||||||
|
///
|
||||||
|
/// This should re-save on every new result so that the output can be monitored externally.
|
||||||
|
///
|
||||||
|
void add(std::shared_ptr<celero::ExperimentResult> x);
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
void save();
|
||||||
|
|
||||||
|
private:
|
||||||
|
///
|
||||||
|
/// Default Constructor
|
||||||
|
///
|
||||||
|
ResultTable();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Default Destructor
|
||||||
|
///
|
||||||
|
~ResultTable();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
class Impl;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
Pimpl<Impl> pimpl;
|
||||||
|
};
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
252
lib/celero/Statistics.h
Normal file
252
lib/celero/Statistics.h
Normal file
@ -0,0 +1,252 @@
|
|||||||
|
#ifndef H_CELERO_STATISTICS_H
|
||||||
|
#define H_CELERO_STATISTICS_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \class Statistics
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// Sources:
|
||||||
|
/// http://www.johndcook.com/skewness_kurtosis.html
|
||||||
|
/// http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
|
||||||
|
/// http://prod.sandia.gov/techlib/access-control.cgi/2008/086212.pdf
|
||||||
|
/// http://en.wikipedia.org/wiki/Kurtosis
|
||||||
|
///
|
||||||
|
template <typename T = int64_t>
|
||||||
|
class Statistics
|
||||||
|
{
|
||||||
|
static_assert(std::is_arithmetic<T>::value, "Statistics requres an arithmetic type.");
|
||||||
|
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// \brief Default constructor
|
||||||
|
///
|
||||||
|
Statistics() = default;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Statistics(const Statistics& other) :
|
||||||
|
sampleSize(other.sampleSize),
|
||||||
|
M1(other.M1),
|
||||||
|
M2(other.M2),
|
||||||
|
M3(other.M3),
|
||||||
|
M4(other.M4),
|
||||||
|
min(other.min),
|
||||||
|
max(other.max)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
~Statistics() = default;
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
///
|
||||||
|
Statistics operator+(const Statistics<T>& other)
|
||||||
|
{
|
||||||
|
Statistics<T> combined;
|
||||||
|
|
||||||
|
combined.sampleSize = this->sampleSize + other.sampleSize;
|
||||||
|
|
||||||
|
const auto delta = other.M1 - this->M1;
|
||||||
|
const auto delta2 = delta * delta;
|
||||||
|
const auto delta3 = delta * delta2;
|
||||||
|
const auto delta4 = delta2 * delta2;
|
||||||
|
|
||||||
|
combined.M1 = (this->sampleSize * this->M1 + other.sampleSize * other.M1) / combined.sampleSize;
|
||||||
|
|
||||||
|
combined.M2 = this->M2 + other.M2 + delta2 * this->sampleSize * other.sampleSize / combined.sampleSize;
|
||||||
|
|
||||||
|
combined.M3 =
|
||||||
|
this->M3 + other.M3
|
||||||
|
+ delta3 * this->sampleSize * other.sampleSize * (this->sampleSize - other.sampleSize) / (combined.sampleSize * combined.sampleSize);
|
||||||
|
|
||||||
|
combined.M3 += 3.0 * delta * (this->sampleSize * other.M2 - other.sampleSize * this->M2) / combined.sampleSize;
|
||||||
|
|
||||||
|
combined.M4 = this->M4 + other.M4
|
||||||
|
+ delta4 * this->sampleSize * other.sampleSize
|
||||||
|
* (this->sampleSize * this->sampleSize - this->sampleSize * other.sampleSize + other.sampleSize * other.sampleSize)
|
||||||
|
/ (combined.sampleSize * combined.sampleSize * combined.sampleSize);
|
||||||
|
|
||||||
|
combined.M4 += 6.0 * delta2 * (this->sampleSize * this->sampleSize * other.M2 + other.sampleSize * other.sampleSize * this->M2)
|
||||||
|
/ (combined.sampleSize * combined.sampleSize)
|
||||||
|
+ 4.0 * delta * (this->sampleSize * other.M3 - other.sampleSize * this->M3) / combined.sampleSize;
|
||||||
|
|
||||||
|
combined.min = std::min(this->min, other.min);
|
||||||
|
combined.max = std::max(this->max, other.max);
|
||||||
|
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
Statistics& operator+=(const Statistics& other)
|
||||||
|
{
|
||||||
|
const auto combined = *this + other;
|
||||||
|
*this = combined;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
Statistics& operator=(const Statistics& other)
|
||||||
|
{
|
||||||
|
this->sampleSize = other.sampleSize;
|
||||||
|
this->M1 = other.M1;
|
||||||
|
this->M2 = other.M2;
|
||||||
|
this->M3 = other.M3;
|
||||||
|
this->M4 = other.M4;
|
||||||
|
this->min = other.min;
|
||||||
|
this->max = other.max;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Resets all accumulated statistics.
|
||||||
|
///
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
this->sampleSize = 0;
|
||||||
|
this->M1 = 0;
|
||||||
|
this->M2 = 0;
|
||||||
|
this->M3 = 0;
|
||||||
|
this->M4 = 0;
|
||||||
|
this->min = std::numeric_limits<decltype(this->min)>::max();
|
||||||
|
this->max = std::numeric_limits<decltype(this->max)>::min();
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Adds a statistical sample.
|
||||||
|
///
|
||||||
|
void addSample(T x)
|
||||||
|
{
|
||||||
|
const auto n1 = this->sampleSize;
|
||||||
|
this->sampleSize++;
|
||||||
|
|
||||||
|
const auto delta = x - this->M1;
|
||||||
|
const auto delta_n = delta / this->sampleSize;
|
||||||
|
const auto delta_n2 = delta_n * delta_n;
|
||||||
|
const auto term1 = delta * delta_n * n1;
|
||||||
|
|
||||||
|
this->M1 += delta_n;
|
||||||
|
this->M4 += term1 * delta_n2 * (this->sampleSize * this->sampleSize - 3 * this->sampleSize + 3) + 6 * delta_n2 * this->M2
|
||||||
|
- 4 * delta_n * this->M3;
|
||||||
|
this->M3 += term1 * delta_n * (this->sampleSize - 2) - 3 * delta_n * this->M2;
|
||||||
|
this->M2 += term1;
|
||||||
|
|
||||||
|
this->min = std::min(this->min, x);
|
||||||
|
this->max = std::max(this->max, x);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getSize() const
|
||||||
|
{
|
||||||
|
return this->sampleSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getMean() const
|
||||||
|
{
|
||||||
|
return this->M1;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getVariance() const
|
||||||
|
{
|
||||||
|
if(this->sampleSize > 1)
|
||||||
|
{
|
||||||
|
return this->M2 / (this->sampleSize - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getStandardDeviation() const
|
||||||
|
{
|
||||||
|
return std::sqrt(this->getVariance());
|
||||||
|
}
|
||||||
|
|
||||||
|
double getSkewness() const
|
||||||
|
{
|
||||||
|
if(this->sampleSize > 2)
|
||||||
|
{
|
||||||
|
return sqrt(this->sampleSize) * this->M3 / pow(this->M2, 1.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double getKurtosis() const
|
||||||
|
{
|
||||||
|
if(this->sampleSize > 3)
|
||||||
|
{
|
||||||
|
if(this->M2 != 0)
|
||||||
|
{
|
||||||
|
return this->sampleSize * this->M4 / (this->M2 * this->M2) - 3.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Computed as (mean - hypothesis)/standard_deviation
|
||||||
|
///
|
||||||
|
/// Here, the hypothesis is our minimum value.
|
||||||
|
///
|
||||||
|
double getZScore() const
|
||||||
|
{
|
||||||
|
const auto sd = this->getStandardDeviation();
|
||||||
|
|
||||||
|
if(sd != 0.0)
|
||||||
|
{
|
||||||
|
return (this->getMean() - static_cast<double>(this->getMin())) / sd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
T getMin() const
|
||||||
|
{
|
||||||
|
return this->min;
|
||||||
|
}
|
||||||
|
|
||||||
|
T getMax() const
|
||||||
|
{
|
||||||
|
return this->max;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t sampleSize{0};
|
||||||
|
double M1{0.0};
|
||||||
|
double M2{0.0};
|
||||||
|
double M3{0.0};
|
||||||
|
double M4{0.0};
|
||||||
|
T min{std::numeric_limits<T>::max()};
|
||||||
|
T max{std::numeric_limits<T>::min()};
|
||||||
|
};
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
145
lib/celero/TestFixture.cpp
Normal file
145
lib/celero/TestFixture.cpp
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <assert.h>
|
||||||
|
#include <celero/TestFixture.h>
|
||||||
|
#include <celero/UserDefinedMeasurement.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
TestFixture::TestFixture()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TestFixture::~TestFixture()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFixture::onExperimentStart(const celero::TestFixture::ExperimentValue&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFixture::onExperimentEnd()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFixture::setUp(const celero::TestFixture::ExperimentValue&)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFixture::setExperimentIterations(uint64_t x)
|
||||||
|
{
|
||||||
|
this->experimentIterations = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t TestFixture::getExperimentIterations() const
|
||||||
|
{
|
||||||
|
return this->experimentIterations;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFixture::setExperimentTime(uint64_t x)
|
||||||
|
{
|
||||||
|
this->experimentTime = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t TestFixture::getExperimentTime() const
|
||||||
|
{
|
||||||
|
return this->experimentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFixture::tearDown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t TestFixture::run(const uint64_t, const uint64_t iterations, const celero::TestFixture::ExperimentValue& experimentValue)
|
||||||
|
{
|
||||||
|
// This function constitutes one sample consisting of several iterations for a single experiment value.
|
||||||
|
|
||||||
|
if(this->HardCodedMeasurement() == 0)
|
||||||
|
{
|
||||||
|
uint64_t totalTime = 0;
|
||||||
|
|
||||||
|
// Set up the testing fixture.
|
||||||
|
this->setUp(experimentValue);
|
||||||
|
this->setExperimentIterations(iterations);
|
||||||
|
|
||||||
|
// Run the test body for each iterations.
|
||||||
|
auto iterationCounter = iterations;
|
||||||
|
|
||||||
|
// Get the starting time.
|
||||||
|
const auto startTime = celero::timer::GetSystemTime();
|
||||||
|
|
||||||
|
// Count down to zero
|
||||||
|
// Iterations are used when the benchmarks are very fast.
|
||||||
|
// Do not start/stop the timer inside this loop.
|
||||||
|
// The purpose of the loop is to help counter timer quantization/errors.
|
||||||
|
while(iterationCounter--)
|
||||||
|
{
|
||||||
|
this->onExperimentStart(experimentValue);
|
||||||
|
|
||||||
|
this->UserBenchmark();
|
||||||
|
|
||||||
|
this->onExperimentEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
// See how long it took.
|
||||||
|
totalTime += celero::timer::GetSystemTime() - startTime;
|
||||||
|
|
||||||
|
this->setExperimentTime(totalTime);
|
||||||
|
|
||||||
|
// Tear down the testing fixture.
|
||||||
|
this->tearDown();
|
||||||
|
|
||||||
|
// Return the duration in microseconds for the given problem size.
|
||||||
|
return totalTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->HardCodedMeasurement();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestFixture::UserBenchmark()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t TestFixture::HardCodedMeasurement() const
|
||||||
|
{
|
||||||
|
return uint64_t(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<UserDefinedMeasurement>> TestFixture::getUserDefinedMeasurements() const
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> TestFixture::getUserDefinedMeasurementNames() const
|
||||||
|
{
|
||||||
|
std::vector<std::string> names;
|
||||||
|
const auto udms = this->getUserDefinedMeasurements();
|
||||||
|
|
||||||
|
if(udms.empty() == false)
|
||||||
|
{
|
||||||
|
for(const auto udm : udms)
|
||||||
|
{
|
||||||
|
names.emplace_back(udm->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
216
lib/celero/TestFixture.h
Normal file
216
lib/celero/TestFixture.h
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
#ifndef H_CELERO_TESTFIXTURE_H
|
||||||
|
#define H_CELERO_TESTFIXTURE_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Export.h>
|
||||||
|
#include <celero/Timer.h>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// This must be included last.
|
||||||
|
#include <celero/ThreadLocal.h>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
class Benchmark;
|
||||||
|
class UserDefinedMeasurement;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \class TestFixture
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
class CELERO_EXPORT TestFixture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Default Constructor.
|
||||||
|
///
|
||||||
|
TestFixture();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Virtual destructor for inheritance.
|
||||||
|
///
|
||||||
|
virtual ~TestFixture();
|
||||||
|
|
||||||
|
enum class Constants : int64_t
|
||||||
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#if(_MSC_VER < 1900)
|
||||||
|
NoProblemSpaceValue = -9223372036854775807
|
||||||
|
#else
|
||||||
|
NoProblemSpaceValue = std::numeric_limits<int64_t>::min()
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
NoProblemSpaceValue = std::numeric_limits<int64_t>::min()
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \class ExperimentValue
|
||||||
|
///
|
||||||
|
/// You can derive from this type to add your own information to the experiment value set.
|
||||||
|
///
|
||||||
|
class ExperimentValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ExperimentValue() = default;
|
||||||
|
ExperimentValue(int64_t v) : Value(v){};
|
||||||
|
ExperimentValue(int64_t v, int64_t i) : Value(v), Iterations(i){};
|
||||||
|
|
||||||
|
virtual ~ExperimentValue() = default;
|
||||||
|
|
||||||
|
/// An arbitrary integer value which can be used or translated for use by the test fixture.
|
||||||
|
int64_t Value{0};
|
||||||
|
|
||||||
|
/// The number of iterations to do with this test value. 0 (default) indicates that the default number of iterations set up for the test
|
||||||
|
/// case should be used.
|
||||||
|
int64_t Iterations{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Allows a test fixture to supply values to use for experiments.
|
||||||
|
///
|
||||||
|
/// This is used to create multiple runs of the same experiment
|
||||||
|
/// and varrying the data set size, for example. The second value
|
||||||
|
/// of the pair is an optional override for the number of iterations
|
||||||
|
/// to be used. If zero is specified, then the default number of
|
||||||
|
/// iterations is used.
|
||||||
|
///
|
||||||
|
/// It is only guaranteed that the constructor is called prior to this function being called.
|
||||||
|
///
|
||||||
|
virtual std::vector<celero::TestFixture::ExperimentValue> getExperimentValues() const
|
||||||
|
{
|
||||||
|
return std::vector<celero::TestFixture::ExperimentValue>();
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Provide a units result scale of each experiment value.
|
||||||
|
///
|
||||||
|
/// If the value is greater than 0 then additional statistic value will be printed
|
||||||
|
/// in output - [ xxxx units/sec ]. For example for measure speed of
|
||||||
|
/// file IO operations method might return 1024 * 1024 to get megabytes
|
||||||
|
/// per second.
|
||||||
|
///
|
||||||
|
/// It is only guaranteed that the constructor is called prior to this function being called.
|
||||||
|
///
|
||||||
|
virtual double getExperimentValueResultScale() const
|
||||||
|
{
|
||||||
|
return 1.0;
|
||||||
|
};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Allows the text fixture to run code that will be executed once immediately before the benchmark.
|
||||||
|
///
|
||||||
|
/// Unlike setUp, the evaluation of this function IS included in the total experiment execution
|
||||||
|
/// time.
|
||||||
|
///
|
||||||
|
/// \param x The value for the experiment. This can be ignored if the test does not utilize experiment values.
|
||||||
|
///
|
||||||
|
virtual void onExperimentStart(const celero::TestFixture::ExperimentValue& x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Allows the text fixture to run code that will be executed once immediately after the benchmark.
|
||||||
|
/// Unlike tearDown, the evaluation of this function IS included in the total experiment execution
|
||||||
|
/// time.
|
||||||
|
///
|
||||||
|
virtual void onExperimentEnd();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Set up the test fixture before benchmark execution.
|
||||||
|
///
|
||||||
|
/// This code is NOT included in the benchmark timing.
|
||||||
|
/// It is executed once before all iterations are executed and between each Sample.
|
||||||
|
/// Your experiment should NOT rely on "setUp" methods to be called before EACH experiment run, only between each sample.
|
||||||
|
///
|
||||||
|
/// \param x The celero::TestFixture::ExperimentValue for the experiment. This can be ignored if the test does not utilize experiment values.
|
||||||
|
///
|
||||||
|
virtual void setUp(const celero::TestFixture::ExperimentValue& x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Internal to Celero
|
||||||
|
///
|
||||||
|
void setExperimentTime(uint64_t x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Valid inside tearDown().
|
||||||
|
///
|
||||||
|
uint64_t getExperimentTime() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Internal to Celero
|
||||||
|
///
|
||||||
|
void setExperimentIterations(uint64_t x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Valid inside tearDown().
|
||||||
|
///
|
||||||
|
uint64_t getExperimentIterations() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Called after test completion to destroy the fixture.
|
||||||
|
///
|
||||||
|
/// This code is NOT included in the benchmark timing.
|
||||||
|
/// It is executed once after all iterations are executed and between each Sample.
|
||||||
|
/// Your experiment should NOT rely on "tearDown" methods to be called after EACH experiment run, only between each sample.
|
||||||
|
///
|
||||||
|
virtual void tearDown();
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// \param threads The number of working threads.
|
||||||
|
/// \param iterations The number of times to loop over the UserBenchmark function.
|
||||||
|
/// \param experimentValue The experiment value to pass in setUp function.
|
||||||
|
///
|
||||||
|
/// \return Returns the number of microseconds the run took.
|
||||||
|
///
|
||||||
|
virtual uint64_t run(uint64_t threads, uint64_t iterations, const celero::TestFixture::ExperimentValue& experimentValue);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief If you want to use user-defined measurements, override this method to return them
|
||||||
|
///
|
||||||
|
/// This method must return a vector of pointers, one per type of user-defined measurement that you want to measure.
|
||||||
|
///
|
||||||
|
virtual std::vector<std::shared_ptr<UserDefinedMeasurement>> getUserDefinedMeasurements() const;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Returns the names of all user-defined measurements in this fixture.
|
||||||
|
///
|
||||||
|
std::vector<std::string> getUserDefinedMeasurementNames() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// Executed for each operation the benchmarking test is run.
|
||||||
|
virtual void UserBenchmark();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Only used for baseline cases. Used to define a hard-coded execution time vs. actually making a measurement.
|
||||||
|
///
|
||||||
|
virtual uint64_t HardCodedMeasurement() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint64_t experimentIterations{0};
|
||||||
|
uint64_t experimentTime{0};
|
||||||
|
};
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
96
lib/celero/TestVector.cpp
Normal file
96
lib/celero/TestVector.cpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Benchmark.h>
|
||||||
|
#include <celero/PimplImpl.h>
|
||||||
|
#include <celero/TestVector.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \class Impl
|
||||||
|
///
|
||||||
|
class TestVector::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Impl() : testVectorMutex(), testVector()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~Impl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
mutable std::mutex testVectorMutex;
|
||||||
|
std::vector<std::shared_ptr<Benchmark>> testVector;
|
||||||
|
};
|
||||||
|
|
||||||
|
TestVector::TestVector() : pimpl()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TestVector::~TestVector()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
TestVector& TestVector::Instance()
|
||||||
|
{
|
||||||
|
static TestVector singleton;
|
||||||
|
return singleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestVector::push_back(std::shared_ptr<Benchmark> x)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> mutexLock(this->pimpl->testVectorMutex);
|
||||||
|
this->pimpl->testVector.push_back(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestVector::clear()
|
||||||
|
{
|
||||||
|
this->pimpl->testVector.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TestVector::size() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> mutexLock(this->pimpl->testVectorMutex);
|
||||||
|
return this->pimpl->testVector.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Benchmark> TestVector::operator[](size_t x)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> mutexLock(this->pimpl->testVectorMutex);
|
||||||
|
return this->pimpl->testVector[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Benchmark> TestVector::operator[](const std::string& x)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> mutexLock(this->pimpl->testVectorMutex);
|
||||||
|
|
||||||
|
const auto found = std::find_if(std::begin(this->pimpl->testVector), std::end(this->pimpl->testVector),
|
||||||
|
[x](std::shared_ptr<Benchmark> const& bmark) -> bool { return (bmark->getName() == x); });
|
||||||
|
|
||||||
|
if(found != std::end(this->pimpl->testVector))
|
||||||
|
{
|
||||||
|
return *found;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
67
lib/celero/TestVector.h
Normal file
67
lib/celero/TestVector.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#ifndef H_CELERO_TESTVECTOR_H
|
||||||
|
#define H_CELERO_TESTVECTOR_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Benchmark.h>
|
||||||
|
#include <celero/Export.h>
|
||||||
|
#include <celero/Pimpl.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \class TestVector
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
class CELERO_EXPORT TestVector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
~TestVector();
|
||||||
|
|
||||||
|
static TestVector& Instance();
|
||||||
|
|
||||||
|
void push_back(std::shared_ptr<Benchmark> x);
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
size_t size() const;
|
||||||
|
|
||||||
|
std::shared_ptr<Benchmark> operator[](size_t x);
|
||||||
|
std::shared_ptr<Benchmark> operator[](const std::string& x);
|
||||||
|
|
||||||
|
private:
|
||||||
|
///
|
||||||
|
/// Default Constructor
|
||||||
|
///
|
||||||
|
TestVector();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
class Impl;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Pimpl Idiom
|
||||||
|
///
|
||||||
|
Pimpl<Impl> pimpl;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
36
lib/celero/ThreadLocal.h
Normal file
36
lib/celero/ThreadLocal.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#ifndef H_CELERO_THREADLOCAL_H
|
||||||
|
#define H_CELERO_THREADLOCAL_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author Ivan Shynkarenka
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifndef thread_local
|
||||||
|
|
||||||
|
#if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__
|
||||||
|
#define thread_local _Thread_local
|
||||||
|
#elif defined _WIN32 && (defined _MSC_VER || defined __ICL || defined __DMC__ || defined __BORLANDC__)
|
||||||
|
#define thread_local __declspec(thread)
|
||||||
|
/* note that ICC (linux) and Clang are covered by __GNUC__ */
|
||||||
|
#elif defined __GNUC__ || defined __SUNPRO_C || defined __xlC__
|
||||||
|
#define thread_local __thread
|
||||||
|
#else
|
||||||
|
#error "Cannot define thread_local"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
124
lib/celero/ThreadTestFixture.cpp
Normal file
124
lib/celero/ThreadTestFixture.cpp
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
///
|
||||||
|
/// \author Ivan Shynkarenka
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/ThreadTestFixture.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <celero/PimplImpl.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <future>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
class ThreadTestFixture::Impl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static thread_local uint64_t currentCallId;
|
||||||
|
static thread_local uint64_t currentThreadId;
|
||||||
|
std::vector<std::future<void>> futures;
|
||||||
|
};
|
||||||
|
|
||||||
|
thread_local uint64_t ThreadTestFixture::Impl::currentCallId = 0;
|
||||||
|
thread_local uint64_t ThreadTestFixture::Impl::currentThreadId = 0;
|
||||||
|
|
||||||
|
ThreadTestFixture::ThreadTestFixture() : TestFixture()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadTestFixture::~ThreadTestFixture()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadTestFixture::startThreads(uint64_t threads, uint64_t iterations)
|
||||||
|
{
|
||||||
|
const uint64_t iterationsPerThread = iterations / threads;
|
||||||
|
|
||||||
|
for(uint64_t i = 0; i < threads; ++i)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this->pimpl->futures.push_back(
|
||||||
|
// std::async(std::launch::deferred,
|
||||||
|
std::async(std::launch::async, [this, i, iterationsPerThread]() {
|
||||||
|
this->pimpl->currentThreadId = i + 1;
|
||||||
|
for(auto threadIterationCounter = size_t(0); threadIterationCounter < iterationsPerThread;)
|
||||||
|
{
|
||||||
|
this->pimpl->currentCallId = ++threadIterationCounter;
|
||||||
|
this->UserBenchmark();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
catch(std::system_error& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Exception. Error Code: " << e.code() << ", " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThreadTestFixture::stopThreads()
|
||||||
|
{
|
||||||
|
// This part will be more effective after
|
||||||
|
// wait_for_all() will be avaliable for futures!
|
||||||
|
for(auto& f : this->pimpl->futures)
|
||||||
|
{
|
||||||
|
if(f.valid() == true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
f.wait();
|
||||||
|
}
|
||||||
|
catch(std::system_error& e)
|
||||||
|
{
|
||||||
|
std::cerr << "Exception. Error Code: " << e.code() << ", " << e.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t ThreadTestFixture::run(uint64_t threads, uint64_t calls, const celero::TestFixture::ExperimentValue& experimentValue)
|
||||||
|
{
|
||||||
|
if(this->HardCodedMeasurement() == 0)
|
||||||
|
{
|
||||||
|
// Set up the testing fixture.
|
||||||
|
this->setUp(experimentValue);
|
||||||
|
|
||||||
|
// Get the starting time.
|
||||||
|
const auto startTime = celero::timer::GetSystemTime();
|
||||||
|
|
||||||
|
this->onExperimentStart(experimentValue);
|
||||||
|
|
||||||
|
// Start working threads.
|
||||||
|
this->startThreads(threads, calls);
|
||||||
|
|
||||||
|
// Stop working threads.
|
||||||
|
this->stopThreads();
|
||||||
|
|
||||||
|
this->onExperimentEnd();
|
||||||
|
|
||||||
|
const auto endTime = celero::timer::GetSystemTime();
|
||||||
|
|
||||||
|
// Tear down the testing fixture.
|
||||||
|
this->tearDown();
|
||||||
|
|
||||||
|
// Return the duration in microseconds for the given problem size.
|
||||||
|
return (endTime - startTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->HardCodedMeasurement();
|
||||||
|
}
|
75
lib/celero/ThreadTestFixture.h
Normal file
75
lib/celero/ThreadTestFixture.h
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#ifndef H_CELERO_THREADTESTFIXTURE_H
|
||||||
|
#define H_CELERO_THREADTESTFIXTURE_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author Ivan Shynkarenka
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Pimpl.h>
|
||||||
|
#include <celero/TestFixture.h>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
class Benchmark;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \class ThreadTestFixture
|
||||||
|
///
|
||||||
|
/// \author Ivan Shynkarenka
|
||||||
|
///
|
||||||
|
class CELERO_EXPORT ThreadTestFixture : public TestFixture
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Default Constructor.
|
||||||
|
///
|
||||||
|
ThreadTestFixture();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Virtual destructor for inheritance.
|
||||||
|
///
|
||||||
|
virtual ~ThreadTestFixture();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Start threads before benchmark execution.
|
||||||
|
///
|
||||||
|
/// \param threads Count of working threads to start.
|
||||||
|
/// \param calls The total number of times to loop over the UserBenchmark function.
|
||||||
|
///
|
||||||
|
virtual void startThreads(uint64_t threads, uint64_t calls);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Called after test completion to stop threads.
|
||||||
|
///
|
||||||
|
virtual void stopThreads();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \param threads The number of working threads.
|
||||||
|
/// \param calls The total number of times to loop over the UserBenchmark function.
|
||||||
|
/// \param experimentValue The experiment value to pass in setUp function.
|
||||||
|
///
|
||||||
|
/// \return Returns the number of microseconds the run took.
|
||||||
|
///
|
||||||
|
uint64_t run(uint64_t threads, uint64_t calls, const celero::TestFixture::ExperimentValue& experimentValue) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
class Impl;
|
||||||
|
Pimpl<Impl> pimpl;
|
||||||
|
};
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
74
lib/celero/Timer.cpp
Normal file
74
lib/celero/Timer.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Print.h>
|
||||||
|
#include <celero/Timer.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
LARGE_INTEGER QPCFrequency;
|
||||||
|
#else
|
||||||
|
#include <chrono>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint64_t celero::timer::GetSystemTime()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
LARGE_INTEGER timeStorage;
|
||||||
|
QueryPerformanceCounter(&timeStorage);
|
||||||
|
if(QPCFrequency.QuadPart != 0)
|
||||||
|
{
|
||||||
|
return static_cast<uint64_t>(timeStorage.QuadPart * 1000000) / static_cast<uint64_t>(QPCFrequency.QuadPart);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
auto timePoint = std::chrono::high_resolution_clock::now();
|
||||||
|
return std::chrono::duration_cast<std::chrono::microseconds>(timePoint.time_since_epoch()).count();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
double celero::timer::CachePerformanceFrequency(bool quiet)
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
QueryPerformanceFrequency(&QPCFrequency);
|
||||||
|
if(QPCFrequency.QuadPart == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto precision = ((1.0 / static_cast<double>(QPCFrequency.QuadPart)) * 1000000.0);
|
||||||
|
#else
|
||||||
|
if(static_cast<double>(std::chrono::high_resolution_clock::period::den) == 0)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto precision =
|
||||||
|
(static_cast<double>(std::chrono::high_resolution_clock::period::num) / static_cast<double>(std::chrono::high_resolution_clock::period::den))
|
||||||
|
* 1000000.0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(quiet == false)
|
||||||
|
{
|
||||||
|
std::cout << "Timer resolution: " << std::to_string(precision) << " us" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return precision;
|
||||||
|
}
|
70
lib/celero/Timer.h
Normal file
70
lib/celero/Timer.h
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
#ifndef H_CELERO_TIMER_H
|
||||||
|
#define H_CELERO_TIMER_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Utilities.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \namespace timer
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \brief Provide basic cross-platform timing functions to measure code performance speed.
|
||||||
|
///
|
||||||
|
namespace timer
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \brief Retrieves the current time.
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \return The time, in ticks.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT uint64_t GetSystemTime();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Converts the gathered system time into seconds.
|
||||||
|
///
|
||||||
|
/// This assumes "x" is a delta and relatively small (easily fits inside of a double).
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \param x The time, in ticks.
|
||||||
|
///
|
||||||
|
/// \return The time, in seconds.
|
||||||
|
///
|
||||||
|
constexpr double ConvertSystemTime(const uint64_t x)
|
||||||
|
{
|
||||||
|
return x * celero::UsToSec;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// On Windows, this caches the frequency of the high performance clock.
|
||||||
|
///
|
||||||
|
/// \return The number of microseconds of precision that we have.
|
||||||
|
///
|
||||||
|
CELERO_EXPORT double CachePerformanceFrequency(bool quiet);
|
||||||
|
} // namespace timer
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
76
lib/celero/UserDefinedMeasurement.h
Normal file
76
lib/celero/UserDefinedMeasurement.h
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
#ifndef H_CELERO_USERDEFINEDMEASUREMENT_H
|
||||||
|
#define H_CELERO_USERDEFINEDMEASUREMENT_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author Lukas Barth
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Export.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
class UserDefinedMeasurement;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Describes, which aggregations should be computed on a user-defined measurement.
|
||||||
|
///
|
||||||
|
/// The string names the aggregation, the UDMAggregateFunction is the function that will be called on the collected vector of user-defined
|
||||||
|
/// measurements.
|
||||||
|
///
|
||||||
|
using UDMAggregationTable = std::vector<std::pair<std::string, std::function<double(void)>>>;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \class UserDefinedMeasurement
|
||||||
|
///
|
||||||
|
/// Base class that the user must derive user-defined measurements from.
|
||||||
|
///
|
||||||
|
/// \author Lukas Barth
|
||||||
|
///
|
||||||
|
class CELERO_EXPORT UserDefinedMeasurement
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// \brief Must be implemented by the user. Must return a specification which aggregations the user wants to be computed.
|
||||||
|
///
|
||||||
|
virtual UDMAggregationTable getAggregationInfo() const = 0;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Must be implemented by the user. Must return the name of this user-defined measurement.
|
||||||
|
///
|
||||||
|
virtual std::string getName() const = 0;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Combine the results of two user defined measurements.
|
||||||
|
///
|
||||||
|
/// As TestFixture classes are created and destroyed, this provides a mechanisim to preserve data. Internally, this function is used so that
|
||||||
|
/// each unique set of (group, experiment, problem space) has its own combined set of user defined measurements.
|
||||||
|
///
|
||||||
|
virtual void merge(const UserDefinedMeasurement* const x) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Class may never be directly instantiated
|
||||||
|
UserDefinedMeasurement() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
94
lib/celero/UserDefinedMeasurementCollector.cpp
Normal file
94
lib/celero/UserDefinedMeasurementCollector.cpp
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
///
|
||||||
|
/// \author Lukas Barth
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <assert.h>
|
||||||
|
#include <celero/TestFixture.h>
|
||||||
|
#include <celero/UserDefinedMeasurementCollector.h>
|
||||||
|
|
||||||
|
using namespace celero;
|
||||||
|
|
||||||
|
UserDefinedMeasurementCollector::UserDefinedMeasurementCollector(std::shared_ptr<TestFixture> fixture)
|
||||||
|
{
|
||||||
|
const auto udm = fixture->getUserDefinedMeasurementNames();
|
||||||
|
|
||||||
|
if(udm.empty() == false)
|
||||||
|
{
|
||||||
|
for(auto name : fixture->getUserDefinedMeasurementNames())
|
||||||
|
{
|
||||||
|
this->collected[name] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UserDefinedMeasurementCollector::collect(std::shared_ptr<TestFixture> fixture)
|
||||||
|
{
|
||||||
|
const auto udms = fixture->getUserDefinedMeasurements();
|
||||||
|
|
||||||
|
if(udms.empty() == false)
|
||||||
|
{
|
||||||
|
for(auto udm : udms)
|
||||||
|
{
|
||||||
|
if(this->collected[udm->getName()] == nullptr)
|
||||||
|
{
|
||||||
|
this->collected[udm->getName()] = udm;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this->collected[udm->getName()]->merge(&*udm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> UserDefinedMeasurementCollector::getFields(std::shared_ptr<TestFixture> fixture) const
|
||||||
|
{
|
||||||
|
std::vector<std::string> fields;
|
||||||
|
const auto udms = fixture->getUserDefinedMeasurements();
|
||||||
|
|
||||||
|
if(udms.empty() == false)
|
||||||
|
{
|
||||||
|
for(auto udm : udms)
|
||||||
|
{
|
||||||
|
for(const auto& aggDesc : udm->getAggregationInfo())
|
||||||
|
{
|
||||||
|
fields.emplace_back(std::string(udm->getName()) + std::string(" ") + std::string(aggDesc.first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::pair<std::string, double>> UserDefinedMeasurementCollector::getAggregateValues() const
|
||||||
|
{
|
||||||
|
std::vector<std::pair<std::string, double>> aggregates;
|
||||||
|
|
||||||
|
for(const auto& collectedEntry : this->collected)
|
||||||
|
{
|
||||||
|
const auto name = collectedEntry.first;
|
||||||
|
const auto collectedUDMs = collectedEntry.second;
|
||||||
|
|
||||||
|
for(const auto& aggDesc : collectedUDMs->getAggregationInfo())
|
||||||
|
{
|
||||||
|
const auto fieldName = name + std::string(" ") + aggDesc.first;
|
||||||
|
aggregates.emplace_back(fieldName, (aggDesc.second)());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return aggregates;
|
||||||
|
}
|
47
lib/celero/UserDefinedMeasurementCollector.h
Normal file
47
lib/celero/UserDefinedMeasurementCollector.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#ifndef H_CELERO_USERDEFINEDMEASUREMENTCOLLECTOR_H
|
||||||
|
#define H_CELERO_USERDEFINEDMEASUREMENTCOLLECTOR_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author Lukas Barth
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/UserDefinedMeasurement.h>
|
||||||
|
#include <celero/TestFixture.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \class UserDefinedMeasurementCollector
|
||||||
|
///
|
||||||
|
/// \author Lukas Barth
|
||||||
|
///
|
||||||
|
class CELERO_EXPORT UserDefinedMeasurementCollector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UserDefinedMeasurementCollector(std::shared_ptr<TestFixture> fixture);
|
||||||
|
|
||||||
|
void collect(std::shared_ptr<TestFixture> fixture);
|
||||||
|
std::vector<std::string> getFields(std::shared_ptr<TestFixture> fixture) const;
|
||||||
|
std::vector<std::pair<std::string, double>> getAggregateValues() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unordered_map<std::string, std::shared_ptr<celero::UserDefinedMeasurement>> collected;
|
||||||
|
};
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
182
lib/celero/UserDefinedMeasurementTemplate.h
Normal file
182
lib/celero/UserDefinedMeasurementTemplate.h
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
#ifndef H_CELERO_USERDEFINEDMEASUREMENTTEMPLATE_H
|
||||||
|
#define H_CELERO_USERDEFINEDMEASUREMENTTEMPLATE_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author Lukas Barth, John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Statistics.h>
|
||||||
|
#include <celero/UserDefinedMeasurement.h>
|
||||||
|
#include <numeric>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \class UserDefinedMeasurementTemplate
|
||||||
|
///
|
||||||
|
/// Base class that the user must derive user-defined measurements from.
|
||||||
|
///
|
||||||
|
/// \author Lukas Barth, John Farrier
|
||||||
|
///
|
||||||
|
template <typename T>
|
||||||
|
class UserDefinedMeasurementTemplate : public UserDefinedMeasurement
|
||||||
|
{
|
||||||
|
static_assert(std::is_arithmetic<T>::value, "UserDefinedMeasurementTemplate requres an arithmetic type.");
|
||||||
|
|
||||||
|
public:
|
||||||
|
///
|
||||||
|
/// Default constructor
|
||||||
|
///
|
||||||
|
UserDefinedMeasurementTemplate() = default;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Default destructor
|
||||||
|
///
|
||||||
|
virtual ~UserDefinedMeasurementTemplate() = default;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief Must be implemented by the user. Must return a specification which aggregations the user wants to be computed.
|
||||||
|
///
|
||||||
|
virtual UDMAggregationTable getAggregationInfo() const override
|
||||||
|
{
|
||||||
|
UDMAggregationTable table;
|
||||||
|
|
||||||
|
if(this->reportSize() == true)
|
||||||
|
{
|
||||||
|
table.push_back({"# Samp", [this]() { return static_cast<double>(this->getStatistics().getSize()); }});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->reportMean() == true)
|
||||||
|
{
|
||||||
|
table.push_back({"Mean", [this]() { return this->getStatistics().getMean(); }});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->reportVariance() == true)
|
||||||
|
{
|
||||||
|
table.push_back({"Var", [this]() { return this->getStatistics().getVariance(); }});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->reportStandardDeviation() == true)
|
||||||
|
{
|
||||||
|
table.push_back({"StdDev", [this]() { return this->getStatistics().getStandardDeviation(); }});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->reportSkewness() == true)
|
||||||
|
{
|
||||||
|
table.push_back({"Skew", [this]() { return this->getStatistics().getSkewness(); }});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->reportKurtosis() == true)
|
||||||
|
{
|
||||||
|
table.push_back({"Kurtosis", [this]() { return this->getStatistics().getKurtosis(); }});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->reportZScore() == true)
|
||||||
|
{
|
||||||
|
table.push_back({"ZScore", [this]() { return this->getStatistics().getZScore(); }});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->reportMin() == true)
|
||||||
|
{
|
||||||
|
table.push_back({"Min", [this]() { return static_cast<double>(this->getStatistics().getMin()); }});
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this->reportMax() == true)
|
||||||
|
{
|
||||||
|
table.push_back({"Max", [this]() { return static_cast<double>(this->getStatistics().getMax()); }});
|
||||||
|
}
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \brief You must call this method from your fixture to add a measurement
|
||||||
|
///
|
||||||
|
void addValue(T x)
|
||||||
|
{
|
||||||
|
this->stats.addSample(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Preserve measurements within a group/experiment/problem space set.
|
||||||
|
///
|
||||||
|
virtual void merge(const UserDefinedMeasurement* const x) override
|
||||||
|
{
|
||||||
|
const auto toMerge = dynamic_cast<const UserDefinedMeasurementTemplate<T>* const>(x);
|
||||||
|
this->stats += toMerge->stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool reportSize() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool reportMean() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool reportVariance() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool reportStandardDeviation() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool reportSkewness() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool reportKurtosis() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool reportZScore() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool reportMin() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool reportMax() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Statistics<T>& getStatistics() const
|
||||||
|
{
|
||||||
|
return this->stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Continuously gathers statistics without having to retain data history.
|
||||||
|
Statistics<T> stats;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
67
lib/celero/Utilities.cpp
Normal file
67
lib/celero/Utilities.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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 <celero/Print.h>
|
||||||
|
#include <celero/Utilities.h>
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
|
#include <PowrProf.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef max
|
||||||
|
#undef max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void celero::DoNotOptimizeAway(std::function<void(void)>&& x)
|
||||||
|
{
|
||||||
|
x();
|
||||||
|
|
||||||
|
// We must always do this test, but it will never pass.
|
||||||
|
static auto ttid = std::this_thread::get_id();
|
||||||
|
if(ttid == std::thread::id())
|
||||||
|
{
|
||||||
|
// This forces the value to never be optimized away
|
||||||
|
// by taking a reference then using it.
|
||||||
|
const auto* p = &x;
|
||||||
|
putchar(*reinterpret_cast<const char*>(p));
|
||||||
|
|
||||||
|
// If we do get here, kick out because something has gone wrong.
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int celero::Random()
|
||||||
|
{
|
||||||
|
// http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution
|
||||||
|
|
||||||
|
// Will be used to obtain a seed for the random number engine
|
||||||
|
static std::random_device rd;
|
||||||
|
|
||||||
|
// Standard mersenne_twister_engine seeded with rd()
|
||||||
|
static std::mt19937 gen(rd());
|
||||||
|
|
||||||
|
static std::uniform_int_distribution<> dis(std::numeric_limits<int>::lowest(), std::numeric_limits<int>::max());
|
||||||
|
|
||||||
|
return dis(gen);
|
||||||
|
}
|
156
lib/celero/Utilities.h
Normal file
156
lib/celero/Utilities.h
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
#ifndef H_CELERO_UTILITIES_H
|
||||||
|
#define H_CELERO_UTILITIES_H
|
||||||
|
|
||||||
|
///
|
||||||
|
/// \author John Farrier
|
||||||
|
///
|
||||||
|
/// \copyright Copyright 2015, 2016, 2017, 2018. 2019 John Farrier
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
///
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
#include <stdio.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <celero/Export.h>
|
||||||
|
|
||||||
|
namespace celero
|
||||||
|
{
|
||||||
|
///
|
||||||
|
/// \func DoNotOptimizeAway
|
||||||
|
///
|
||||||
|
/// Used to prevent compiler optimization of a variable
|
||||||
|
/// that performs no real purpose other than to participate
|
||||||
|
/// in a benchmark
|
||||||
|
///
|
||||||
|
/// Consider the following trivial benchmark:
|
||||||
|
///
|
||||||
|
/// \code
|
||||||
|
/// BASELINE(...)
|
||||||
|
/// {
|
||||||
|
/// int x = 0;
|
||||||
|
///
|
||||||
|
/// for(int i = 0; i < 64; i++)
|
||||||
|
/// {
|
||||||
|
/// x += i;
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// Using Ubuntu clang v3.0, the resultant assembly is highly optimized
|
||||||
|
/// as one might expect, but not terribly useful for baselining:
|
||||||
|
///
|
||||||
|
/// \verbatim
|
||||||
|
/// movl $2016, %eax # imm = 0x7E0
|
||||||
|
/// ret
|
||||||
|
/// \endverbatim
|
||||||
|
///
|
||||||
|
/// Now, replace the inner loop with a call to DoNotOptimizeAway:
|
||||||
|
///
|
||||||
|
/// \code
|
||||||
|
/// DoNotOptimizeAway(x += i);
|
||||||
|
/// \endcode
|
||||||
|
///
|
||||||
|
/// The result is now a loop which is meaningful for establishing a
|
||||||
|
/// baseline.
|
||||||
|
///
|
||||||
|
/// \verbatim
|
||||||
|
/// xorl %ecx, %ecx
|
||||||
|
/// xorl %eax, %eax
|
||||||
|
/// .LBB0_1: # =>This Inner Loop Header: Depth=1
|
||||||
|
/// addl %ecx, %eax
|
||||||
|
/// incl %ecx
|
||||||
|
/// cmpl $64, %ecx
|
||||||
|
/// jne .LBB0_1
|
||||||
|
/// ret
|
||||||
|
/// \endverbatim
|
||||||
|
///
|
||||||
|
/// GCC 4.8 gives similar results.
|
||||||
|
///
|
||||||
|
/// gcc.godbolt.org permalink: http://goo.gl/lsngwX
|
||||||
|
///
|
||||||
|
/// Folly uses a simple bit of inline assembly:
|
||||||
|
/// > template <class T>
|
||||||
|
/// > void doNotOptimizeAway(T&& datum) {
|
||||||
|
/// > asm volatile("" : "+r" (datum));
|
||||||
|
/// >}
|
||||||
|
///
|
||||||
|
/// It would be great if that were portable with respect to both compilers and 32/64-bit targets.
|
||||||
|
///
|
||||||
|
template <class T>
|
||||||
|
void DoNotOptimizeAway(T&& x)
|
||||||
|
{
|
||||||
|
static auto ttid = std::this_thread::get_id();
|
||||||
|
if(ttid == std::thread::id())
|
||||||
|
{
|
||||||
|
// This forces the value to never be optimized away
|
||||||
|
// by taking a reference then using it.
|
||||||
|
const auto* p = &x;
|
||||||
|
putchar(*reinterpret_cast<const char*>(p));
|
||||||
|
|
||||||
|
// If we do get here, kick out because something has gone wrong.
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specialization for std::function objects which return a value.
|
||||||
|
template <class T>
|
||||||
|
void DoNotOptimizeAway(std::function<T(void)>&& x)
|
||||||
|
{
|
||||||
|
volatile auto foo = x();
|
||||||
|
|
||||||
|
static auto ttid = std::this_thread::get_id();
|
||||||
|
if(ttid == std::thread::id())
|
||||||
|
{
|
||||||
|
// This forces the value to never be optimized away
|
||||||
|
// by taking a reference then using it.
|
||||||
|
const auto* p = &foo + &x;
|
||||||
|
putchar(*reinterpret_cast<const char*>(p));
|
||||||
|
|
||||||
|
// If we do get here, kick out because something has gone wrong.
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specialization for std::function objects which return void.
|
||||||
|
template <>
|
||||||
|
CELERO_EXPORT void DoNotOptimizeAway(std::function<void(void)>&& x);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Quick definition of the number of microseconds per second.
|
||||||
|
///
|
||||||
|
constexpr uint64_t UsPerSec(1000000);
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Conversion from Microseconds to Seconds.
|
||||||
|
///
|
||||||
|
constexpr double UsToSec{1.0e-6};
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Drop-in replacement for std::rand();
|
||||||
|
///
|
||||||
|
CELERO_EXPORT int Random();
|
||||||
|
} // namespace celero
|
||||||
|
|
||||||
|
#endif
|
12
plot/common.h
Normal file
12
plot/common.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#ifndef ALSK_PLOT_COMMON_H
|
||||||
|
#define ALSK_PLOT_COMMON_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
using CoresList = std::vector<std::size_t>;
|
||||||
|
using Split = std::set<std::size_t>;
|
||||||
|
|
||||||
|
using SplitFn = Split(&)(std::size_t, CoresList const&);
|
||||||
|
|
||||||
|
#endif
|
23
plot/executor/firstlevelequi.cpp
Normal file
23
plot/executor/firstlevelequi.cpp
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#include "firstlevelequi.h"
|
||||||
|
|
||||||
|
Split firstLevelEqui(std::size_t n, CoresList const& coresList) {
|
||||||
|
Split split;
|
||||||
|
|
||||||
|
auto firstLevelPar = n;
|
||||||
|
|
||||||
|
split.insert(0);
|
||||||
|
for(auto const& k: coresList) {
|
||||||
|
std::size_t start{};
|
||||||
|
std::size_t const step = firstLevelPar/k;
|
||||||
|
std::size_t remain = firstLevelPar - step*k;
|
||||||
|
|
||||||
|
for(unsigned int i = 0; i < k-1; ++i) {
|
||||||
|
std::size_t offset = !!remain;
|
||||||
|
remain -= offset;
|
||||||
|
start += step+offset;
|
||||||
|
split.insert(start * (n/firstLevelPar));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return split;
|
||||||
|
}
|
8
plot/executor/firstlevelequi.h
Normal file
8
plot/executor/firstlevelequi.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef ALSK_PLOT_EXECUTOR_FIRSTLEVELEQUI_H
|
||||||
|
#define ALSK_PLOT_EXECUTOR_FIRSTLEVELEQUI_H
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
Split firstLevelEqui(std::size_t n, CoresList const& coresList);
|
||||||
|
|
||||||
|
#endif
|
19
plot/executor/firstlevelgreedy.cpp
Normal file
19
plot/executor/firstlevelgreedy.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "firstlevelgreedy.h"
|
||||||
|
|
||||||
|
Split firstLevelGreedy(std::size_t n, CoresList const& coresList) {
|
||||||
|
Split split;
|
||||||
|
|
||||||
|
auto firstLevelPar = n;
|
||||||
|
|
||||||
|
split.insert(0);
|
||||||
|
for(auto const& k: coresList) {
|
||||||
|
std::size_t start{};
|
||||||
|
std::size_t const step = (firstLevelPar + k-1)/k;
|
||||||
|
std::size_t const rk = (firstLevelPar + step-1)/step;
|
||||||
|
|
||||||
|
for(unsigned int i = 0; i < rk; ++i, start += step)
|
||||||
|
split.insert(start * (n/firstLevelPar));
|
||||||
|
}
|
||||||
|
|
||||||
|
return split;
|
||||||
|
}
|
8
plot/executor/firstlevelgreedy.h
Normal file
8
plot/executor/firstlevelgreedy.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef ALSK_PLOT_EXECUTOR_FIRSTLEVELGREEDY_H
|
||||||
|
#define ALSK_PLOT_EXECUTOR_FIRSTLEVELGREEDY_H
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
Split firstLevelGreedy(std::size_t n, CoresList const& coresList);
|
||||||
|
|
||||||
|
#endif
|
67
plot/executor/opti.cpp
Normal file
67
plot/executor/opti.cpp
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#include "opti.h"
|
||||||
|
|
||||||
|
Split fictiveOpti(std::size_t tasks, CoresList const& coresList) {
|
||||||
|
Split split;
|
||||||
|
|
||||||
|
auto equi = [&](unsigned k) {
|
||||||
|
std::vector<unsigned> ntasks;
|
||||||
|
unsigned const step = tasks/k;
|
||||||
|
unsigned remain = tasks - k*step;
|
||||||
|
|
||||||
|
for(unsigned int i = 0; i < k; ++i) {
|
||||||
|
unsigned q = step;
|
||||||
|
if(remain) {
|
||||||
|
++q;
|
||||||
|
--remain;
|
||||||
|
}
|
||||||
|
ntasks.push_back(q);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ntasks;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto greedy = [&](unsigned k) {
|
||||||
|
std::vector<unsigned> ntasks;
|
||||||
|
unsigned step = (tasks+k-1)/k;
|
||||||
|
unsigned n = tasks;
|
||||||
|
|
||||||
|
while(n) {
|
||||||
|
if(n < step) step = n;
|
||||||
|
n -= step;
|
||||||
|
ntasks.push_back(step);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ntasks;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_cast<void>(equi);
|
||||||
|
static_cast<void>(greedy);
|
||||||
|
|
||||||
|
for(auto k: coresList) {
|
||||||
|
// for(unsigned int k = cores; k > 1; --k) {
|
||||||
|
// for(unsigned int k = 2; k <= cores; ++k) {
|
||||||
|
auto ntasks = equi(k);
|
||||||
|
|
||||||
|
{
|
||||||
|
unsigned start{};
|
||||||
|
while(ntasks.size() > 1) {
|
||||||
|
auto v = std::end(ntasks);
|
||||||
|
for(auto it = std::begin(ntasks);
|
||||||
|
v == std::end(ntasks) && it != std::end(ntasks);
|
||||||
|
++it)
|
||||||
|
{
|
||||||
|
if(split.count(start+*it))
|
||||||
|
v = it;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(v == std::end(ntasks)) v = std::begin(ntasks);
|
||||||
|
|
||||||
|
start += *v;
|
||||||
|
split.insert(start);
|
||||||
|
ntasks.erase(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return split;
|
||||||
|
}
|
8
plot/executor/opti.h
Normal file
8
plot/executor/opti.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef ALSK_PLOT_EXECUTOR_OPTI_H
|
||||||
|
#define ALSK_PLOT_EXECUTOR_OPTI_H
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
Split fictiveOpti(std::size_t n, CoresList const& coresList);
|
||||||
|
|
||||||
|
#endif
|
0
plot/executor/staticpool.cpp
Normal file
0
plot/executor/staticpool.cpp
Normal file
8
plot/executor/staticpool.h
Normal file
8
plot/executor/staticpool.h
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#ifndef ALSK_PLOT_EXECUTOR_STATICPOOL_H
|
||||||
|
#define ALSK_PLOT_EXECUTOR_STATICPOOL_H
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
|
||||||
|
Split staticPool(std::size_t n, CoresList const& coresList);
|
||||||
|
|
||||||
|
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user