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