thesis version
This commit is contained in:
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
a.out
|
||||
build/
|
||||
release/
|
||||
clang/
|
||||
__pycache__/
|
||||
173
CMakeLists.txt
Normal file
173
CMakeLists.txt
Normal file
@@ -0,0 +1,173 @@
|
||||
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 ON)
|
||||
set(GEN_LIBRARY ON)
|
||||
set(LIB_TYPE STATIC) # NONE, STATIC, SHARED, MODULE
|
||||
set(LIBS_TYPE STATIC)
|
||||
|
||||
set(FLAGS_ANY "-Wall -Wextra -Winline -Wfatal-errors")
|
||||
set(FLAGS_DEBUG "-DDEBUG -O0 -pg")
|
||||
set(FLAGS_RELEASE "-DNDEBUG -O2")
|
||||
|
||||
set(SRCDIRS src)
|
||||
set(LIBSDIRS )
|
||||
set(LIBDIRS src/tsp)
|
||||
set(TESTSDIRS celero)
|
||||
set(EXAMPLESDIRS bench examples)
|
||||
|
||||
set(INCLUDE_DIRS "inc")
|
||||
set(LIBRARIES "-lpthread -ltbb")
|
||||
|
||||
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}")
|
||||
|
||||
## 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})
|
||||
list(APPEND LIBDIRS ${libsdir}/${child})
|
||||
else()
|
||||
message(WARNING "! Ignoring file: ${libsdir}/${child}")
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
foreach(libdir ${LIBDIRS})
|
||||
set(libpath ${CMAKE_CURRENT_SOURCE_DIR}/${libdir})
|
||||
file(GLOB_RECURSE lib_src ${libpath}/*.${EXTENSION})
|
||||
if(lib_src)
|
||||
get_filename_component(lib ${libpath} NAME)
|
||||
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()
|
||||
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()
|
||||
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>.
|
||||
162
README.md
Normal file
162
README.md
Normal file
@@ -0,0 +1,162 @@
|
||||
# About
|
||||
|
||||
ROSA (in french "**R**echerche **O**pérationnelle grâce aux **S**quelettes **A**lgorithmiques",
|
||||
i.e. [Operational Research](https://en.wikipedia.org/wiki/Operations_research) with [Algorithmic Skeletons](https://en.wikipedia.org/wiki/Algorithmic_skeleton)).
|
||||
It relies on [alsk](https://phd.pereda.fr/dev/alsk) for the algorithmic skeletons.
|
||||
This is part of the work done for my Ph.D. thesis.
|
||||
|
||||
## Brief
|
||||
|
||||
The main algorithm implemented and presented is GRASP×ELS.
|
||||
This metaheuristic can be represented as below:
|
||||
|
||||
<div align="center"><img src="https://phd.pereda.fr/assets/rosa/graspels.png" width="800"></div>
|
||||
|
||||
When implemented as an algorithmic skeleton, its representation becomes this tree:
|
||||
|
||||
<div align="center"><img src="https://phd.pereda.fr/assets/rosa/treegraspels.png" width="500"></div>
|
||||
|
||||
Obtained using the following source code.
|
||||
|
||||
For the internal ELS:
|
||||
```cpp
|
||||
template<typename InitLS, typename Mutate, typename LS, typename InnerSelect, typename OuterSelect>
|
||||
using SkelElsStruct =
|
||||
S<alsk::Serial,
|
||||
InitLS, // LSI
|
||||
S<IterSel,
|
||||
S<FarmSel,
|
||||
S<Serial,
|
||||
Mutate, LS // M then LS
|
||||
>,
|
||||
InnerSelect // Sel3
|
||||
>,
|
||||
OuterSelect // Sel2
|
||||
>
|
||||
>;
|
||||
|
||||
template<typename Solution>
|
||||
using SkelElsLinks =
|
||||
L<Serial, R<1>(Solution const&),
|
||||
Solution(P<0>),
|
||||
L<IterSel, Solution(R<0> const&),
|
||||
L<FarmSel, Solution(Solution),
|
||||
L<Serial, R<1>(P<0>),
|
||||
Solution(P<0>, RNG&),
|
||||
Solution(R<0> const&)
|
||||
>,
|
||||
Solution(Solution const&, Solution const&)
|
||||
>,
|
||||
Solution(Solution const&, Solution const&)
|
||||
>
|
||||
>;
|
||||
|
||||
template<
|
||||
typename Solution,
|
||||
typename InitLS, typename Mutate, typename LS, typename InnerSelect, typename OuterSelect
|
||||
>
|
||||
using SkelEls = BuildSkeleton<SkelElsStruct, SkelElsLinks>::skeleton<
|
||||
Pack<InitLS, Mutate, LS, InnerSelect, OuterSelect>,
|
||||
Pack<Solution>
|
||||
>;
|
||||
```
|
||||
|
||||
For the GRASP:
|
||||
```cpp
|
||||
template<typename CH, typename LS, typename Select>
|
||||
using SkelGraspStructure =
|
||||
S<FarmSel,
|
||||
S<Serial, CH, LS>,
|
||||
Select // Sel1
|
||||
>;
|
||||
|
||||
template<typename Problem, typename Solution>
|
||||
using SkelGraspLinks =
|
||||
L<FarmSel, Solution(Problem const&),
|
||||
L<Serial, R<1>(P<0>),
|
||||
Solution(P<0>, RNG),
|
||||
Solution(R<0>)
|
||||
>,
|
||||
Solution(Solution, Solution)
|
||||
>;
|
||||
|
||||
template<typename Problem, typename Solution, typename CH, typename LS, typename Select>
|
||||
using SkelGrasp = BuildSkeleton<SkelGraspStructure, SkelGraspLinks>::skeleton<
|
||||
Pack<CH, LS, Select>,
|
||||
Pack<Problem, Solution>
|
||||
>;
|
||||
```
|
||||
|
||||
Then the GRASP×ELS can be constructed:
|
||||
```cpp
|
||||
// All arguments are defined types or functions, see full source code
|
||||
using ELS = SkelEls<
|
||||
tsp::Solution,
|
||||
Descent,
|
||||
Move2Opt, Descent,
|
||||
FN(selectMin)
|
||||
>;
|
||||
|
||||
using GRASPxELS = SkelGrasp<
|
||||
tsp::Problem, tsp::Solution,
|
||||
RGreedy<tsp::Solution>, ELS,
|
||||
FN(selectMin)
|
||||
>;
|
||||
```
|
||||
|
||||
## Performances
|
||||
|
||||
The measures shown below are from using the GRASPxELS algorithm to solve an instance of [TSP](https://en.wikipedia.org/wiki/Travelling_salesman_problem) with 194 nodes.
|
||||
Various execution policies are used:
|
||||
<ul>
|
||||
<li>"hw_seq": handwritten sequential implementation;
|
||||
<img src="https://phd.pereda.fr/assets/rosa/rt_legend.png" width="250" align="right">
|
||||
</li>
|
||||
<li>"hw_par": handwritten parallel implementation;</li>
|
||||
<li>"sk_seq": skeleton without parallelisation;</li>
|
||||
<li>"sk_firstlevel": skeleton with parallelisation of the first level;</li>
|
||||
<li>"sk_staticpool": skeleton with parallelisation using a thread pool with static task distribution;</li>
|
||||
<li>"sk_dynamicpool": skeleton with parallelisation using a classical thread pool with dynamic task distribution;</li>
|
||||
<li>"sk_thread": skeleton with parallelisation using dynamically created threads.</li>
|
||||
</ul>
|
||||
|
||||
For an execution with only one allotted core, meaning that there is no parallelisation done, we obtain the data below.
|
||||
Note that this data set do not use the legend shown above.
|
||||
All subsequent images use it.
|
||||
<div align="center"><img src="https://phd.pereda.fr/assets/rosa/rt_graspels_qa194_24_20_20_seq.png" width="500"></div>
|
||||
|
||||
For parallel executions, measures give the following data.
|
||||
|
||||
With 24 iterations for the outmost parallel loop:
|
||||
<div align="center"><img src="https://phd.pereda.fr/assets/rosa/rt_graspels_qa194_24_20_20_par.png" width="500"></div>
|
||||
<div align="center"><img src="https://phd.pereda.fr/assets/rosa/rt_graspels_qa194_20_20_20_speedup.png" width="500"></div>
|
||||
|
||||
With only 4 iterations for the outmost parallel loop:
|
||||
<div align="center"><img src="https://phd.pereda.fr/assets/rosa/rt_graspels_qa194_v4_20_20_par.png" width="500"></div>
|
||||
<div align="center"><img src="https://phd.pereda.fr/assets/rosa/rt_graspels_qa194_4_20_20_speedup.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`: sources;
|
||||
- `src/rosa`: the library sources;
|
||||
- `rtbenchmarks`: scripts for compile-time/run-time benchmarking;
|
||||
- `results`: results presented in the thesis, obtained using mentioned scripts and codes.
|
||||
|
||||
## Usage
|
||||
|
||||
To produce the `Makefile` and build the project:
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
make
|
||||
```
|
||||
106
bench/graspels/bad_graspels.h
Normal file
106
bench/graspels/bad_graspels.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#ifndef ROSA_BENCH_GRASPELS_BAD_GRASPELS_H
|
||||
#define ROSA_BENCH_GRASPELS_BAD_GRASPELS_H
|
||||
|
||||
#include <alsk/alsk.h>
|
||||
|
||||
namespace rosa {
|
||||
|
||||
/* GRASP
|
||||
* loop
|
||||
* * s = init()
|
||||
* * s = ls(s)
|
||||
* * best = select(s, best)
|
||||
* ----
|
||||
* return best
|
||||
*/
|
||||
template<typename Init, typename LS, typename Select>
|
||||
using SkelNRGraspStructure =
|
||||
alsk::S<alsk::FarmSel,
|
||||
alsk::S<alsk::Serial, Init, LS>,
|
||||
Select
|
||||
>;
|
||||
|
||||
template<typename Problem, typename Solution, typename RNG>
|
||||
using SkelNRGraspLinks =
|
||||
alsk::L<alsk::FarmSel, Solution(Problem const&, RNG&),
|
||||
alsk::L<alsk::Serial, alsk::arg::R<1>(alsk::arg::P<0>, alsk::arg::P<1>),
|
||||
Solution(alsk::arg::P<0>, alsk::arg::P<1>),
|
||||
Solution(alsk::arg::R<0> const&, alsk::arg::P<1>)
|
||||
>,
|
||||
Solution(Solution const&, Solution const&)
|
||||
>;
|
||||
|
||||
/* *** */
|
||||
|
||||
template<
|
||||
typename Problem, typename Solution, typename RNG,
|
||||
typename Init, typename LS, typename Select
|
||||
>
|
||||
using SkelNRGrasp = alsk::BuildSkeleton<SkelNRGraspStructure, SkelNRGraspLinks>::skeleton<
|
||||
tmp::Pack<Init, LS, Select>,
|
||||
tmp::Pack<Problem, Solution, RNG>
|
||||
>;
|
||||
|
||||
}
|
||||
|
||||
namespace rosa {
|
||||
|
||||
/* ELS
|
||||
* best = ls(s) -- SEls
|
||||
* loop -- SElsOuterLoop
|
||||
* * loop -- SElsInnerLoop
|
||||
* * * s = mutate(best) -- SElsGen
|
||||
* * * s = ls(s)
|
||||
* * * ibest = select(s, ibest)
|
||||
* * ----
|
||||
* * best = select(s, best) // with acceptation criteria?
|
||||
* ----
|
||||
* return best
|
||||
*/
|
||||
template<
|
||||
typename InitLS, typename Mutate, typename LS,
|
||||
typename InnerSelect, typename OuterSelect
|
||||
>
|
||||
using SkelNRElsStruct =
|
||||
alsk::S<alsk::Serial,
|
||||
InitLS,
|
||||
alsk::S<alsk::IterSel,
|
||||
alsk::S<alsk::FarmSel,
|
||||
alsk::S<alsk::Serial,
|
||||
Mutate, LS
|
||||
>,
|
||||
InnerSelect
|
||||
>,
|
||||
OuterSelect
|
||||
>
|
||||
>;
|
||||
|
||||
template<typename Solution, typename RNG>
|
||||
using SkelNRElsLinks =
|
||||
alsk::L<alsk::Serial, alsk::arg::R<1>(Solution const&, RNG&),
|
||||
Solution(alsk::arg::P<0>),
|
||||
alsk::L<alsk::IterSel, Solution(alsk::arg::R<0> const&, alsk::arg::P<1>),
|
||||
alsk::L<alsk::FarmSel, Solution(Solution const&, alsk::arg::P<1>),
|
||||
alsk::L<alsk::Serial, alsk::arg::R<1>(alsk::arg::P<0>, alsk::arg::P<1>),
|
||||
Solution(alsk::arg::P<0>, alsk::arg::P<1>),
|
||||
Solution(alsk::arg::R<0> const&)
|
||||
>,
|
||||
Solution(Solution const&, Solution const&)
|
||||
>,
|
||||
Solution(Solution const&, Solution const&)
|
||||
>
|
||||
>;
|
||||
|
||||
template<
|
||||
typename Solution, typename RNG,
|
||||
typename InitLS, typename Mutate, typename LS,
|
||||
typename InnerSelect, typename OuterSelect = InnerSelect
|
||||
>
|
||||
using SkelNREls = alsk::BuildSkeleton<SkelNRElsStruct, SkelNRElsLinks>::skeleton<
|
||||
tmp::Pack<InitLS, Mutate, LS, InnerSelect, OuterSelect>,
|
||||
tmp::Pack<Solution, RNG>
|
||||
>;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
86
bench/graspels/common.h
Normal file
86
bench/graspels/common.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#ifndef ROSA_BENCH_GRASPELS_COMMON_H
|
||||
#define ROSA_BENCH_GRASPELS_COMMON_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <thread>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <alsk/alsk.h>
|
||||
|
||||
#include <muscles/descent.h>
|
||||
#include <muscles/move2opt.h>
|
||||
#include <muscles/rgreedy.h>
|
||||
|
||||
#include <rosa/els.h>
|
||||
#include <rosa/grasp.h>
|
||||
|
||||
#include <tsp/solution.h>
|
||||
#include <tsp/problem.h>
|
||||
#include <tsp/tsp.h>
|
||||
|
||||
#ifndef DATA_FILE
|
||||
#define DATA_FILE "../data/qa194"
|
||||
#endif
|
||||
#ifndef GRASP_N
|
||||
#define GRASP_N 2
|
||||
#endif
|
||||
#ifndef ELS_ITER_MAX
|
||||
#define ELS_ITER_MAX 20
|
||||
#endif
|
||||
#ifndef ELS_GEN
|
||||
#define ELS_GEN 10
|
||||
#endif
|
||||
#ifndef FUNC
|
||||
#define FUNC none
|
||||
#endif
|
||||
#ifndef NTHREADS
|
||||
#define NTHREADS 1
|
||||
#endif
|
||||
#ifndef SEED
|
||||
#define SEED std::mt19937::default_seed
|
||||
#endif
|
||||
|
||||
#define STR_(A) #A
|
||||
#define STR(A) STR_(A)
|
||||
|
||||
/* repeatable* */
|
||||
#define REPRODUCIBLE
|
||||
|
||||
using RNG = std::mt19937;
|
||||
|
||||
struct Arguments {
|
||||
std::mt19937::result_type seed;
|
||||
};
|
||||
|
||||
inline tsp::Solution selectMin(tsp::Solution const& a, tsp::Solution const& b) { return a<b? a:b; }
|
||||
inline auto rgreedy() { return RGreedy<tsp::Solution>{2}; }
|
||||
|
||||
inline double tvdiff(struct timeval& b, struct timeval& e) {
|
||||
return (e.tv_sec - b.tv_sec) + (e.tv_usec - b.tv_usec) / 1e6;
|
||||
}
|
||||
|
||||
template<typename F, typename... Args>
|
||||
void timeit(int who, std::string const& prefix, F&& f, Args&&... args) {
|
||||
using Clock = std::chrono::high_resolution_clock;
|
||||
|
||||
struct rusage b, e;
|
||||
|
||||
auto tp0 = Clock::now();
|
||||
getrusage(who, &b);
|
||||
std::forward<F>(f)(std::forward<Args>(args)...);
|
||||
getrusage(who, &e);
|
||||
auto tp1 = Clock::now();
|
||||
|
||||
std::cout << prefix;
|
||||
std::cout << "[" << std::this_thread::get_id() << "] ";
|
||||
std::cout << "time: ";
|
||||
std::cout << "real " << std::chrono::duration<double>(tp1 - tp0).count() << " ";
|
||||
std::cout << "user " << tvdiff(b.ru_utime, e.ru_utime) << " ";
|
||||
std::cout << "sys " << tvdiff(b.ru_stime, e.ru_stime);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
#endif
|
||||
26
bench/graspels/decl.h
Normal file
26
bench/graspels/decl.h
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef ROSA_BENCH_GRASPELS_DECL_H
|
||||
#define ROSA_BENCH_GRASPELS_DECL_H
|
||||
|
||||
#include "common.h"
|
||||
|
||||
tsp::Solution none(tsp::Problem const&, RNG&, Arguments const&);
|
||||
|
||||
tsp::Solution hwElsGen(tsp::Solution const&, RNG&, Arguments const&);
|
||||
tsp::Solution hw_seq(tsp::Problem const&, RNG&, Arguments const&);
|
||||
tsp::Solution hw_par(tsp::Problem const&, RNG&, Arguments const&);
|
||||
|
||||
tsp::Solution hw_seq_v(tsp::Problem const&, RNG&, Arguments const&);
|
||||
tsp::Solution hw_par_v(tsp::Problem const&, RNG&, Arguments const&);
|
||||
|
||||
tsp::Solution sk_nr_seq(tsp::Problem const&, RNG&, Arguments const&);
|
||||
tsp::Solution sk_nr_par(tsp::Problem const&, RNG&, Arguments const&);
|
||||
|
||||
tsp::Solution sk_seq(tsp::Problem const&, RNG&, Arguments const&);
|
||||
tsp::Solution sk_par_firstlevel(tsp::Problem const&, RNG&, Arguments const&);
|
||||
tsp::Solution sk_par_staticpool(tsp::Problem const&, RNG&, Arguments const&);
|
||||
tsp::Solution sk_par_dynamicpool(tsp::Problem const&, RNG&, Arguments const&);
|
||||
tsp::Solution sk_par_thread(tsp::Problem const&, RNG&, Arguments const&);
|
||||
|
||||
tsp::Solution tbb_par(tsp::Problem const&, RNG&, Arguments const&);
|
||||
|
||||
#endif
|
||||
220
bench/graspels/hw.cpp
Normal file
220
bench/graspels/hw.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
#include "common.h"
|
||||
|
||||
auto hwElsGen(tsp::Solution const& solution, RNG& rng) {
|
||||
return Descent{}(Move2Opt{}(solution, rng));
|
||||
}
|
||||
|
||||
#if PARLEV==2
|
||||
auto hwElsInner(tsp::Solution const& solution, RNG& rng, std::size_t nCore) {
|
||||
std::size_t const nThreads = std::min<std::size_t>(nCore, ELS_GEN);
|
||||
std::size_t const step = ELS_GEN/nThreads;
|
||||
std::size_t remain = ELS_GEN - step*nThreads;
|
||||
|
||||
// std::cout << "LEVEL #2 : " << nCore << ";" << nThreads << ";" << step << ";" << remain << std::endl;
|
||||
|
||||
std::vector<std::thread> threads{nThreads-1};
|
||||
std::vector<tsp::Solution> solutions(nThreads);
|
||||
|
||||
tsp::Solution best;
|
||||
|
||||
/* repeatability at loop level */
|
||||
std::vector<RNG> rngs(ELS_GEN);
|
||||
for(std::size_t i = 0; i < ELS_GEN; ++i)
|
||||
rngs[i].seed(rng());
|
||||
/* ***** */
|
||||
|
||||
std::size_t start{};
|
||||
|
||||
for(std::size_t i{}; i < (nThreads-1); ++i) {
|
||||
std::size_t offset = !!remain;
|
||||
remain -= offset;
|
||||
|
||||
threads[i] = std::thread{
|
||||
[&,start,i,step=step+offset](auto const& solution) {
|
||||
tsp::Solution& s = solutions[i];
|
||||
|
||||
for(std::size_t j{}; j < step; ++j) {
|
||||
tsp::Solution cur = hwElsGen(solution, rngs[start+j]);
|
||||
s = selectMin(std::move(s), std::move(cur));
|
||||
}
|
||||
},
|
||||
std::cref(solution)
|
||||
};
|
||||
|
||||
start += step+offset;
|
||||
}
|
||||
|
||||
{
|
||||
tsp::Solution& s = solutions[nThreads-1];
|
||||
|
||||
for(std::size_t j{}; j < step; ++j) {
|
||||
tsp::Solution cur = hwElsGen(solution, rngs[start+j]);
|
||||
s = selectMin(std::move(s), std::move(cur));
|
||||
}
|
||||
}
|
||||
|
||||
for(auto& thread: threads) thread.join();
|
||||
|
||||
best = *std::min_element(std::begin(solutions), std::end(solutions));
|
||||
|
||||
return best;
|
||||
}
|
||||
#else
|
||||
auto hwElsInner(tsp::Solution const& solution, RNG& rng, std::size_t) {
|
||||
tsp::Solution best;
|
||||
|
||||
/* repeatability at loop level */
|
||||
std::vector<RNG> rngs(ELS_GEN);
|
||||
for(std::size_t i = 0; i < ELS_GEN; ++i)
|
||||
rngs[i].seed(rng());
|
||||
/* ***** */
|
||||
|
||||
if(ELS_GEN)
|
||||
best = hwElsGen(solution, rngs[0]);
|
||||
|
||||
for(std::size_t i = 1; i < ELS_GEN; ++i) {
|
||||
tsp::Solution current = hwElsGen(solution, rngs[i]);
|
||||
best = selectMin(std::move(best), std::move(current));
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
#endif
|
||||
|
||||
auto hwEls(tsp::Solution const& solution, RNG& rng, std::size_t nCore) {
|
||||
tsp::Solution best = Descent{}(solution);
|
||||
|
||||
for(std::size_t i = 0; i < ELS_ITER_MAX; ++i) {
|
||||
tsp::Solution current = hwElsInner(best, rng, nCore);
|
||||
best = selectMin(std::move(best), std::move(current));
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
auto hwGraspGen(tsp::Problem const& problem, RNG& rng, std::size_t nCore = 1) {
|
||||
return hwEls(rgreedy()(problem, rng), rng, nCore);
|
||||
}
|
||||
|
||||
/* *** */
|
||||
|
||||
auto hwGraspEls(tsp::Problem const& problem, RNG& rng) {
|
||||
tsp::Solution best;
|
||||
|
||||
/* repeatability at loop level */
|
||||
std::vector<RNG> rngs(GRASP_N);
|
||||
for(std::size_t i = 0; i < GRASP_N; ++i)
|
||||
rngs[i].seed(rng());
|
||||
/* ***** */
|
||||
|
||||
if(GRASP_N)
|
||||
best = hwGraspGen(problem, rngs[0]);
|
||||
for(std::size_t i = 1; i < GRASP_N; ++i) {
|
||||
tsp::Solution current = hwGraspGen(problem, rngs[i]);
|
||||
best = selectMin(std::move(best), std::move(current));
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
template<std::size_t K>
|
||||
auto hwGraspElsPar(tsp::Problem const& problem, RNG& rng) {
|
||||
std::size_t const nThreads = std::min<std::size_t>(K, GRASP_N);
|
||||
std::size_t const step = GRASP_N/nThreads;
|
||||
std::size_t const remain = GRASP_N - step*nThreads;
|
||||
|
||||
std::size_t cores2a = K/nThreads;
|
||||
std::size_t cores2b = (remain==0 ? 1 : K/remain);
|
||||
|
||||
// std::cout << "LEVEL #1 : " << K << ";" << nThreads << ";" << step << ";" << remain << std::endl;
|
||||
|
||||
tsp::Solution best;
|
||||
|
||||
std::vector<std::thread> threadsA{nThreads-1};
|
||||
std::vector<std::thread> threadsB{remain==0 ? 0 : remain-1};
|
||||
|
||||
std::vector<tsp::Solution> solutions(nThreads+remain);
|
||||
|
||||
/* repeatability at loop level */
|
||||
std::vector<RNG> rngs(GRASP_N);
|
||||
for(std::size_t i = 0; i < GRASP_N; ++i)
|
||||
rngs[i].seed(rng());
|
||||
/* ***** */
|
||||
|
||||
std::size_t start{};
|
||||
std::size_t i{};
|
||||
|
||||
/* Loop A */
|
||||
for(; i < (nThreads-1); ++i) {
|
||||
threadsA[i] = std::thread{
|
||||
[&,start,i,cores2a](auto const& problem) {
|
||||
tsp::Solution& s = solutions[i];
|
||||
|
||||
for(std::size_t j{}; j < step; ++j) {
|
||||
tsp::Solution cur = hwGraspGen(problem, rngs[start+j],cores2a);
|
||||
s = selectMin(std::move(s), std::move(cur));
|
||||
}
|
||||
},
|
||||
std::cref(problem)
|
||||
};
|
||||
|
||||
start += step;
|
||||
}
|
||||
|
||||
{
|
||||
tsp::Solution& s = solutions[i];
|
||||
|
||||
for(std::size_t j{}; j < step; ++j) {
|
||||
tsp::Solution cur = hwGraspGen(problem, rngs[start+j],cores2a);
|
||||
s = selectMin(std::move(s), std::move(cur));
|
||||
}
|
||||
|
||||
start+=step;
|
||||
++i;
|
||||
}
|
||||
|
||||
for(auto& thread: threadsA) thread.join();
|
||||
|
||||
/* Loop B */
|
||||
for(; i < nThreads+remain-1; ++i) {
|
||||
threadsB[i-nThreads] = std::thread{
|
||||
[&,start,i,cores2b](auto const& problem) {
|
||||
tsp::Solution& s = solutions[i];
|
||||
|
||||
tsp::Solution cur = hwGraspGen(problem, rngs[start],cores2b);
|
||||
s = selectMin(std::move(s), std::move(cur));
|
||||
},
|
||||
std::cref(problem)
|
||||
};
|
||||
|
||||
++start;
|
||||
}
|
||||
|
||||
if (remain>0)
|
||||
{
|
||||
tsp::Solution& s = solutions[i];
|
||||
|
||||
tsp::Solution cur = hwGraspGen(problem, rngs[start],cores2b);
|
||||
s = selectMin(std::move(s), std::move(cur));
|
||||
++start;
|
||||
++i;
|
||||
}
|
||||
|
||||
for(auto& thread: threadsB) thread.join();
|
||||
|
||||
/* Selection */
|
||||
|
||||
best = *std::min_element(std::begin(solutions), std::end(solutions));
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
/* *** */
|
||||
|
||||
tsp::Solution hw_seq(tsp::Problem const& p, RNG& rng, Arguments const&) {
|
||||
return hwGraspEls(p, rng);
|
||||
}
|
||||
|
||||
tsp::Solution hw_par(tsp::Problem const& p, RNG& rng, Arguments const&) {
|
||||
return hwGraspElsPar<NTHREADS>(p, rng);
|
||||
}
|
||||
188
bench/graspels/hwv.cpp
Normal file
188
bench/graspels/hwv.cpp
Normal file
@@ -0,0 +1,188 @@
|
||||
#include "common.h"
|
||||
|
||||
auto hwElsGenV(tsp::Solution const& solution, RNG& rng) {
|
||||
return Descent{}(Move2Opt{}(solution, rng));
|
||||
}
|
||||
|
||||
auto hwElsInner(tsp::Solution const& solution, std::vector<RNG>& rngs, std::size_t id, std::size_t nCore) {
|
||||
std::size_t n = ELS_GEN;
|
||||
std::size_t maxThreads = nCore;
|
||||
|
||||
std::size_t const nThreads = std::min<std::size_t>(maxThreads, ELS_GEN);
|
||||
|
||||
std::vector<std::thread> threads{nThreads-1};
|
||||
std::size_t const step = n/nThreads;
|
||||
std::size_t const remainBase = n - step*nThreads;
|
||||
std::size_t remain = remainBase;
|
||||
|
||||
auto run = [&solution,&rngs](tsp::Solution& out, std::size_t id, std::size_t k) {
|
||||
tsp::Solution best{};
|
||||
|
||||
if(k)
|
||||
best = hwElsGenV(solution, rngs[id]);
|
||||
for(std::size_t i = 1; i < k; ++i) {
|
||||
tsp::Solution current = hwElsGenV(solution, rngs[id+i]);
|
||||
best = selectMin(std::move(best), std::move(current));
|
||||
}
|
||||
|
||||
out = std::move(best);
|
||||
};
|
||||
|
||||
std::size_t start{};
|
||||
std::vector<tsp::Solution> bests(nThreads);
|
||||
|
||||
for(std::size_t i = 0; i < nThreads-1; ++i) {
|
||||
std::size_t offset = !!remain;
|
||||
remain -= offset;
|
||||
threads[i] = std::thread{run, std::ref(bests[i]), id+start, step+offset};
|
||||
start += step+offset;
|
||||
}
|
||||
|
||||
run(bests[nThreads-1], id+start, step);
|
||||
|
||||
for(auto& thread: threads) thread.join();
|
||||
|
||||
tsp::Solution best;
|
||||
// best = *std::min_element(std::begin(solutions), std::end(solutions));
|
||||
if(nThreads) best = std::move(bests[0]);
|
||||
for(std::size_t i = 1; i < nThreads; ++i)
|
||||
best = selectMin(std::move(best), std::move(bests[i]));
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
auto hwEls(tsp::Solution const& solution, std::vector<RNG>& rngs, std::size_t id, std::size_t nCore) {
|
||||
tsp::Solution best = Descent{}(solution);
|
||||
|
||||
for(std::size_t i = 0; i < ELS_ITER_MAX; ++i) {
|
||||
tsp::Solution current = hwElsInner(best, rngs, id, nCore);
|
||||
best = selectMin(std::move(best), std::move(current));
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
auto hwGraspGen(tsp::Problem const& problem, std::vector<RNG>& rngs, std::size_t id, std::size_t nCore = 1) {
|
||||
return hwEls(rgreedy()(problem, rngs[id]), rngs, id, nCore);
|
||||
}
|
||||
|
||||
/* *** */
|
||||
|
||||
auto hwGraspEls(tsp::Problem const& problem, std::vector<RNG>& rngs) {
|
||||
tsp::Solution best;
|
||||
|
||||
auto graspIter = [&](tsp::Problem const& problem, tsp::Solution& s, std::size_t id) {
|
||||
tsp::Solution cur = hwGraspGen(problem, rngs, id);
|
||||
s = selectMin(std::move(s), std::move(cur));
|
||||
};
|
||||
|
||||
if(GRASP_N) {
|
||||
auto graspInit = [&](tsp::Problem const& problem, tsp::Solution& s) {
|
||||
s = hwGraspGen(problem, rngs, 0);
|
||||
};
|
||||
#ifdef SUBTIME
|
||||
timeit(RUSAGE_THREAD, "[GRASP] ", graspInit, problem, best);
|
||||
#else
|
||||
graspInit(problem, best);
|
||||
#endif
|
||||
}
|
||||
|
||||
for(std::size_t i = 1; i < GRASP_N; ++i) {
|
||||
#ifdef SUBTIME
|
||||
timeit(RUSAGE_THREAD, "[GRASP] ", graspIter, problem, best, i*ELS_GEN);
|
||||
#else
|
||||
graspIter(problem, best, i*ELS_GEN);
|
||||
#endif
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
template<std::size_t K>
|
||||
auto hwGraspElsPar(tsp::Problem const& problem, std::vector<RNG>& rngs) {
|
||||
std::size_t const n = GRASP_N;
|
||||
std::size_t const maxThreads = K;
|
||||
|
||||
std::size_t const nThreads = std::min<std::size_t>(maxThreads, n);
|
||||
std::size_t const cores = maxThreads/nThreads;
|
||||
|
||||
std::vector<std::thread> threads(nThreads-1);
|
||||
std::size_t const step = n/nThreads;
|
||||
std::size_t const remainBase = n - step*nThreads;
|
||||
std::size_t remain = remainBase;
|
||||
|
||||
auto iter0 = [&problem,&rngs](tsp::Solution& best, std::size_t id, std::size_t cores) {
|
||||
best = hwGraspGen(problem, rngs, id, cores);
|
||||
};
|
||||
auto iter = [&problem,&rngs](tsp::Solution& best, std::size_t id, std::size_t cores) {
|
||||
tsp::Solution current = hwGraspGen(problem, rngs, id, cores);
|
||||
best = selectMin(std::move(best), std::move(current));
|
||||
};
|
||||
|
||||
auto run = [&](tsp::Solution& out, std::size_t id, std::size_t k, std::size_t cores) {
|
||||
tsp::Solution best{};
|
||||
|
||||
if(k) {
|
||||
#ifdef SUBTIME
|
||||
timeit(RUSAGE_THREAD, "[GRASP] ", iter0, best, id*ELS_GEN, cores);
|
||||
#else
|
||||
iter0(best, id*ELS_GEN, cores);
|
||||
#endif
|
||||
}
|
||||
|
||||
for(std::size_t i = 1; i < k; ++i) {
|
||||
#ifdef SUBTIME
|
||||
timeit(RUSAGE_THREAD, "[GRASP] ", iter, best, (id+i)*ELS_GEN, cores);
|
||||
#else
|
||||
iter(best, (id+1)*ELS_GEN, cores);
|
||||
#endif
|
||||
}
|
||||
|
||||
out = std::move(best);
|
||||
};
|
||||
|
||||
std::size_t start{};
|
||||
std::vector<tsp::Solution> bests(nThreads);
|
||||
|
||||
for(std::size_t i = 0; i < nThreads-1; ++i) {
|
||||
std::size_t offset = !!remain;
|
||||
remain -= offset;
|
||||
threads[i] = std::thread{run, std::ref(bests[i]), start, step+offset, cores};
|
||||
start += step+offset;
|
||||
}
|
||||
|
||||
run(bests[nThreads-1], start, step, cores);
|
||||
|
||||
for(std::thread& thread: threads) thread.join();
|
||||
|
||||
tsp::Solution best;
|
||||
if(nThreads) best = std::move(bests[0]);
|
||||
for(std::size_t i = 1; i < nThreads; ++i)
|
||||
best = selectMin(std::move(best), std::move(bests[i]));
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
/* *** */
|
||||
|
||||
tsp::Solution hw_seq_v(tsp::Problem const& p, RNG& seeder, Arguments const&) {
|
||||
std::size_t n = GRASP_N * ELS_GEN;
|
||||
|
||||
std::vector<RNG> rngs;
|
||||
rngs.reserve(n);
|
||||
for(std::size_t i = 0; i < n; ++i)
|
||||
rngs.emplace_back(seeder());
|
||||
|
||||
return hwGraspEls(p, rngs);
|
||||
}
|
||||
|
||||
tsp::Solution hw_par_v(tsp::Problem const& p, RNG& seeder, Arguments const&) {
|
||||
std::size_t n = GRASP_N * ELS_GEN;
|
||||
|
||||
std::vector<RNG> rngs;
|
||||
rngs.reserve(n);
|
||||
for(std::size_t i = 0; i < n; ++i)
|
||||
rngs.emplace_back(seeder());
|
||||
|
||||
return hwGraspElsPar<NTHREADS>(p, rngs);
|
||||
}
|
||||
49
bench/graspels/main.cpp
Normal file
49
bench/graspels/main.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <getopt.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "common.h"
|
||||
#include "decl.h"
|
||||
|
||||
Arguments cli(int argc, char **argv) {
|
||||
int option_index, option;
|
||||
struct option long_options[] = {
|
||||
{"seed", required_argument, 0, 's' },
|
||||
{0, 0, 0, 0 }
|
||||
};
|
||||
|
||||
Arguments args;
|
||||
args.seed = SEED;
|
||||
|
||||
optind = 0;
|
||||
while((option = getopt_long(argc, argv,
|
||||
"" "" "" "s:",
|
||||
long_options, &option_index)) != -1) {
|
||||
switch(option) {
|
||||
case 's': {
|
||||
std::istringstream iss{optarg};
|
||||
iss >> args.seed;
|
||||
} break;
|
||||
default:;
|
||||
}
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
Arguments args = cli(argc, argv);
|
||||
|
||||
tsp::Tsp tspData{DATA_FILE};
|
||||
tsp::Problem problem{tspData.points()};
|
||||
RNG rng{args.seed};
|
||||
|
||||
std::printf("conf: f: %s, data: %s, grasp: %s, outer: %s, inner: %s, threads: %s, seed: %zu\n",
|
||||
STR(FUNC), STR(DATA_FILE), STR(GRASP_N), STR(ELS_ITER_MAX), STR(ELS_GEN), STR(NTHREADS), args.seed);
|
||||
|
||||
tsp::Solution s;
|
||||
auto task = [&]{ s = FUNC(problem, rng, args); };
|
||||
|
||||
timeit(RUSAGE_SELF, "", task);
|
||||
|
||||
std::cout << "result: " << s.value() << std::endl;
|
||||
}
|
||||
18
bench/graspels/none.cpp
Normal file
18
bench/graspels/none.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
#include "common.h"
|
||||
|
||||
tsp::Solution none(tsp::Problem const&, RNG&, Arguments const&) {
|
||||
std::cout << 1+R"(
|
||||
Options:
|
||||
- DATA_FILE
|
||||
- GRASP_N
|
||||
- ELS_ITER_MAX
|
||||
- ELS_GEN
|
||||
- FUNC (mandatory)
|
||||
|
||||
Example:
|
||||
- g++ -Wall -Wextra -O2 -Isrc -Iinc -pthread src/tsp/*.cpp -DFUNC=sk_par2 -DNTHREADS=4 bench/graspels/*.cpp
|
||||
)";
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
37
bench/graspels/nrsk.cpp
Normal file
37
bench/graspels/nrsk.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "bad_graspels.h"
|
||||
#include "common.h"
|
||||
|
||||
using NRELS = rosa::SkelNREls<
|
||||
tsp::Solution, RNG,
|
||||
Descent,
|
||||
Move2Opt, Descent, FN(selectMin)
|
||||
>;
|
||||
|
||||
using NRGRASPxELS = rosa::SkelNRGrasp<
|
||||
tsp::Problem, tsp::Solution, RNG,
|
||||
RGreedy<tsp::Solution>, NRELS,
|
||||
FN(selectMin)
|
||||
>;
|
||||
|
||||
tsp::Solution sk_nr_seq(tsp::Problem const& p, RNG& rng, Arguments const&) {
|
||||
auto graspEls = alsk::implement<alsk::exec::Sequential, NRGRASPxELS>();
|
||||
graspEls.executor.repeatability.disabled();
|
||||
graspEls.skeleton.task.task<0>() = rgreedy();
|
||||
graspEls.skeleton.task.task<1>().task<1>().n = ELS_ITER_MAX;
|
||||
graspEls.skeleton.task.task<1>().task<1>().task.n = ELS_GEN;
|
||||
graspEls.skeleton.n = GRASP_N;
|
||||
|
||||
return graspEls(p, rng);
|
||||
}
|
||||
|
||||
tsp::Solution sk_nr_par(tsp::Problem const& p, RNG& rng, Arguments const&) {
|
||||
auto graspEls = alsk::implement<alsk::exec::FirstLevelNoOpti, NRGRASPxELS>();
|
||||
graspEls.executor.cores = NTHREADS;
|
||||
graspEls.executor.repeatability.disabled();
|
||||
graspEls.skeleton.task.task<0>() = rgreedy();
|
||||
graspEls.skeleton.task.task<1>().task<1>().n = ELS_ITER_MAX;
|
||||
graspEls.skeleton.task.task<1>().task<1>().task.n = ELS_GEN;
|
||||
graspEls.skeleton.n = GRASP_N;
|
||||
|
||||
return graspEls(p, rng);
|
||||
}
|
||||
74
bench/graspels/sk.cpp
Normal file
74
bench/graspels/sk.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include "common.h"
|
||||
|
||||
using ELS = rosa::SkelEls<
|
||||
tsp::Solution,
|
||||
Descent,
|
||||
Move2Opt, Descent, FN(selectMin)
|
||||
>;
|
||||
|
||||
using GRASPxELS = rosa::SkelGrasp<
|
||||
tsp::Problem, tsp::Solution,
|
||||
RGreedy<tsp::Solution>, ELS,
|
||||
FN(selectMin)
|
||||
>;
|
||||
|
||||
tsp::Solution sk_seq(tsp::Problem const& p, RNG&, Arguments const& args) {
|
||||
auto graspEls = alsk::implement<alsk::exec::Sequential, GRASPxELS>();
|
||||
graspEls.executor.repeatability.upTo(4);
|
||||
graspEls.state.context.seed = args.seed;
|
||||
graspEls.executor.cores = 1;
|
||||
graspEls.skeleton.task.task<0>() = rgreedy();
|
||||
graspEls.skeleton.task.task<1>().task<1>().n = ELS_ITER_MAX;
|
||||
graspEls.skeleton.task.task<1>().task<1>().task.n = ELS_GEN;
|
||||
graspEls.skeleton.n = GRASP_N;
|
||||
|
||||
return graspEls(p);
|
||||
}
|
||||
|
||||
tsp::Solution sk_par_firstlevel(tsp::Problem const& p, RNG&, Arguments const& args) {
|
||||
auto graspEls = alsk::implement<alsk::exec::FirstLevelEqui, GRASPxELS>();
|
||||
graspEls.executor.repeatability.upTo(4);
|
||||
graspEls.state.context.seed = args.seed;
|
||||
graspEls.executor.cores = NTHREADS;
|
||||
graspEls.skeleton.task.task<0>() = rgreedy();
|
||||
graspEls.skeleton.task.task<1>().task<1>().n = ELS_ITER_MAX;
|
||||
graspEls.skeleton.task.task<1>().task<1>().task.n = ELS_GEN;
|
||||
graspEls.skeleton.n = GRASP_N;
|
||||
|
||||
return graspEls(p);
|
||||
}
|
||||
|
||||
tsp::Solution sk_par_staticpool(tsp::Problem const& p, RNG&, Arguments const& args) {
|
||||
auto graspEls = alsk::implement<alsk::exec::StaticPool, GRASPxELS>();
|
||||
graspEls.state.context.seed = args.seed;
|
||||
graspEls.executor.cores = NTHREADS;
|
||||
graspEls.skeleton.task.task<0>() = rgreedy();
|
||||
graspEls.skeleton.task.task<1>().task<1>().n = ELS_ITER_MAX;
|
||||
graspEls.skeleton.task.task<1>().task<1>().task.n = ELS_GEN;
|
||||
graspEls.skeleton.n = GRASP_N;
|
||||
|
||||
return graspEls(p);
|
||||
}
|
||||
|
||||
tsp::Solution sk_par_dynamicpool(tsp::Problem const& p, RNG&, Arguments const& args) {
|
||||
auto graspEls = alsk::implement<alsk::exec::DynamicPool, GRASPxELS>();
|
||||
graspEls.state.context.seed = args.seed;
|
||||
graspEls.executor.cores = NTHREADS;
|
||||
graspEls.skeleton.task.task<0>() = rgreedy();
|
||||
graspEls.skeleton.task.task<1>().task<1>().n = ELS_ITER_MAX;
|
||||
graspEls.skeleton.task.task<1>().task<1>().task.n = ELS_GEN;
|
||||
graspEls.skeleton.n = GRASP_N;
|
||||
|
||||
return graspEls(p);
|
||||
}
|
||||
|
||||
tsp::Solution sk_par_thread(tsp::Problem const& p, RNG&, Arguments const&) {
|
||||
auto graspEls = alsk::implement<alsk::exec::StaticThread, GRASPxELS>();
|
||||
graspEls.executor.cores = NTHREADS;
|
||||
graspEls.skeleton.task.task<0>() = rgreedy();
|
||||
graspEls.skeleton.task.task<1>().task<1>().n = ELS_ITER_MAX;
|
||||
graspEls.skeleton.task.task<1>().task<1>().task.n = ELS_GEN;
|
||||
graspEls.skeleton.n = GRASP_N;
|
||||
|
||||
return graspEls(p);
|
||||
}
|
||||
111
bench/graspels/tbb.cpp
Normal file
111
bench/graspels/tbb.cpp
Normal file
@@ -0,0 +1,111 @@
|
||||
#ifdef WITH_TBB
|
||||
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
#include <tbb/flow_graph.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "decl.h"
|
||||
|
||||
/* *** */
|
||||
/* TBB */
|
||||
|
||||
auto tbbElsInner(tsp::Solution const& solution, RNG& rng, std::size_t nCore) {
|
||||
tsp::Solution best;
|
||||
std::vector<tsp::Solution> solutions(ELS_GEN);
|
||||
|
||||
/* repeatability at loop level */
|
||||
std::vector<RNG> rngs(ELS_GEN);
|
||||
for(std::size_t i = 0; i < ELS_GEN; ++i)
|
||||
rngs[i].seed(rng());
|
||||
/* ***** */
|
||||
|
||||
using ElsGenP = std::tuple<tsp::Solution const*, RNG*, tsp::Solution*>;
|
||||
using ElsGenR = std::tuple<tsp::Solution, tsp::Solution*>;
|
||||
|
||||
tbb::flow::graph g;
|
||||
tbb::flow::function_node<ElsGenP, ElsGenR> fElsGen(g, nCore,
|
||||
[](ElsGenP t) { return std::make_tuple(hwElsGen(*std::get<0>(t), *std::get<1>(t)), std::get<2>(t)); }
|
||||
);
|
||||
tbb::flow::function_node<ElsGenR, bool> fSelectMin(g, nCore,
|
||||
[](ElsGenR t) { *std::get<1>(t) = (selectMin(std::get<0>(t), *std::get<1>(t))); return true; }
|
||||
);
|
||||
|
||||
tbb::flow::make_edge(fElsGen, fSelectMin);
|
||||
|
||||
if(ELS_GEN)
|
||||
solutions[0] = hwElsGen(solution, rngs[0]);
|
||||
for(std::size_t i = 1; i < ELS_GEN; ++i)
|
||||
fElsGen.try_put(std::make_tuple(&solution, &rngs[i], &solutions[i]));
|
||||
|
||||
g.wait_for_all();
|
||||
|
||||
best = *std::min_element(std::begin(solutions), std::end(solutions));
|
||||
return best;
|
||||
}
|
||||
|
||||
auto tbbEls(tsp::Solution const& solution, RNG& rng,std::size_t nCore) {
|
||||
tsp::Solution best = Descent{}(solution);
|
||||
|
||||
for(std::size_t i = 0; i < ELS_ITER_MAX; ++i) {
|
||||
tsp::Solution current = tbbElsInner(best, rng, nCore);
|
||||
best = selectMin(std::move(best), std::move(current));
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
auto tbbGraspGen(tsp::Problem const& problem, RNG& rng, std::size_t nCore) {
|
||||
return tbbEls(rgreedy()(problem, rng), rng, nCore);
|
||||
}
|
||||
|
||||
template<std::size_t K>
|
||||
tsp::Solution tbbGraspElsPar(tsp::Problem const& problem, RNG& rng) {
|
||||
tsp::Solution best;
|
||||
std::vector<tsp::Solution> solutions(GRASP_N);
|
||||
|
||||
tbb::task_scheduler_init init(K);
|
||||
|
||||
/* repeatability at loop level */
|
||||
std::vector<RNG> rngs(GRASP_N);
|
||||
for(std::size_t i = 0; i < GRASP_N; ++i)
|
||||
rngs[i].seed(rng());
|
||||
/* ***** */
|
||||
|
||||
using GraspGenP = std::tuple<tsp::Problem const*, RNG*, unsigned long, tsp::Solution*>;
|
||||
using GraspGenR = std::tuple<tsp::Solution, tsp::Solution*>;
|
||||
|
||||
tbb::flow::graph g;
|
||||
tbb::flow::function_node<GraspGenP, GraspGenR> fGraspGen(g, K,
|
||||
[](GraspGenP t) { return std::make_tuple(tbbGraspGen(*std::get<0>(t), *std::get<1>(t), std::get<2>(t)), std::get<3>(t)); }
|
||||
);
|
||||
tbb::flow::function_node<GraspGenR, bool> fSelectMin(g, K,
|
||||
[](GraspGenR t) { *std::get<1>(t) = (selectMin(std::get<0>(t), *std::get<1>(t))); return true; }
|
||||
);
|
||||
|
||||
tbb::flow::make_edge(fGraspGen, fSelectMin);
|
||||
|
||||
for(std::size_t i = 0; i < GRASP_N; ++i)
|
||||
fGraspGen.try_put(std::make_tuple(&problem, &rngs[i], K, &solutions[i]));
|
||||
|
||||
g.wait_for_all();
|
||||
|
||||
/* Selection */
|
||||
best = *std::min_element(std::begin(solutions), std::end(solutions));
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
tsp::Solution tbb_par(tsp::Problem const& p, RNG& rng, Arguments const&) {
|
||||
return tbbGraspElsPar<NTHREADS>(p, rng);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#include "common.h"
|
||||
|
||||
tsp::Solution tbb_par(tsp::Problem const&, RNG&, Arguments const&) {
|
||||
std::clog << "must compile with -ltbb -DWITH_TBB to enable TBB\n";
|
||||
return {};
|
||||
}
|
||||
|
||||
#endif
|
||||
390
celero/celero/Archive.cpp
Normal file
390
celero/celero/Archive.cpp
Normal file
@@ -0,0 +1,390 @@
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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 <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)
|
||||
{
|
||||
assert(x.empty() == false);
|
||||
this->pimpl->fileName = x;
|
||||
this->pimpl->readExistingResults();
|
||||
}
|
||||
|
||||
void Archive::add(std::shared_ptr<celero::ExperimentResult> x)
|
||||
{
|
||||
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;
|
||||
|
||||
r.GroupName = x->getExperiment()->getBenchmark()->getName();
|
||||
r.RunName = x->getExperiment()->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;
|
||||
}
|
||||
}
|
||||
}
|
||||
102
celero/celero/Archive.h
Normal file
102
celero/celero/Archive.h
Normal file
@@ -0,0 +1,102 @@
|
||||
#ifndef H_CELERO_ARCHIVE_H
|
||||
#define H_CELERO_ARCHIVE_H
|
||||
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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/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&)
|
||||
{
|
||||
}
|
||||
|
||||
///
|
||||
/// 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
|
||||
120
celero/celero/Benchmark.cpp
Normal file
120
celero/celero/Benchmark.cpp
Normal file
@@ -0,0 +1,120 @@
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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/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()
|
||||
{
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// This is unsafe, but not user code. I'll accept the risk.
|
||||
return this->pimpl->experiments[x];
|
||||
}
|
||||
|
||||
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
celero/celero/Benchmark.h
Normal file
112
celero/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 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.
|
||||
///
|
||||
Benchmark(const std::string& name);
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
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
celero/celero/Callbacks.cpp
Normal file
52
celero/celero/Callbacks.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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/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
celero/celero/Callbacks.h
Normal file
65
celero/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 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
|
||||
{
|
||||
void ExperimentComplete(std::shared_ptr<Experiment> x);
|
||||
void ExperimentResultComplete(std::shared_ptr<celero::ExperimentResult> x);
|
||||
} // namespace impl
|
||||
} // namespace celero
|
||||
|
||||
#endif
|
||||
256
celero/celero/Celero.cpp
Normal file
256
celero/celero/Celero.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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/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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if(argument.empty() == false)
|
||||
{
|
||||
executor::Run(argument);
|
||||
}
|
||||
else
|
||||
{
|
||||
executor::RunAll();
|
||||
}
|
||||
|
||||
if(mustCloseFile == true)
|
||||
{
|
||||
celero::ResultTable::Instance().closeFile();
|
||||
}
|
||||
|
||||
// Final output.
|
||||
std::cout << "Complete." << std::endl;
|
||||
}
|
||||
315
celero/celero/Celero.h
Normal file
315
celero/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 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<Benchmark> RegisterTest(const char* groupName, const char* benchmarkName, const uint64_t samples,
|
||||
const uint64_t iterations, const uint64_t threads,
|
||||
std::shared_ptr<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
|
||||
935
celero/celero/CommandLine.h
Normal file
935
celero/celero/CommandLine.h
Normal file
@@ -0,0 +1,935 @@
|
||||
/*
|
||||
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";
|
||||
}
|
||||
|
||||
} // 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()
|
||||
{
|
||||
}
|
||||
~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;
|
||||
};
|
||||
|
||||
} // cmdline
|
||||
254
celero/celero/Console.cpp
Normal file
254
celero/celero/Console.cpp
Normal file
@@ -0,0 +1,254 @@
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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/Console.h>
|
||||
|
||||
using namespace celero;
|
||||
|
||||
#ifdef WIN32
|
||||
#include <Windows.h>
|
||||
#include <stdio.h>
|
||||
#else
|
||||
#include <curses.h>
|
||||
#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 ConsoleColor_Red:
|
||||
Red();
|
||||
break;
|
||||
case ConsoleColor_Red_Bold:
|
||||
RedBold();
|
||||
break;
|
||||
case ConsoleColor_Green:
|
||||
Green();
|
||||
break;
|
||||
case ConsoleColor_Green_Bold:
|
||||
GreenBold();
|
||||
break;
|
||||
case ConsoleColor_Blue:
|
||||
Blue();
|
||||
break;
|
||||
case ConsoleColor_Blue_Bold:
|
||||
BlueBold();
|
||||
break;
|
||||
case ConsoleColor_Cyan:
|
||||
Cyan();
|
||||
break;
|
||||
case ConsoleColor_Cyan_Bold:
|
||||
CyanBold();
|
||||
break;
|
||||
case ConsoleColor_Yellow:
|
||||
Yellow();
|
||||
break;
|
||||
case ConsoleColor_Yellow_Bold:
|
||||
YellowBold();
|
||||
break;
|
||||
case ConsoleColor_White:
|
||||
White();
|
||||
break;
|
||||
case ConsoleColor_White_Bold:
|
||||
WhiteBold();
|
||||
break;
|
||||
case ConsoleColor_WhiteOnRed:
|
||||
WhiteOnRed();
|
||||
break;
|
||||
case ConsoleColor_WhiteOnRed_Bold:
|
||||
WhiteOnRedBold();
|
||||
break;
|
||||
case ConsoleColor_Purple_Bold:
|
||||
PurpleBold();
|
||||
break;
|
||||
case ConsoleColor_Default:
|
||||
default:
|
||||
Default();
|
||||
break;
|
||||
}
|
||||
}
|
||||
65
celero/celero/Console.h
Normal file
65
celero/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 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 ConsoleColor
|
||||
{
|
||||
ConsoleColor_Default,
|
||||
ConsoleColor_Red,
|
||||
ConsoleColor_Red_Bold,
|
||||
ConsoleColor_Green,
|
||||
ConsoleColor_Green_Bold,
|
||||
ConsoleColor_Blue,
|
||||
ConsoleColor_Blue_Bold,
|
||||
ConsoleColor_Cyan,
|
||||
ConsoleColor_Cyan_Bold,
|
||||
ConsoleColor_Yellow,
|
||||
ConsoleColor_Yellow_Bold,
|
||||
ConsoleColor_White,
|
||||
ConsoleColor_White_Bold,
|
||||
ConsoleColor_WhiteOnRed,
|
||||
ConsoleColor_WhiteOnRed_Bold,
|
||||
ConsoleColor_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
|
||||
160
celero/celero/Distribution.cpp
Normal file
160
celero/celero/Distribution.cpp
Normal file
@@ -0,0 +1,160 @@
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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/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);
|
||||
|
||||
auto series1 = celero::BuildDistribution(intArgument, uint64_t(64));
|
||||
auto series2 = celero::BuildDistribution(intArgument, uint64_t(256));
|
||||
auto series3 = celero::BuildDistribution(intArgument, uint64_t(1024));
|
||||
auto series4 = celero::BuildDistribution(intArgument, uint64_t(4096));
|
||||
|
||||
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
celero/celero/Distribution.h
Normal file
39
celero/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 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
celero/celero/Exceptions.cpp
Normal file
251
celero/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* const 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
celero/celero/Exceptions.h
Normal file
64
celero/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 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
|
||||
360
celero/celero/Executor.cpp
Normal file
360
celero/celero/Executor.cpp
Normal file
@@ -0,0 +1,360 @@
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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/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)
|
||||
{
|
||||
// The smallest test should take at least 10x as long as our timer's resolution.
|
||||
// I chose "10x" arbitrarily.
|
||||
const auto minTestTime = static_cast<int64_t>(celero::timer::CachePerformanceFrequency(true) * 1e6) * 10;
|
||||
|
||||
// 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();
|
||||
|
||||
experiment->setIterations(iterations);
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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.
|
||||
{
|
||||
const auto testValues = e->getFactory()->Create()->getExperimentValues();
|
||||
const auto valueResultScale = e->getFactory()->Create()->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
celero/celero/Executor.h
Normal file
82
celero/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 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.
|
||||
///
|
||||
void RunAll();
|
||||
|
||||
///
|
||||
/// Run all baselines (but not experiments) registered within the final application.
|
||||
///
|
||||
void RunAllBaselines();
|
||||
|
||||
///
|
||||
/// Run a specific benchmark's baseline.
|
||||
///
|
||||
void RunBaseline(std::shared_ptr<Benchmark> x);
|
||||
|
||||
///
|
||||
/// Run all experiments registered within the final application.
|
||||
///
|
||||
void RunAllExperiments();
|
||||
|
||||
///
|
||||
/// Run all experiments within a specific benchmark.
|
||||
///
|
||||
void RunExperiments(std::shared_ptr<Benchmark> x);
|
||||
|
||||
///
|
||||
/// Run a specific benchmark.
|
||||
///
|
||||
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.
|
||||
///
|
||||
void Run(std::shared_ptr<Experiment> x);
|
||||
|
||||
///
|
||||
/// Run a specific benchmark with the specified name.
|
||||
///
|
||||
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.
|
||||
///
|
||||
void Run(const std::string& group, const std::string& experiment);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
315
celero/celero/Experiment.cpp
Normal file
315
celero/celero/Experiment.cpp
Normal file
@@ -0,0 +1,315 @@
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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/Experiment.h>
|
||||
#include <celero/Factory.h>
|
||||
#include <celero/PimplImpl.h>
|
||||
#include <celero/TestFixture.h>
|
||||
#include <celero/TestVector.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)
|
||||
{
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/// Used to pass/fail benchmarks when outputting JUnit.
|
||||
double baselineTarget;
|
||||
|
||||
/// Test samples to complete.
|
||||
uint64_t samples;
|
||||
|
||||
/// Iterations per test run. (Size of each sample.)
|
||||
uint64_t iterations;
|
||||
|
||||
/// Threads per test run.
|
||||
uint64_t threads;
|
||||
|
||||
/// The best run time for this test
|
||||
uint64_t totalRunTime;
|
||||
|
||||
bool isBaselineCase;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
196
celero/celero/Experiment.h
Normal file
196
celero/celero/Experiment.h
Normal file
@@ -0,0 +1,196 @@
|
||||
#ifndef H_CELERO_EXPERIMENT_H
|
||||
#define H_CELERO_EXPERIMENT_H
|
||||
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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/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<Benchmark> benchmark);
|
||||
|
||||
///
|
||||
///
|
||||
///
|
||||
explicit Experiment(std::weak_ptr<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<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<Factory> x);
|
||||
|
||||
///
|
||||
/// Gets the factory used to create this experiment's test fixtures.
|
||||
///
|
||||
std::shared_ptr<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 Experiment&);
|
||||
|
||||
///
|
||||
///
|
||||
/// \brief Pimpl Idiom
|
||||
///
|
||||
class Impl;
|
||||
|
||||
///
|
||||
/// \brief Pimpl Idiom
|
||||
///
|
||||
Pimpl<Impl> pimpl;
|
||||
};
|
||||
} // namespace celero
|
||||
|
||||
#endif
|
||||
206
celero/celero/ExperimentResult.cpp
Normal file
206
celero/celero/ExperimentResult.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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/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
celero/celero/ExperimentResult.h
Normal file
161
celero/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 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
celero/celero/Export.h
Normal file
40
celero/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 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
celero/celero/Factory.h
Normal file
59
celero/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 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
celero/celero/FileReader.h
Normal file
50
celero/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 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
celero/celero/GenericFactory.h
Normal file
60
celero/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 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
celero/celero/JUnit.cpp
Normal file
149
celero/celero/JUnit.cpp
Normal file
@@ -0,0 +1,149 @@
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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 <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
celero/celero/JUnit.h
Normal file
73
celero/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 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
celero/celero/Memory.cpp
Normal file
392
celero/celero/Memory.cpp
Normal file
@@ -0,0 +1,392 @@
|
||||
///
|
||||
/// \author John Farrier
|
||||
///
|
||||
/// \copyright Copyright 2015, 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/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;
|
||||
|
||||
constexpr int64_t Kilobytes2Bytes{1024};
|
||||
|
||||
#ifdef WIN32
|
||||
#else
|
||||
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
celero/celero/Memory.h
Normal file
142
celero/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
celero/celero/Pimpl.h
Normal file
65
celero/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 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
celero/celero/PimplImpl.h
Normal file
98
celero/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 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();
|
||||
}
|
||||