commit caf2a692f9af41ce5de977a6fadd99f8d5e92c8a Author: Alexis Pereda Date: Mon May 10 18:14:24 2021 +0200 thesis version diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e65b65 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +a.out +build/ +release/ +clang/ +__pycache__/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ef5342b --- /dev/null +++ b/CMakeLists.txt @@ -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() diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ffdc907 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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 . + +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: + + 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 +. + + 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 +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..bc9f893 --- /dev/null +++ b/README.md @@ -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: + +
+ +When implemented as an algorithmic skeleton, its representation becomes this tree: + +
+ +Obtained using the following source code. + +For the internal ELS: +```cpp +template +using SkelElsStruct = +S, + InnerSelect // Sel3 + >, + OuterSelect // Sel2 + > +>; + +template +using SkelElsLinks = +L(Solution const&), + Solution(P<0>), + L const&), + L(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::skeleton< + Pack, + Pack +>; +``` + +For the GRASP: +```cpp +template +using SkelGraspStructure = +S, + Select // Sel1 +>; + +template +using SkelGraspLinks = +L(P<0>), + Solution(P<0>, RNG), + Solution(R<0>) + >, + Solution(Solution, Solution) +>; + +template +using SkelGrasp = BuildSkeleton::skeleton< + Pack, + Pack +>; +``` + +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, 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: +
    +
  • "hw_seq": handwritten sequential implementation; + +
  • +
  • "hw_par": handwritten parallel implementation;
  • +
  • "sk_seq": skeleton without parallelisation;
  • +
  • "sk_firstlevel": skeleton with parallelisation of the first level;
  • +
  • "sk_staticpool": skeleton with parallelisation using a thread pool with static task distribution;
  • +
  • "sk_dynamicpool": skeleton with parallelisation using a classical thread pool with dynamic task distribution;
  • +
  • "sk_thread": skeleton with parallelisation using dynamically created threads.
  • +
+ +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. +
+ +For parallel executions, measures give the following data. + +With 24 iterations for the outmost parallel loop: +
+
+ +With only 4 iterations for the outmost parallel loop: +
+
+ +## 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 +``` diff --git a/bench/graspels/bad_graspels.h b/bench/graspels/bad_graspels.h new file mode 100644 index 0000000..e155854 --- /dev/null +++ b/bench/graspels/bad_graspels.h @@ -0,0 +1,106 @@ +#ifndef ROSA_BENCH_GRASPELS_BAD_GRASPELS_H +#define ROSA_BENCH_GRASPELS_BAD_GRASPELS_H + +#include + +namespace rosa { + +/* GRASP + * loop + * * s = init() + * * s = ls(s) + * * best = select(s, best) + * ---- + * return best + */ +template +using SkelNRGraspStructure = +alsk::S, + Select +>; + +template +using SkelNRGraspLinks = +alsk::L(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::skeleton< + tmp::Pack, + tmp::Pack +>; + +} + +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, + InnerSelect + >, + OuterSelect + > +>; + +template +using SkelNRElsLinks = +alsk::L(Solution const&, RNG&), + Solution(alsk::arg::P<0>), + alsk::L const&, alsk::arg::P<1>), + alsk::L), + alsk::L(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::skeleton< + tmp::Pack, + tmp::Pack +>; + +} + +#endif diff --git a/bench/graspels/common.h b/bench/graspels/common.h new file mode 100644 index 0000000..ebc7425 --- /dev/null +++ b/bench/graspels/common.h @@ -0,0 +1,86 @@ +#ifndef ROSA_BENCH_GRASPELS_COMMON_H +#define ROSA_BENCH_GRASPELS_COMMON_H + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#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{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 +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)(std::forward(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(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 diff --git a/bench/graspels/decl.h b/bench/graspels/decl.h new file mode 100644 index 0000000..71e91ea --- /dev/null +++ b/bench/graspels/decl.h @@ -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 diff --git a/bench/graspels/hw.cpp b/bench/graspels/hw.cpp new file mode 100644 index 0000000..826f445 --- /dev/null +++ b/bench/graspels/hw.cpp @@ -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(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 threads{nThreads-1}; + std::vector solutions(nThreads); + + tsp::Solution best; + + /* repeatability at loop level */ + std::vector 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 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 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 +auto hwGraspElsPar(tsp::Problem const& problem, RNG& rng) { + std::size_t const nThreads = std::min(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 threadsA{nThreads-1}; + std::vector threadsB{remain==0 ? 0 : remain-1}; + + std::vector solutions(nThreads+remain); + + /* repeatability at loop level */ + std::vector 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(p, rng); +} diff --git a/bench/graspels/hwv.cpp b/bench/graspels/hwv.cpp new file mode 100644 index 0000000..323d732 --- /dev/null +++ b/bench/graspels/hwv.cpp @@ -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& 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(maxThreads, ELS_GEN); + + std::vector 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 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& 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& 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& 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 +auto hwGraspElsPar(tsp::Problem const& problem, std::vector& rngs) { + std::size_t const n = GRASP_N; + std::size_t const maxThreads = K; + + std::size_t const nThreads = std::min(maxThreads, n); + std::size_t const cores = maxThreads/nThreads; + + std::vector 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 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 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 rngs; + rngs.reserve(n); + for(std::size_t i = 0; i < n; ++i) + rngs.emplace_back(seeder()); + + return hwGraspElsPar(p, rngs); +} diff --git a/bench/graspels/main.cpp b/bench/graspels/main.cpp new file mode 100644 index 0000000..a566f3f --- /dev/null +++ b/bench/graspels/main.cpp @@ -0,0 +1,49 @@ +#include +#include + +#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; +} diff --git a/bench/graspels/none.cpp b/bench/graspels/none.cpp new file mode 100644 index 0000000..7a7547a --- /dev/null +++ b/bench/graspels/none.cpp @@ -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 {}; +} + diff --git a/bench/graspels/nrsk.cpp b/bench/graspels/nrsk.cpp new file mode 100644 index 0000000..b7112d3 --- /dev/null +++ b/bench/graspels/nrsk.cpp @@ -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, NRELS, + FN(selectMin) +>; + +tsp::Solution sk_nr_seq(tsp::Problem const& p, RNG& rng, Arguments const&) { + auto graspEls = alsk::implement(); + 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(); + 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); +} diff --git a/bench/graspels/sk.cpp b/bench/graspels/sk.cpp new file mode 100644 index 0000000..2b89eb4 --- /dev/null +++ b/bench/graspels/sk.cpp @@ -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, ELS, + FN(selectMin) +>; + +tsp::Solution sk_seq(tsp::Problem const& p, RNG&, Arguments const& args) { + auto graspEls = alsk::implement(); + 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(); + 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(); + 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(); + 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(); + 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); +} diff --git a/bench/graspels/tbb.cpp b/bench/graspels/tbb.cpp new file mode 100644 index 0000000..eaa1353 --- /dev/null +++ b/bench/graspels/tbb.cpp @@ -0,0 +1,111 @@ +#ifdef WITH_TBB + +#include +#include + +#include "common.h" +#include "decl.h" + +/* *** */ +/* TBB */ + +auto tbbElsInner(tsp::Solution const& solution, RNG& rng, std::size_t nCore) { + tsp::Solution best; + std::vector solutions(ELS_GEN); + + /* repeatability at loop level */ + std::vector rngs(ELS_GEN); + for(std::size_t i = 0; i < ELS_GEN; ++i) + rngs[i].seed(rng()); + /* ***** */ + + using ElsGenP = std::tuple; + using ElsGenR = std::tuple; + + tbb::flow::graph g; + tbb::flow::function_node 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 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 +tsp::Solution tbbGraspElsPar(tsp::Problem const& problem, RNG& rng) { + tsp::Solution best; + std::vector solutions(GRASP_N); + + tbb::task_scheduler_init init(K); + + /* repeatability at loop level */ + std::vector rngs(GRASP_N); + for(std::size_t i = 0; i < GRASP_N; ++i) + rngs[i].seed(rng()); + /* ***** */ + + using GraspGenP = std::tuple; + using GraspGenR = std::tuple; + + tbb::flow::graph g; + tbb::flow::function_node 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 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(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 diff --git a/celero/celero/Archive.cpp b/celero/celero/Archive.cpp new file mode 100644 index 0000000..0d83912 --- /dev/null +++ b/celero/celero/Archive.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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& 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( + std::chrono::duration_cast(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::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 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 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; + } + } +} diff --git a/celero/celero/Archive.h b/celero/celero/Archive.h new file mode 100644 index 0000000..ad01e8a --- /dev/null +++ b/celero/celero/Archive.h @@ -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 +#include +#include + +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 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 pimpl; + }; +} // namespace celero + +#endif diff --git a/celero/celero/Benchmark.cpp b/celero/celero/Benchmark.cpp new file mode 100644 index 0000000..6f9fb3d --- /dev/null +++ b/celero/celero/Benchmark.cpp @@ -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 +#include +#include +#include +#include + +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 stats; + + /// Group name + std::string name; + + std::shared_ptr baseline; + std::vector> 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 x) +{ + this->pimpl->baseline = x; +} + +std::shared_ptr Benchmark::getBaseline() const +{ + return this->pimpl->baseline; +} + +void Benchmark::addExperiment(std::shared_ptr x) +{ + this->pimpl->experiments.push_back(x); +} + +std::shared_ptr 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 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(); +} diff --git a/celero/celero/Benchmark.h b/celero/celero/Benchmark.h new file mode 100644 index 0000000..d853e5f --- /dev/null +++ b/celero/celero/Benchmark.h @@ -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 +#include +#include +#include + +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 x); + + /// + /// Gets the baseline case associated this benchmark. + /// + std::shared_ptr getBaseline() const; + + /// + /// + /// + void addExperiment(std::shared_ptr x); + + /// + /// Gets the test case associated with the given experiment index. + /// + std::shared_ptr getExperiment(size_t experimentIndex); + + /// + /// Gets the test case associated with the given experiment name. + /// + std::shared_ptr 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 pimpl; + }; +} // namespace celero + +#endif diff --git a/celero/celero/Callbacks.cpp b/celero/celero/Callbacks.cpp new file mode 100644 index 0000000..a99973b --- /dev/null +++ b/celero/celero/Callbacks.cpp @@ -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 +#include +#include + +using namespace celero; + +std::vector)>> ExperimentFunctions; +std::vector)>> ExperimentResultFunctions; + +void celero::impl::ExperimentComplete(std::shared_ptr x) +{ + for(auto& i : ExperimentFunctions) + { + i(x); + } +} + +void celero::impl::ExperimentResultComplete(std::shared_ptr x) +{ + for(auto& i : ExperimentResultFunctions) + { + i(x); + } +} + +void celero::AddExperimentCompleteFunction(std::function)> x) +{ + ExperimentFunctions.push_back(x); +} + +void celero::AddExperimentResultCompleteFunction(std::function)> x) +{ + ExperimentResultFunctions.push_back(x); +} diff --git a/celero/celero/Callbacks.h b/celero/celero/Callbacks.h new file mode 100644 index 0000000..7f58ea9 --- /dev/null +++ b/celero/celero/Callbacks.h @@ -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 +#include +#include +#include + +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)> 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)> x); + + namespace impl + { + void ExperimentComplete(std::shared_ptr x); + void ExperimentResultComplete(std::shared_ptr x); + } // namespace impl +} // namespace celero + +#endif diff --git a/celero/celero/Celero.cpp b/celero/celero/Celero.cpp new file mode 100644 index 0000000..ab72a47 --- /dev/null +++ b/celero/celero/Celero.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace celero; + +std::shared_ptr celero::RegisterTest(const char* groupName, const char* benchmarkName, const uint64_t samples, + const uint64_t iterations, const uint64_t threads, + std::shared_ptr experimentFactory, const double target) +{ + auto bm = celero::TestVector::Instance()[groupName]; + + if(bm == nullptr) + { + bm = std::make_shared(groupName); + celero::TestVector::Instance().push_back(bm); + } + + auto p = std::make_shared(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::RegisterBaseline(const char* groupName, const char* benchmarkName, const uint64_t samples, + const uint64_t iterations, const uint64_t threads, + std::shared_ptr experimentFactory) +{ + auto bm = celero::TestVector::Instance()[groupName]; + + if(bm == nullptr) + { + bm = std::make_shared(groupName); + celero::TestVector::Instance().push_back(bm); + } + + auto p = std::make_shared(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("group", 'g', "Runs a specific group of benchmarks.", false, ""); + args.add("outputTable", 't', "Saves a results table to the named file.", false, ""); + args.add("junit", 'j', "Saves a JUnit XML-formatted file to the named file.", false, ""); + args.add("archive", 'a', "Saves or updates a result archive file.", false, ""); + args.add("distribution", 'd', "Builds a file to help characterize the distribution of measurements and exits.", false, 0); + args.add("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 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("distribution"); + if(intArgument > 0) + { + RunDistribution(intArgument); + } + + // Has a result output file been specified? + auto mustCloseFile = false; + auto argument = args.get("outputTable"); + if(argument.empty() == false) + { + std::cout << "Writing results to: " << argument << std::endl; + celero::ResultTable::Instance().setFileName(argument); + + celero::AddExperimentResultCompleteFunction([](std::shared_ptr p) { celero::ResultTable::Instance().add(p); }); + mustCloseFile = true; + } + + // Has a result output file been specified? + argument = args.get("archive"); + if(argument.empty() == false) + { + std::cout << "Archiving results to: " << argument << std::endl; + celero::Archive::Instance().setFileName(argument); + + celero::AddExperimentResultCompleteFunction([](std::shared_ptr p) { celero::Archive::Instance().add(p); }); + } + + // Has a JUnit output file been specified? + argument = args.get("junit"); + if(argument.empty() == false) + { + std::cout << "Writing JUnit results to: " << argument << std::endl; + celero::JUnit::Instance().setFileName(argument); + + celero::AddExperimentResultCompleteFunction([](std::shared_ptr 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("catchExceptions")); + } + + // Has a run group been specified? + argument = args.get("group"); + + // Collect all user-defined fields + std::set userDefinedFields; + auto collectFromBenchmark = [&](std::shared_ptr 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 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; +} diff --git a/celero/celero/Celero.h b/celero/celero/Celero.h new file mode 100644 index 0000000..ce241be --- /dev/null +++ b/celero/celero/Celero.h @@ -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 +#endif + +#include +#include +#include +#include +#include +#include +#include + +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 RegisterTest(const char* groupName, const char* benchmarkName, const uint64_t samples, + const uint64_t iterations, const uint64_t threads, + std::shared_ptr 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 RegisterBaseline(const char* groupName, const char* benchmarkName, const uint64_t samples, + const uint64_t iterations, const uint64_t threads, + std::shared_ptr 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 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>()); \ + \ + 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>(), 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>()); \ + \ + 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 diff --git a/celero/celero/CommandLine.h b/celero/celero/CommandLine.h new file mode 100644 index 0000000..52e645f --- /dev/null +++ b/celero/celero/CommandLine.h @@ -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 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 ''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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + +namespace cmdline +{ + namespace detail + { + template + 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 + class lexical_cast_t + { + public: + static Target cast(const Source &arg) + { + return arg; + } + }; + + template + class lexical_cast_t + { + public: + static std::string cast(const Source &arg) + { + std::ostringstream ss; + ss << arg; + return ss.str(); + } + }; + + template + class lexical_cast_t + { + 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 + struct is_same + { + static const bool value = false; + }; + + template + struct is_same + { + static const bool value = true; + }; + + template + Target lexical_cast(const Source &arg) + { + return lexical_cast_t::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 + std::string readable_typename() + { + return demangle(typeid(T).name()); + } + + template + std::string default_value(T def) + { + return detail::lexical_cast(def); + } + + template <> + inline std::string readable_typename() + { + 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 + struct default_reader + { + T operator()(const std::string &str) + { + return detail::lexical_cast(str); + } + }; + + template + 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()(s); + if(!(ret >= low && ret <= high)) + throw cmdline::cmdline_error("range_error"); + return ret; + } + + private: + T low, high; + }; + + template + range_reader range(const T &low, const T &high) + { + return range_reader(low, high); + } + + template + struct oneof_reader + { + T operator()(const std::string &s) + { + T ret = default_reader()(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 alt; + }; + + template + oneof_reader oneof(T a1) + { + oneof_reader ret; + ret.add(a1); + return ret; + } + + template + oneof_reader oneof(T a1, T a2) + { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + return ret; + } + + template + oneof_reader oneof(T a1, T a2, T a3) + { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + ret.add(a3); + return ret; + } + + template + oneof_reader oneof(T a1, T a2, T a3, T a4) + { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + ret.add(a3); + ret.add(a4); + return ret; + } + + template + oneof_reader oneof(T a1, T a2, T a3, T a4, T a5) + { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + ret.add(a3); + ret.add(a4); + ret.add(a5); + return ret; + } + + template + oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6) + { + oneof_reader ret; + ret.add(a1); + ret.add(a2); + ret.add(a3); + ret.add(a4); + ret.add(a5); + ret.add(a6); + return ret; + } + + template + oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7) + { + oneof_reader 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 + oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8) + { + oneof_reader 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 + oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9) + { + oneof_reader 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 + oneof_reader oneof(T a1, T a2, T a3, T a4, T a5, T a6, T a7, T a8, T a9, T a10) + { + oneof_reader 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::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 + 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()); + } + + template + 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(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 + 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 *p = dynamic_cast *>(options.find(name)->second); + if(p == NULL) + throw cmdline_error("type mismatch flag '" + name + "'"); + return p->get(); + } + + const std::vector &rest() const + { + return others; + } + + bool parse(const std::string &arg) + { + std::vector 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 &args) + { + int argc = static_cast(args.size()); + std::vector argv(static_cast(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 lookup; + for(std::map::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::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 &args) + { + if(!options.count("help")) + add("help", '?', "print this message"); + check(static_cast(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 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(); + } + + protected: + std::string full_description(const std::string &description) + { + return description + " (" + detail::readable_typename() + (need ? "" : " [=" + detail::default_value(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 option_with_value_with_reader : public option_with_value + { + 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(name, short_name, need, def, desc), reader(reader) + { + } + + private: + T read(const std::string &s) + { + return reader(s); + } + + F reader; + }; + + std::map options; + std::vector ordered; + std::string ftr; + + std::string prog_name; + std::vector others; + + std::vector errors; + }; + +} // cmdline diff --git a/celero/celero/Console.cpp b/celero/celero/Console.cpp new file mode 100644 index 0000000..085c7c5 --- /dev/null +++ b/celero/celero/Console.cpp @@ -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 + +using namespace celero; + +#ifdef WIN32 +#include +#include +#else +#include +#include +#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; + } +} diff --git a/celero/celero/Console.h b/celero/celero/Console.h new file mode 100644 index 0000000..4af5523 --- /dev/null +++ b/celero/celero/Console.h @@ -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 + +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 diff --git a/celero/celero/Distribution.cpp b/celero/celero/Distribution.cpp new file mode 100644 index 0000000..2d9218b --- /dev/null +++ b/celero/celero/Distribution.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace celero; + +std::vector celero::BuildDistribution(uint64_t numberOfSamples, uint64_t iterationsPerSample) +{ + std::vector 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 series1Normalized(intArgument); + std::vector series2Normalized(intArgument); + std::vector series3Normalized(intArgument); + std::vector 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, 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(val) / static_cast(delta); + } + + return static_cast(maxVal); + } + + return static_cast(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(val * 1024)]++; }); + + std::for_each(std::begin(series2Normalized), std::end(series2Normalized), + [&histograms](const double val) { histograms[1][static_cast(val * 1024)]++; }); + + std::for_each(std::begin(series3Normalized), std::end(series3Normalized), + [&histograms](const double val) { histograms[2][static_cast(val * 1024)]++; }); + + std::for_each(std::begin(series4Normalized), std::end(series4Normalized), + [&histograms](const double val) { histograms[3][static_cast(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(); +} diff --git a/celero/celero/Distribution.h b/celero/celero/Distribution.h new file mode 100644 index 0000000..31e9ad5 --- /dev/null +++ b/celero/celero/Distribution.h @@ -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 +#include +#include + +namespace celero +{ + /// + /// Collects results from Celero for analysis of a hard-coded internal trivial measurement case. + /// + CELERO_EXPORT std::vector 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 diff --git a/celero/celero/Exceptions.cpp b/celero/celero/Exceptions.cpp new file mode 100644 index 0000000..7616b0a --- /dev/null +++ b/celero/celero/Exceptions.cpp @@ -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 + +#include +#include + +#ifdef WIN32 +#include +#endif // WIN32 + +#include +#include + +// +// 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 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 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 diff --git a/celero/celero/Exceptions.h b/celero/celero/Exceptions.h new file mode 100644 index 0000000..7f334d5 --- /dev/null +++ b/celero/celero/Exceptions.h @@ -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 +#include +#include + +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 RunAndCatchSEHExc(TestFixture& test, uint64_t threads, uint64_t calls, + const celero::TestFixture::ExperimentValue& experimentValue); + + /// + /// Run test and catch all exceptions we can + /// + std::pair RunAndCatchExc(TestFixture& test, uint64_t threads, uint64_t calls, + const celero::TestFixture::ExperimentValue& experimentValue); +} // namespace celero + +#endif diff --git a/celero/celero/Executor.cpp b/celero/celero/Executor.cpp new file mode 100644 index 0000000..1794311 --- /dev/null +++ b/celero/celero/Executor.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 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(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(std::max(static_cast(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 r) +{ + // Define a small internal function object to use to uniformly execute the tests. + auto testRunner = [r](const bool record, std::shared_ptr 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(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 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(valueResultScale), i.Iterations); + } + else + { + baselineExperiment->addProblemSpace(i.Value, static_cast(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(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 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 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 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); + } +} diff --git a/celero/celero/Executor.h b/celero/celero/Executor.h new file mode 100644 index 0000000..969bd22 --- /dev/null +++ b/celero/celero/Executor.h @@ -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 +#include +#include +#include + +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 x); + + /// + /// Run all experiments registered within the final application. + /// + void RunAllExperiments(); + + /// + /// Run all experiments within a specific benchmark. + /// + void RunExperiments(std::shared_ptr x); + + /// + /// Run a specific benchmark. + /// + void Run(std::shared_ptr 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 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 diff --git a/celero/celero/Experiment.cpp b/celero/celero/Experiment.cpp new file mode 100644 index 0000000..996b04d --- /dev/null +++ b/celero/celero/Experiment.cpp @@ -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 +#include +#include +#include +#include +#include + +#include +#include + +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 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 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> results; + + /// The owning benchmark object which groups together all experiments. + std::weak_ptr benchmark; + + /// The factory to associate with this benchmark. + std::shared_ptr 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) : pimpl(benchmark) +{ +} + +Experiment::Experiment(std::weak_ptr 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 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 x) +{ + this->pimpl->factory = x; +} + +std::shared_ptr Experiment::getFactory() const +{ + return this->pimpl->factory; +} + +void Experiment::addProblemSpace(int64_t x, double scale, uint64_t iterations) +{ + auto r = std::make_shared(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(this); + defaultResult->setProblemSpaceValue(static_cast(TestFixture::Constants::NoProblemSpaceValue), 1.0, this->getIterations()); + this->pimpl->results.push_back(defaultResult); + } + + return this->pimpl->results.size(); +} + +std::shared_ptr 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 Experiment::getResultByValue(int64_t x) +{ + std::shared_ptr r; + + const auto found = std::find_if(std::begin(this->pimpl->results), std::end(this->pimpl->results), + [x](std::shared_ptr i) -> bool { return (i->getProblemSpaceValue() == x); }); + + if(found != std::end(this->pimpl->results)) + { + r = (*found); + } + + return r; +} diff --git a/celero/celero/Experiment.h b/celero/celero/Experiment.h new file mode 100644 index 0000000..f7202b8 --- /dev/null +++ b/celero/celero/Experiment.h @@ -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 +#include +#include +#include + +namespace celero +{ + class Benchmark; + + /// + /// \class Experiment + /// + /// \author John Farrier + /// + class CELERO_EXPORT Experiment + { + public: + /// + /// + /// + explicit Experiment(std::weak_ptr benchmark); + + /// + /// + /// + explicit Experiment(std::weak_ptr 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 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 x); + + /// + /// Gets the factory used to create this experiment's test fixtures. + /// + std::shared_ptr 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 getResult(size_t x); + + /// + /// Get the ExperimentResult for the given problem space value. + /// + std::shared_ptr 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 pimpl; + }; +} // namespace celero + +#endif diff --git a/celero/celero/ExperimentResult.cpp b/celero/celero/ExperimentResult.cpp new file mode 100644 index 0000000..32cc1e3 --- /dev/null +++ b/celero/celero/ExperimentResult.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 statsTime; + Statistics statsRAM; + + std::shared_ptr 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* 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(this->pimpl->statsTime.getMin()) / static_cast(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 x) +{ + this->pimpl->udmCollector = x; +} + +std::shared_ptr ExperimentResult::getUserDefinedMeasurements() const +{ + return this->pimpl->udmCollector; +} diff --git a/celero/celero/ExperimentResult.h b/celero/celero/ExperimentResult.h new file mode 100644 index 0000000..6486125 --- /dev/null +++ b/celero/celero/ExperimentResult.h @@ -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 +#include +#include +#include + +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* 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 x); + + /// + /// + /// + std::shared_ptr getUserDefinedMeasurements() const; + + private: + /// + /// Disable default constructor + /// + ExperimentResult(); + + /// + /// \brief Pimpl Idiom + /// + class Impl; + + /// + /// \brief Pimpl Idiom + /// + Pimpl pimpl; + }; +} // namespace celero + +#endif diff --git a/celero/celero/Export.h b/celero/celero/Export.h new file mode 100644 index 0000000..532962f --- /dev/null +++ b/celero/celero/Export.h @@ -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 diff --git a/celero/celero/Factory.h b/celero/celero/Factory.h new file mode 100644 index 0000000..d737b05 --- /dev/null +++ b/celero/celero/Factory.h @@ -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 +#include +#include + +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 Create() = 0; + }; +} + +#endif diff --git a/celero/celero/FileReader.h b/celero/celero/FileReader.h new file mode 100644 index 0000000..a24bb2e --- /dev/null +++ b/celero/celero/FileReader.h @@ -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 + +namespace celero +{ + /// + /// \struct FileReader + /// + /// A helper struct to aid in reading CSV files. + /// + /// Classify commas as whitespace. + /// + struct FieldReader : std::ctype + { + FieldReader() : std::ctype(FieldReader::GetTable()) + { + } + + static std::ctype_base::mask const* GetTable() + { + static std::vector 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 diff --git a/celero/celero/GenericFactory.h b/celero/celero/GenericFactory.h new file mode 100644 index 0000000..9aec47e --- /dev/null +++ b/celero/celero/GenericFactory.h @@ -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 +#include + +namespace celero +{ + /// + /// \class GenericFactory + /// + /// \author John farrier + /// + template + 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 Create() + { + return std::make_shared(); + } + }; +} + +#endif diff --git a/celero/celero/JUnit.cpp b/celero/celero/JUnit.cpp new file mode 100644 index 0000000..e89fb20 --- /dev/null +++ b/celero/celero/JUnit.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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>> 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 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 << "" << 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 << "" << std::endl; + + for(auto j : runs) + { + *os << "\tgetFailure() ? 0 : j->getRunTime()) << "\" "; + *os << "name=\"" << j->getExperiment()->getName() << "#" << j->getProblemSpaceValue() << "\""; + + // Compare measured to objective + if(j->getFailure()) + { + // Error + *os << ">" << std::endl; + + *os << "\t\t" << std::endl; + + *os << "\t" << std::endl; + } + else if((j->getExperiment()->getBaselineTarget() > 0.0) && (j->getBaselineMeasurement() > j->getExperiment()->getBaselineTarget())) + { + // Failure + *os << ">" << std::endl; + + *os << "\t\tgetBaselineMeasurement() << " exceeds objective baseline of " + << j->getExperiment()->getBaselineTarget() << "\" "; + *os << "/>" << std::endl; + + *os << "\t" << std::endl; + } + else + { + // Success + *os << "/>" << std::endl; + } + } + + *os << "" << std::endl; + } + + ofs.close(); + } +} diff --git a/celero/celero/JUnit.h b/celero/celero/JUnit.h new file mode 100644 index 0000000..f643161 --- /dev/null +++ b/celero/celero/JUnit.h @@ -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 +#include +#include + +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 x); + + /// + /// Save the JUnit (XUnit) formatted file to the given file name. + /// + void save(); + + private: + /// + /// \brief Pimpl Idiom + /// + class Impl; + + /// + /// \brief Pimpl Idiom + /// + Pimpl pimpl; + }; +} // namespace celero + +#endif diff --git a/celero/celero/Memory.cpp b/celero/celero/Memory.cpp new file mode 100644 index 0000000..5fc3696 --- /dev/null +++ b/celero/celero/Memory.cpp @@ -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 +#include + +#ifdef WIN32 +#include + +#include +#elif defined(__APPLE__) +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#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(memInfo.ullTotalPhys) + static_cast(memInfo.ullTotalVirtual); +#elif defined(__unix__) || defined(__unix) || defined(unix) + // Prefer sysctl() over sysconf() except sysctl() HW_REALMEM and HW_PHYSMEM + // return static_cast(sysconf(_SC_PHYS_PAGES)) * static_cast(sysconf(_SC_PAGE_SIZE)); + struct sysinfo memInfo; + sysinfo(&memInfo); + int64_t total = memInfo.totalram; + total += memInfo.totalswap; + total += memInfo.totalhigh; + return total * static_cast(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(memInfo.ullAvailPhys) + static_cast(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 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(memInfo.mem_unit); +#endif +} + +int64_t celero::GetRAMSystemUsedByCurrentProcess() +{ +#ifdef WIN32 + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast(&pmc), sizeof(pmc)); + return static_cast(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(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(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(memInfo.totalram) - static_cast(memInfo.freeram)) * static_cast(memInfo.mem_unit); +#endif +} + +int64_t celero::GetRAMPhysicalUsedByCurrentProcess() +{ +#ifdef WIN32 + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast(&pmc), sizeof(pmc)); + return static_cast(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(result); +#endif +} + +int64_t celero::GetRAMPhysicalUsedByCurrentProcessPeak() +{ +#if defined(_WIN32) + PROCESS_MEMORY_COUNTERS pmc; + GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc)); + return static_cast(pmc.PeakWorkingSetSize); +#elif defined(__APPLE__) && defined(__MACH__) + struct rusage rusage; + getrusage(RUSAGE_SELF, &rusage); + return static_cast(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(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(memInfo.totalswap) * static_cast(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(memInfo.mem_unit); +#endif +} + +int64_t celero::GetRAMVirtualUsedByCurrentProcess() +{ +#ifdef WIN32 + PROCESS_MEMORY_COUNTERS_EX pmc; + GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast(&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; +} diff --git a/celero/celero/Memory.h b/celero/celero/Memory.h new file mode 100644 index 0000000..ea353db --- /dev/null +++ b/celero/celero/Memory.h @@ -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 +#include +#include + +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 diff --git a/celero/celero/Pimpl.h b/celero/celero/Pimpl.h new file mode 100644 index 0000000..f148df9 --- /dev/null +++ b/celero/celero/Pimpl.h @@ -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 +#include + +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 + class Pimpl + { + public: + Pimpl(); + // template Pimpl( Args&& ... ); + template + Pimpl(Arg1&&); + template + Pimpl(Arg1&&, Arg2&&); + template + Pimpl(Arg1&&, Arg2&&, Arg3&&); + template + Pimpl(Arg1&&, Arg2&&, Arg3&&, Arg4&&); + template + Pimpl(Arg1&&, Arg2&&, Arg3&&, Arg4&&, Arg5&&); + template + Pimpl(Arg1&&, Arg2&&, Arg3&&, Arg4&&, Arg5&&, Arg6&&); + ~Pimpl(); + + T* operator->(); + const T* operator->() const; + T& operator*(); + + private: + std::unique_ptr _pimpl; + }; +} // namespace celero + +#endif diff --git a/celero/celero/PimplImpl.h b/celero/celero/PimplImpl.h new file mode 100644 index 0000000..b3fb405 --- /dev/null +++ b/celero/celero/PimplImpl.h @@ -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 +#include + +namespace celero +{ + template + Pimpl::Pimpl() : _pimpl(new T()) + { + } + + template + template + Pimpl::Pimpl(Arg1&& arg1) : _pimpl(new T(std::forward(arg1))) + { + } + + template + template + Pimpl::Pimpl(Arg1&& arg1, Arg2&& arg2) : _pimpl(new T(std::forward(arg1), std::forward(arg2))) + { + } + + template + template + Pimpl::Pimpl(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3) + : _pimpl(new T(std::forward(arg1), std::forward(arg2), std::forward(arg3))) + { + } + + template + template + Pimpl::Pimpl(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4) + : _pimpl(new T(std::forward(arg1), std::forward(arg2), std::forward(arg3), std::forward(arg4))) + { + } + + template + template + Pimpl::Pimpl(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5) + : _pimpl( + new T(std::forward(arg1), std::forward(arg2), std::forward(arg3), std::forward(arg4), std::forward(arg5))) + { + } + + template + template + Pimpl::Pimpl(Arg1&& arg1, Arg2&& arg2, Arg3&& arg3, Arg4&& arg4, Arg5&& arg5, Arg6&& arg6) + : _pimpl(new T(std::forward(arg1), std::forward(arg2), std::forward(arg3), std::forward(arg4), + std::forward(arg5), std::forward(arg6))) + { + } + + template + Pimpl::~Pimpl() + { + } + + template + T* Pimpl::operator->() + { + return _pimpl.get(); + } + + template + const T* Pimpl::operator->() const + { + return _pimpl.get(); + } + + template + T& Pimpl::operator*() + { + return *_pimpl.get(); + } +} + +#endif diff --git a/celero/celero/Print.cpp b/celero/celero/Print.cpp new file mode 100644 index 0000000..a680556 --- /dev/null +++ b/celero/celero/Print.cpp @@ -0,0 +1,324 @@ +/// +/// \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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace celero; + +enum PrintConstants : size_t +{ + ColumnSeperatorWidth = 3, + DoubleDecimals = 5, + NumberOfColumns = 8, + ColumnWidth = 15 +}; + +/// +/// http://stackoverflow.com/questions/14765155/how-can-i-easily-format-my-data-table-in-c +/// Center-aligns string within a field of width w. Pads with blank spaces to enforce alignment. +/// +std::string PrintCenter(const std::string s, const size_t w = PrintConstants::ColumnWidth) +{ + std::stringstream ss; + std::stringstream spaces; + + // count excess room to pad + auto padding = w - s.size(); + + for(size_t i = 0; i < padding / 2; ++i) + { + spaces << " "; + } + + // format with padding + ss << spaces.str() << s << spaces.str(); + + // if odd #, add 1 space + if((padding > 0) && (padding % 2 != 0)) + { + ss << " "; + } + + ss << " | "; + return ss.str(); +} + +/// +/// http://stackoverflow.com/questions/14765155/how-can-i-easily-format-my-data-table-in-c +/// Convert double to string with specified number of places after the decimal and left padding. +/// +std::string PrintColumn(const double x, const size_t decDigits = PrintConstants::DoubleDecimals, const size_t width = PrintConstants::ColumnWidth) +{ + std::stringstream ss; + ss << std::fixed << std::right; + + // fill space around displayed # + ss.fill(' '); + + // set width around displayed # + ss.width(width); + + // set # places after decimal + ss.precision(decDigits); + ss << x << " | "; + + return ss.str(); +} + +/// +/// http://stackoverflow.com/questions/14765155/how-can-i-easily-format-my-data-table-in-c +/// Convert double to string with specified number of places after the decimal. +/// +std::string PrintColumn(const int64_t x, const size_t width = PrintConstants::ColumnWidth) +{ + std::stringstream ss; + ss << std::fixed; + + // fill space around displayed # + ss.fill(' '); + + // set width around displayed # + ss.width(width); + + ss << x << " | "; + return ss.str(); +} + +/// +/// http://stackoverflow.com/questions/14765155/how-can-i-easily-format-my-data-table-in-c +/// Convert double to string with specified number of places after the decimal. +/// +std::string PrintColumn(const uint64_t x, const size_t width = PrintConstants::ColumnWidth) +{ + std::stringstream ss; + ss << std::fixed; + + // fill space around displayed # + ss.fill(' '); + + // set width around displayed # + ss.width(width); + + ss << x << " | "; + return ss.str(); +} + +/// +/// http://stackoverflow.com/questions/14765155/how-can-i-easily-format-my-data-table-in-c +/// Convert double to string with specified number of places after the decimal. +/// +std::string PrintStrColumnAligned(const std::string& x, const size_t width = PrintConstants::ColumnWidth, bool alignLeft = true) +{ + std::stringstream ss; + ss << std::fixed << (alignLeft ? std::left : std::right); + + // fill space around displayed # + ss.fill(' '); + + // set width around displayed # + ss.width(width); + + if(x.length() > width) + { + // Truncate + std::string xTrunc = x; + xTrunc = xTrunc.substr(0, width); + ss << xTrunc << " | "; + } + else + { + ss << x << " | "; + } + + return ss.str(); +} + +std::string PrintColumn(const std::string& x, const size_t width = PrintConstants::ColumnWidth) +{ + return PrintStrColumnAligned(x, width); +} + +std::string PrintColumnRight(const std::string& x, const size_t width = PrintConstants::ColumnWidth) +{ + return PrintStrColumnAligned(x, width, false); +} + +std::string PrintHRule(const size_t additionalColumns = 0) +{ + std::stringstream ss; + std::string column{":"}; + + while(column.length() < PrintConstants::ColumnWidth) + { + column += "-"; + } + + std::string firstColumn = column + ":|"; + + column += "-:|"; + + ss << "|" << firstColumn; + + for(size_t i = 0; i < PrintConstants::NumberOfColumns + additionalColumns - 1; ++i) + { + ss << column; + } + + ss << std::endl; + + return ss.str(); +} + +namespace celero +{ + void Printer::Console(const std::string& x) + { + std::cout << "Celero: " << x << std::endl; + } + + void Printer::TableBanner() + { + celero::console::SetConsoleColor(celero::console::ConsoleColor_Default); + + std::cout << "|" << PrintCenter("Group") << PrintCenter("Experiment") << PrintCenter("Prob. Space") << PrintCenter("Samples") + << PrintCenter("Iterations") << PrintCenter("Baseline") << PrintCenter("us/Iteration") << PrintCenter("Iterations/sec"); + + for(size_t i = PrintConstants::NumberOfColumns; i < this->columnWidths.size(); ++i) + { + std::cout << PrintCenter(this->userDefinedColumns[i - PrintConstants::NumberOfColumns], this->columnWidths[i]); + } + + std::cout << "\n"; + std::cout << PrintHRule(this->userDefinedColumns.size()); + } + + void Printer::TableRowExperimentHeader(Experiment* x) + { + celero::console::SetConsoleColor(celero::console::ConsoleColor_Default); + std::cout << "|" << PrintColumn(x->getBenchmark()->getName()) << PrintColumn(x->getName()); + } + + void Printer::TableRowFailure(const std::string& msg) + { + std::cout << PrintColumnRight("-") << PrintColumnRight("-") << PrintColumnRight("-"); + + for(size_t i = PrintConstants::NumberOfColumns; i < this->columnWidths.size(); ++i) + { + std::cout << PrintColumnRight("-", this->columnWidths[i]); + } + + celero::console::SetConsoleColor(celero::console::ConsoleColor_Red); + std::cout << msg << std::endl; + celero::console::SetConsoleColor(celero::console::ConsoleColor_Default); + } + + void Printer::TableRowProblemSpaceHeader(std::shared_ptr x) + { + celero::console::SetConsoleColor(celero::console::ConsoleColor_Default); + + if(x->getProblemSpaceValue() == static_cast(TestFixture::Constants::NoProblemSpaceValue)) + { + std::cout << PrintColumnRight("Null"); + } + else + { + std::cout << PrintColumn(x->getProblemSpaceValue()); + } + + std::cout << PrintColumn(x->getExperiment()->getSamples()) << PrintColumn(x->getProblemSpaceIterations()); + } + + void Printer::TableRowHeader(std::shared_ptr x) + { + TableRowExperimentHeader(x->getExperiment()); + TableRowProblemSpaceHeader(x); + } + + void Printer::TableResult(std::shared_ptr x) + { + celero::console::SetConsoleColor(celero::console::ConsoleColor_Default); + + // Slower than Baseline + if(x->getBaselineMeasurement() > 1.0) + { + celero::console::SetConsoleColor(celero::console::ConsoleColor_Yellow); + } + else if(x->getBaselineMeasurement() < 1.0) + { + celero::console::SetConsoleColor(celero::console::ConsoleColor_Green); + } + else + { + celero::console::SetConsoleColor(celero::console::ConsoleColor_Cyan); + } + + std::cout << PrintColumn(x->getBaselineMeasurement()) << PrintColumn(x->getUsPerCall()) << PrintColumn(x->getCallsPerSecond(), 2); + + celero::console::SetConsoleColor(celero::console::ConsoleColor_Default); + + std::unordered_map udmValues; + + auto udmCollector = x->getUserDefinedMeasurements(); + for(const auto& entry : udmCollector->getAggregateValues()) + { + udmValues[entry.first] = entry.second; + } + + for(size_t i = 0; i < this->userDefinedColumns.size(); ++i) + { + const auto& fieldName = this->userDefinedColumns[i]; + + if(udmValues.find(fieldName) == udmValues.end()) + { + std::cout << PrintCenter("---", this->columnWidths[i + PrintConstants::NumberOfColumns]); + } + else + { + std::cout << PrintColumn(udmValues.at(fieldName), 2, this->columnWidths[i + PrintConstants::NumberOfColumns]); + } + } + + std::cout << "\n"; + } + + void Printer::initialize(std::vector userDefinedColumnsIn) + { + this->userDefinedColumns = userDefinedColumnsIn; + + this->columnWidths.clear(); + this->columnWidths.resize(PrintConstants::NumberOfColumns, PrintConstants::ColumnWidth); + + for(const auto& name : this->userDefinedColumns) + { + this->columnWidths.push_back(std::max(name.size() + 2, (size_t)PrintConstants::ColumnWidth)); + } + } + +} // namespace celero diff --git a/celero/celero/Print.h b/celero/celero/Print.h new file mode 100644 index 0000000..ddf178b --- /dev/null +++ b/celero/celero/Print.h @@ -0,0 +1,65 @@ +#ifndef H_CELERO_PRINT_H +#define H_CELERO_PRINT_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 +#include +#include + +namespace celero +{ + /// + /// \class Printer + /// + /// \author John farrier + /// + class Printer + { + public: + /// + /// Singleton implementation. + static Printer& get() + { + static Printer p; + return p; + } + + /// + /// Initialize the Printer object with specific user-defined columns. + /// + void initialize(std::vector userDefinedColumns); + + void Console(const std::string& x); + void TableBanner(); + void TableRowExperimentHeader(Experiment* x); + void TableRowFailure(const std::string& msg); + void TableRowProblemSpaceHeader(std::shared_ptr x); + void TableRowHeader(std::shared_ptr x); + void TableResult(std::shared_ptr x); + + private: + Printer() = default; + + std::vector userDefinedColumns; + std::vector columnWidths; + }; +} // namespace celero + +#endif diff --git a/celero/celero/ResultTable.cpp b/celero/celero/ResultTable.cpp new file mode 100644 index 0000000..b3bd5b2 --- /dev/null +++ b/celero/celero/ResultTable.cpp @@ -0,0 +1,125 @@ +/// +/// \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 +#include +#include + +#include + +#include +#include +#include + +using namespace celero; + +/// +/// \class Impl +/// +class celero::ResultTable::Impl +{ +public: + Impl() : precision(5) + { + } + + ~Impl() + { + closeFile(); + } + + void closeFile() + { + if(this->ofs.is_open() == true) + { + this->ofs.close(); + } + } + + void setFileName(const std::string& x) + { + if(this->ofs.is_open() == true) + { + this->ofs.close(); + } + + this->ofs.open(x); + + // Print the header for the table. + if(this->ofs.is_open() == true) + { + this->ofs << "Group,Experiment,Problem " + "Space,Samples,Iterations,Failure,Baseline,"; + this->ofs << "us/Iteration,Iterations/sec,Min (us),Mean (us),Max " + "(us),Variance,Standard Deviation,Skewness,Kurtosis,Z Score" + << std::endl; + } + } + + std::string format(double x) + { + std::stringstream ss; + ss << std::fixed; + ss.precision(this->precision); + ss << x; + return ss.str(); + } + + std::ofstream ofs; + const size_t precision; +}; + +ResultTable::ResultTable() : pimpl() +{ +} + +ResultTable::~ResultTable() +{ +} + +ResultTable& ResultTable::Instance() +{ + static ResultTable singleton; + return singleton; +} + +void ResultTable::setFileName(const std::string& x) +{ + assert(x.empty() == false); + this->pimpl->setFileName(x); +} + +void ResultTable::closeFile() +{ + this->pimpl->closeFile(); +} + +void ResultTable::add(std::shared_ptr x) +{ + if(this->pimpl->ofs.is_open() == true) + { + this->pimpl->ofs << x->getExperiment()->getBenchmark()->getName() << "," << x->getExperiment()->getName() << "," << x->getProblemSpaceValue() + << "," << x->getExperiment()->getSamples() << "," << x->getProblemSpaceIterations() << "," << x->getFailure() << ","; + + this->pimpl->ofs << x->getBaselineMeasurement() << "," << x->getUsPerCall() << "," << x->getCallsPerSecond() << "," + << x->getTimeStatistics()->getMin() << "," << x->getTimeStatistics()->getMean() << "," << x->getTimeStatistics()->getMax() + << "," << x->getTimeStatistics()->getVariance() << "," << x->getTimeStatistics()->getStandardDeviation() << "," + << x->getTimeStatistics()->getSkewness() << "," << x->getTimeStatistics()->getKurtosis() << "," + << x->getTimeStatistics()->getZScore() << std::endl; + } +} diff --git a/celero/celero/ResultTable.h b/celero/celero/ResultTable.h new file mode 100644 index 0000000..82934e7 --- /dev/null +++ b/celero/celero/ResultTable.h @@ -0,0 +1,87 @@ +#ifndef H_CELERO_RESULTTABLE_H +#define H_CELERO_RESULTTABLE_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 +#include +#include + +namespace celero +{ + /// + /// \class ResultTable + /// + /// \author John Farrier + /// + class ResultTable + { + public: + /// + /// Singleton + /// + static ResultTable& Instance(); + + /// + /// Specify a file name for a results output file. + /// + /// \param x The name of the output file in which to store Celero's results. + /// + void setFileName(const std::string& x); + + /// + /// Force the output file (if any) to close + /// + void closeFile(); + /// + /// Add a new result to the result table. + /// + /// This should re-save on every new result so that the output can be monitored externally. + /// + void add(std::shared_ptr x); + + /// + /// + /// + void save(); + + private: + /// + /// Default Constructor + /// + ResultTable(); + + /// + /// Default Destructor + /// + ~ResultTable(); + + /// + /// \brief Pimpl Idiom + /// + class Impl; + + /// + /// \brief Pimpl Idiom + /// + Pimpl pimpl; + }; +} // namespace celero + +#endif diff --git a/celero/celero/Statistics.h b/celero/celero/Statistics.h new file mode 100644 index 0000000..6c1ccb0 --- /dev/null +++ b/celero/celero/Statistics.h @@ -0,0 +1,252 @@ +#ifndef H_CELERO_STATISTICS_H +#define H_CELERO_STATISTICS_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 +#include +#include +#include + +namespace celero +{ + /// + /// \class Statistics + /// + /// \author John Farrier + /// + /// Sources: + /// http://www.johndcook.com/skewness_kurtosis.html + /// http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance + /// http://prod.sandia.gov/techlib/access-control.cgi/2008/086212.pdf + /// http://en.wikipedia.org/wiki/Kurtosis + /// + template + class Statistics + { + static_assert(std::is_arithmetic::value, "Statistics requres an arithmetic type."); + + public: + /// + /// \brief Default constructor + /// + Statistics() = default; + + /// + /// + /// + Statistics(const Statistics& other) : + sampleSize(other.sampleSize), + M1(other.M1), + M2(other.M2), + M3(other.M3), + M4(other.M4), + min(other.min), + max(other.max) + { + } + + /// + /// + /// + ~Statistics() = default; + + /// + /// + /// + Statistics operator+(const Statistics& other) + { + Statistics combined; + + combined.sampleSize = this->sampleSize + other.sampleSize; + + const auto delta = other.M1 - this->M1; + const auto delta2 = delta * delta; + const auto delta3 = delta * delta2; + const auto delta4 = delta2 * delta2; + + combined.M1 = (this->sampleSize * this->M1 + other.sampleSize * other.M1) / combined.sampleSize; + + combined.M2 = this->M2 + other.M2 + delta2 * this->sampleSize * other.sampleSize / combined.sampleSize; + + combined.M3 = + this->M3 + other.M3 + + delta3 * this->sampleSize * other.sampleSize * (this->sampleSize - other.sampleSize) / (combined.sampleSize * combined.sampleSize); + + combined.M3 += 3.0 * delta * (this->sampleSize * other.M2 - other.sampleSize * this->M2) / combined.sampleSize; + + combined.M4 = this->M4 + other.M4 + + delta4 * this->sampleSize * other.sampleSize + * (this->sampleSize * this->sampleSize - this->sampleSize * other.sampleSize + other.sampleSize * other.sampleSize) + / (combined.sampleSize * combined.sampleSize * combined.sampleSize); + + combined.M4 += 6.0 * delta2 * (this->sampleSize * this->sampleSize * other.M2 + other.sampleSize * other.sampleSize * this->M2) + / (combined.sampleSize * combined.sampleSize) + + 4.0 * delta * (this->sampleSize * other.M3 - other.sampleSize * this->M3) / combined.sampleSize; + + combined.min = std::min(this->min, other.min); + combined.max = std::max(this->max, other.max); + + return combined; + } + + Statistics& operator+=(const Statistics& other) + { + const auto combined = *this + other; + *this = combined; + return *this; + } + + Statistics& operator=(const Statistics& other) + { + this->sampleSize = other.sampleSize; + this->M1 = other.M1; + this->M2 = other.M2; + this->M3 = other.M3; + this->M4 = other.M4; + this->min = other.min; + this->max = other.max; + + return *this; + } + + /// + /// Resets all accumulated statistics. + /// + void reset() + { + this->sampleSize = 0; + this->M1 = 0; + this->M2 = 0; + this->M3 = 0; + this->M4 = 0; + this->min = std::numeric_limitsmin)>::max(); + this->max = std::numeric_limitsmax)>::min(); + } + + /// + /// Adds a statistical sample. + /// + void addSample(T x) + { + const auto n1 = this->sampleSize; + this->sampleSize++; + + const auto delta = x - this->M1; + const auto delta_n = delta / this->sampleSize; + const auto delta_n2 = delta_n * delta_n; + const auto term1 = delta * delta_n * n1; + + this->M1 += delta_n; + this->M4 += term1 * delta_n2 * (this->sampleSize * this->sampleSize - 3 * this->sampleSize + 3) + 6 * delta_n2 * this->M2 + - 4 * delta_n * this->M3; + this->M3 += term1 * delta_n * (this->sampleSize - 2) - 3 * delta_n * this->M2; + this->M2 += term1; + + this->min = std::min(this->min, x); + this->max = std::max(this->max, x); + } + + size_t getSize() const + { + return this->sampleSize; + } + + double getMean() const + { + return this->M1; + } + + double getVariance() const + { + if(this->sampleSize > 1) + { + return this->M2 / (this->sampleSize - 1); + } + + return 0.0; + } + + double getStandardDeviation() const + { + return std::sqrt(this->getVariance()); + } + + double getSkewness() const + { + if(this->sampleSize > 2) + { + return sqrt(this->sampleSize) * this->M3 / pow(this->M2, 1.5); + } + + return 0.0; + } + + double getKurtosis() const + { + if(this->sampleSize > 3) + { + if(this->M2 != 0) + { + return this->sampleSize * this->M4 / (this->M2 * this->M2) - 3.0; + } + } + + return 0.0; + } + + /// + /// Computed as (mean - hypothesis)/standard_deviation + /// + /// Here, the hypothesis is our minimum value. + /// + double getZScore() const + { + const auto sd = this->getStandardDeviation(); + + if(sd != 0.0) + { + return (this->getMean() - static_cast(this->getMin())) / sd; + } + + return 0.0; + } + + T getMin() const + { + return this->min; + } + + T getMax() const + { + return this->max; + } + + private: + size_t sampleSize{0}; + double M1{0.0}; + double M2{0.0}; + double M3{0.0}; + double M4{0.0}; + T min{std::numeric_limits::max()}; + T max{std::numeric_limits::min()}; + }; +} // namespace celero + +#endif diff --git a/celero/celero/TestFixture.cpp b/celero/celero/TestFixture.cpp new file mode 100644 index 0000000..355f804 --- /dev/null +++ b/celero/celero/TestFixture.cpp @@ -0,0 +1,122 @@ +/// +/// \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 +#include +#include +#include +#include + +using namespace celero; + +TestFixture::TestFixture() +{ +} + +TestFixture::~TestFixture() +{ +} + +void TestFixture::onExperimentStart(const celero::TestFixture::ExperimentValue&) +{ +} + +void TestFixture::onExperimentEnd() +{ +} + +void TestFixture::setUp(const celero::TestFixture::ExperimentValue&) +{ +} + +void TestFixture::tearDown() +{ +} + +uint64_t TestFixture::run(const uint64_t, const uint64_t iterations, const celero::TestFixture::ExperimentValue& experimentValue) +{ + // This function constitutes one sample consisting of several iterations for a single experiment value. + + if(this->HardCodedMeasurement() == 0) + { + uint64_t totalTime = 0; + + // Set up the testing fixture. + this->setUp(experimentValue); + + // Run the test body for each iterations. + auto iterationCounter = iterations; + + // Get the starting time. + const auto startTime = celero::timer::GetSystemTime(); + + // Count down to zero + // Iterations are used when the benchmarks are very fast. + // Do not start/stop the timer inside this loop. + // The purpose of the loop is to help counter timer quantization/errors. + while(iterationCounter--) + { + this->onExperimentStart(experimentValue); + + this->UserBenchmark(); + + this->onExperimentEnd(); + } + + // See how long it took. + totalTime += celero::timer::GetSystemTime() - startTime; + + // Tear down the testing fixture. + this->tearDown(); + + // Return the duration in microseconds for the given problem size. + return totalTime; + } + + return this->HardCodedMeasurement(); +} + +void TestFixture::UserBenchmark() +{ +} + +uint64_t TestFixture::HardCodedMeasurement() const +{ + return uint64_t(0); +} + +std::vector> TestFixture::getUserDefinedMeasurements() const +{ + return {}; +} + +std::vector TestFixture::getUserDefinedMeasurementNames() const +{ + std::vector names; + const auto udms = this->getUserDefinedMeasurements(); + + if(udms.empty() == false) + { + for(const auto udm : udms) + { + names.emplace_back(udm->getName()); + } + } + + return names; +} diff --git a/celero/celero/TestFixture.h b/celero/celero/TestFixture.h new file mode 100644 index 0000000..d5f54bd --- /dev/null +++ b/celero/celero/TestFixture.h @@ -0,0 +1,192 @@ +#ifndef H_CELERO_TESTFIXTURE_H +#define H_CELERO_TESTFIXTURE_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 +#include +#include +#include +#include +#include +#include + +// This must be included last. +#include + +namespace celero +{ + class Benchmark; + class UserDefinedMeasurement; + + /// + /// \class TestFixture + /// + /// \author John Farrier + /// + class CELERO_EXPORT TestFixture + { + public: + /// + /// Default Constructor. + /// + TestFixture(); + + /// + /// Virtual destructor for inheritance. + /// + virtual ~TestFixture(); + + enum class Constants : int64_t + { +#ifdef _MSC_VER +#if(_MSC_VER < 1900) + NoProblemSpaceValue = -9223372036854775807 +#else + NoProblemSpaceValue = std::numeric_limits::min() +#endif +#else + NoProblemSpaceValue = std::numeric_limits::min() +#endif + }; + + /// + /// \class ExperimentValue + /// + /// You can derive from this type to add your own information to the experiment value set. + /// + class ExperimentValue + { + public: + ExperimentValue() = default; + ExperimentValue(int64_t v) : Value(v){}; + ExperimentValue(int64_t v, int64_t i) : Value(v), Iterations(i){}; + + virtual ~ExperimentValue() = default; + + /// An arbitrary integer value which can be used or translated for use by the test fixture. + int64_t Value{0}; + + /// The number of iterations to do with this test value. 0 (default) indicates that the default number of iterations set up for the test + /// case should be used. + int64_t Iterations{0}; + }; + + /// + /// Allows a test fixture to supply values to use for experiments. + /// + /// This is used to create multiple runs of the same experiment + /// and varrying the data set size, for example. The second value + /// of the pair is an optional override for the number of iterations + /// to be used. If zero is specified, then the default number of + /// iterations is used. + /// + /// It is only guaranteed that the constructor is called prior to this function being called. + /// + virtual std::vector getExperimentValues() const + { + return std::vector(); + }; + + /// + /// Provide a units result scale of each experiment value. + /// + /// If the value is greater than 0 then additional statistic value will be printed + /// in output - [ xxxx units/sec ]. For example for measure speed of + /// file IO operations method might return 1024 * 1024 to get megabytes + /// per second. + /// + /// It is only guaranteed that the constructor is called prior to this function being called. + /// + virtual double getExperimentValueResultScale() const + { + return 1.0; + }; + + /// + /// Allows the text fixture to run code that will be executed once immediately before the benchmark. + /// + /// Unlike setUp, the evaluation of this function IS included in the total experiment execution + /// time. + /// + /// \param x The value for the experiment. This can be ignored if the test does not utilize experiment values. + /// + virtual void onExperimentStart(const celero::TestFixture::ExperimentValue& x); + + /// + /// Allows the text fixture to run code that will be executed once immediately after the benchmark. + /// Unlike tearDown, the evaluation of this function IS included in the total experiment execution + /// time. + /// + virtual void onExperimentEnd(); + + /// + /// Set up the test fixture before benchmark execution. + /// + /// This code is NOT included in the benchmark timing. + /// It is executed once before all iterations are executed and between each Sample. + /// Your experiment should NOT rely on "setUp" methods to be called before EACH experiment run, only between each sample. + /// + /// \param x The celero::TestFixture::ExperimentValue for the experiment. This can be ignored if the test does not utilize experiment values. + /// + virtual void setUp(const celero::TestFixture::ExperimentValue& x); + + /// + /// Called after test completion to destroy the fixture. + /// + /// This code is NOT included in the benchmark timing. + /// It is executed once after all iterations are executed and between each Sample. + /// Your experiment should NOT rely on "tearDown" methods to be called after EACH experiment run, only between each sample. + /// + virtual void tearDown(); + + /// + /// + /// \param threads The number of working threads. + /// \param iterations The number of times to loop over the UserBenchmark function. + /// \param experimentValue The experiment value to pass in setUp function. + /// + /// \return Returns the number of microseconds the run took. + /// + virtual uint64_t run(uint64_t threads, uint64_t iterations, const celero::TestFixture::ExperimentValue& experimentValue); + + /// + /// \brief If you want to use user-defined measurements, override this method to return them + /// + /// This method must return a vector of pointers, one per type of user-defined measurement that you want to measure. + /// + virtual std::vector> getUserDefinedMeasurements() const; + + /// + /// \brief Returns the names of all user-defined measurements in this fixture. + /// + std::vector getUserDefinedMeasurementNames() const; + + protected: + /// Executed for each operation the benchmarking test is run. + virtual void UserBenchmark(); + + /// + /// Only used for baseline cases. Used to define a hard-coded execution time vs. actually making a measurement. + /// + virtual uint64_t HardCodedMeasurement() const; + }; +} // namespace celero + +#endif diff --git a/celero/celero/TestVector.cpp b/celero/celero/TestVector.cpp new file mode 100644 index 0000000..cfca2b9 --- /dev/null +++ b/celero/celero/TestVector.cpp @@ -0,0 +1,83 @@ +/// +/// \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 +#include +#include +#include +#include +#include + +using namespace celero; + +/// +/// \class Impl +/// +class celero::TestVector::Impl +{ +public: + Impl() : testVectorMutex(), testVector() + { + } + + mutable std::mutex testVectorMutex; + std::vector> testVector; +}; + +TestVector::TestVector() : pimpl() +{ +} + +TestVector& TestVector::Instance() +{ + static TestVector singleton; + return singleton; +} + +void TestVector::push_back(std::shared_ptr x) +{ + std::lock_guard mutexLock(this->pimpl->testVectorMutex); + this->pimpl->testVector.push_back(x); +} + +size_t TestVector::size() const +{ + std::lock_guard mutexLock(this->pimpl->testVectorMutex); + return this->pimpl->testVector.size(); +} + +std::shared_ptr TestVector::operator[](size_t x) +{ + std::lock_guard mutexLock(this->pimpl->testVectorMutex); + return this->pimpl->testVector[x]; +} + +std::shared_ptr TestVector::operator[](const std::string& x) +{ + std::lock_guard mutexLock(this->pimpl->testVectorMutex); + + const auto found = std::find_if(std::begin(this->pimpl->testVector), std::end(this->pimpl->testVector), + [x](std::shared_ptr const& bmark) -> bool { return (bmark->getName() == x); }); + + if(found != std::end(this->pimpl->testVector)) + { + return *found; + } + + return nullptr; +} diff --git a/celero/celero/TestVector.h b/celero/celero/TestVector.h new file mode 100644 index 0000000..cdf2362 --- /dev/null +++ b/celero/celero/TestVector.h @@ -0,0 +1,64 @@ +#ifndef H_CELERO_TESTVECTOR_H +#define H_CELERO_TESTVECTOR_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 +#include +#include +#include + +namespace celero +{ + /// + /// \class TestVector + /// + /// \author John Farrier + /// + class TestVector + { + public: + static TestVector& Instance(); + + void push_back(std::shared_ptr x); + + size_t size() const; + + std::shared_ptr operator[](size_t x); + std::shared_ptr operator[](const std::string& x); + + private: + /// + /// Default Constructor + /// + TestVector(); + + /// + /// \brief Pimpl Idiom + /// + class Impl; + + /// + /// \brief Pimpl Idiom + /// + Pimpl pimpl; + }; +} + +#endif diff --git a/celero/celero/ThreadLocal.h b/celero/celero/ThreadLocal.h new file mode 100644 index 0000000..372d525 --- /dev/null +++ b/celero/celero/ThreadLocal.h @@ -0,0 +1,36 @@ +#ifndef H_CELERO_THREADLOCAL_H +#define H_CELERO_THREADLOCAL_H + +/// +/// \author Ivan Shynkarenka +/// +/// \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. +/// + +#ifndef thread_local + +#if __STDC_VERSION__ >= 201112 && !defined __STDC_NO_THREADS__ +#define thread_local _Thread_local +#elif defined _WIN32 && (defined _MSC_VER || defined __ICL || defined __DMC__ || defined __BORLANDC__) +#define thread_local __declspec(thread) +/* note that ICC (linux) and Clang are covered by __GNUC__ */ +#elif defined __GNUC__ || defined __SUNPRO_C || defined __xlC__ +#define thread_local __thread +#else +#error "Cannot define thread_local" +#endif +#endif + +#endif diff --git a/celero/celero/ThreadTestFixture.cpp b/celero/celero/ThreadTestFixture.cpp new file mode 100644 index 0000000..6b5f1cf --- /dev/null +++ b/celero/celero/ThreadTestFixture.cpp @@ -0,0 +1,124 @@ +/// +/// \author Ivan Shynkarenka +/// +/// \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 + +#include +#include +#include +#include +#include + +using namespace celero; + +class ThreadTestFixture::Impl +{ +public: + static thread_local uint64_t currentCallId; + static thread_local uint64_t currentThreadId; + std::vector> futures; +}; + +thread_local uint64_t ThreadTestFixture::Impl::currentCallId = 0; +thread_local uint64_t ThreadTestFixture::Impl::currentThreadId = 0; + +ThreadTestFixture::ThreadTestFixture() : TestFixture() +{ +} + +ThreadTestFixture::~ThreadTestFixture() +{ +} + +void ThreadTestFixture::startThreads(uint64_t threads, uint64_t iterations) +{ + const uint64_t iterationsPerThread = iterations / threads; + + for(uint64_t i = 0; i < threads; ++i) + { + try + { + this->pimpl->futures.push_back( + // std::async(std::launch::deferred, + std::async(std::launch::async, [this, i, iterationsPerThread]() { + this->pimpl->currentThreadId = i + 1; + for(auto threadIterationCounter = size_t(0); threadIterationCounter < iterationsPerThread;) + { + this->pimpl->currentCallId = ++threadIterationCounter; + this->UserBenchmark(); + } + })); + } + catch(std::system_error& e) + { + std::cerr << "Exception. Error Code: " << e.code() << ", " << e.what() << std::endl; + } + } +} + +void ThreadTestFixture::stopThreads() +{ + // This part will be more effective after + // wait_for_all() will be avaliable for futures! + for(auto& f : this->pimpl->futures) + { + if(f.valid() == true) + { + try + { + f.wait(); + } + catch(std::system_error& e) + { + std::cerr << "Exception. Error Code: " << e.code() << ", " << e.what() << std::endl; + } + } + }; +} + +uint64_t ThreadTestFixture::run(uint64_t threads, uint64_t calls, const celero::TestFixture::ExperimentValue& experimentValue) +{ + if(this->HardCodedMeasurement() == 0) + { + // Set up the testing fixture. + this->setUp(experimentValue); + + // Get the starting time. + const auto startTime = celero::timer::GetSystemTime(); + + this->onExperimentStart(experimentValue); + + // Start working threads. + this->startThreads(threads, calls); + + // Stop working threads. + this->stopThreads(); + + this->onExperimentEnd(); + + const auto endTime = celero::timer::GetSystemTime(); + + // Tear down the testing fixture. + this->tearDown(); + + // Return the duration in microseconds for the given problem size. + return (endTime - startTime); + } + + return this->HardCodedMeasurement(); +} diff --git a/celero/celero/ThreadTestFixture.h b/celero/celero/ThreadTestFixture.h new file mode 100644 index 0000000..424fb58 --- /dev/null +++ b/celero/celero/ThreadTestFixture.h @@ -0,0 +1,75 @@ +#ifndef H_CELERO_THREADTESTFIXTURE_H +#define H_CELERO_THREADTESTFIXTURE_H + +/// +/// \author Ivan Shynkarenka +/// +/// \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 +#include + +namespace celero +{ + class Benchmark; + + /// + /// \class ThreadTestFixture + /// + /// \author Ivan Shynkarenka + /// + class CELERO_EXPORT ThreadTestFixture : public TestFixture + { + public: + /// + /// Default Constructor. + /// + ThreadTestFixture(); + + /// + /// Virtual destructor for inheritance. + /// + virtual ~ThreadTestFixture(); + + /// + /// Start threads before benchmark execution. + /// + /// \param threads Count of working threads to start. + /// \param calls The total number of times to loop over the UserBenchmark function. + /// + virtual void startThreads(uint64_t threads, uint64_t calls); + + /// + /// Called after test completion to stop threads. + /// + virtual void stopThreads(); + + /// + /// \param threads The number of working threads. + /// \param calls The total number of times to loop over the UserBenchmark function. + /// \param experimentValue The experiment value to pass in setUp function. + /// + /// \return Returns the number of microseconds the run took. + /// + uint64_t run(uint64_t threads, uint64_t calls, const celero::TestFixture::ExperimentValue& experimentValue) override; + + private: + class Impl; + Pimpl pimpl; + }; +} // namespace celero + +#endif diff --git a/celero/celero/Timer.cpp b/celero/celero/Timer.cpp new file mode 100644 index 0000000..39f4d7e --- /dev/null +++ b/celero/celero/Timer.cpp @@ -0,0 +1,59 @@ +/// +/// \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 +#include +#include + +#ifdef WIN32 +#include +LARGE_INTEGER QPCFrequency; +#else +#include +#endif + +uint64_t celero::timer::GetSystemTime() +{ +#ifdef WIN32 + LARGE_INTEGER timeStorage; + QueryPerformanceCounter(&timeStorage); + return static_cast(timeStorage.QuadPart * 1000000) / static_cast(QPCFrequency.QuadPart); +#else + auto timePoint = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(timePoint.time_since_epoch()).count(); +#endif +} + +double celero::timer::CachePerformanceFrequency(bool quiet) +{ +#ifdef WIN32 + QueryPerformanceFrequency(&QPCFrequency); + auto precision = ((1.0 / static_cast(QPCFrequency.QuadPart)) * 1000000.0); +#else + auto precision = + (static_cast(std::chrono::high_resolution_clock::period::num) / static_cast(std::chrono::high_resolution_clock::period::den)) + * 1000000.0; +#endif + + if(quiet == false) + { + std::cout << "Timer resolution: " << std::to_string(precision) << " us" << std::endl; + } + + return precision; +} diff --git a/celero/celero/Timer.h b/celero/celero/Timer.h new file mode 100644 index 0000000..66f1b05 --- /dev/null +++ b/celero/celero/Timer.h @@ -0,0 +1,70 @@ +#ifndef H_CELERO_TIMER_H +#define H_CELERO_TIMER_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 +#include + +namespace celero +{ + /// + /// \namespace timer + /// + /// \author John Farrier + /// + /// \brief Provide basic cross-platform timing functions to measure code performance speed. + /// + namespace timer + { + /// + /// \brief Retrieves the current time. + /// + /// \author John Farrier + /// + /// \return The time, in ticks. + /// + uint64_t GetSystemTime(); + + /// + /// \brief Converts the gathered system time into seconds. + /// + /// This assumes "x" is a delta and relatively small (easily fits inside of a double). + /// + /// \author John Farrier + /// + /// \param x The time, in ticks. + /// + /// \return The time, in seconds. + /// + constexpr double ConvertSystemTime(const uint64_t x) + { + return x * celero::UsToSec; + } + + /// + /// On Windows, this caches the frequency of the high performance clock. + /// + /// \return The number of microseconds of precision that we have. + /// + double CachePerformanceFrequency(bool quiet); + } // namespace timer +} // namespace celero + +#endif diff --git a/celero/celero/UserDefinedMeasurement.h b/celero/celero/UserDefinedMeasurement.h new file mode 100644 index 0000000..d2eef73 --- /dev/null +++ b/celero/celero/UserDefinedMeasurement.h @@ -0,0 +1,76 @@ +#ifndef H_CELERO_USERDEFINEDMEASUREMENT_H +#define H_CELERO_USERDEFINEDMEASUREMENT_H + +/// +/// \author Lukas Barth +/// +/// \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 +#include +#include +#include +#include +#include + +namespace celero +{ + class UserDefinedMeasurement; + + /// + /// \brief Describes, which aggregations should be computed on a user-defined measurement. + /// + /// The string names the aggregation, the UDMAggregateFunction is the function that will be called on the collected vector of user-defined + /// measurements. + /// + using UDMAggregationTable = std::vector>>; + + /// + /// \class UserDefinedMeasurement + /// + /// Base class that the user must derive user-defined measurements from. + /// + /// \author Lukas Barth + /// + class CELERO_EXPORT UserDefinedMeasurement + { + public: + /// + /// \brief Must be implemented by the user. Must return a specification which aggregations the user wants to be computed. + /// + virtual UDMAggregationTable getAggregationInfo() const = 0; + + /// + /// \brief Must be implemented by the user. Must return the name of this user-defined measurement. + /// + virtual std::string getName() const = 0; + + /// + /// \brief Combine the results of two user defined measurements. + /// + /// As TestFixture classes are created and destroyed, this provides a mechanisim to preserve data. Internally, this function is used so that + /// each unique set of (group, experiment, problem space) has its own combined set of user defined measurements. + /// + virtual void merge(const UserDefinedMeasurement* const x) = 0; + + protected: + // Class may never be directly instantiated + UserDefinedMeasurement() = default; + }; + +} // namespace celero + +#endif diff --git a/celero/celero/UserDefinedMeasurementCollector.cpp b/celero/celero/UserDefinedMeasurementCollector.cpp new file mode 100644 index 0000000..e189638 --- /dev/null +++ b/celero/celero/UserDefinedMeasurementCollector.cpp @@ -0,0 +1,94 @@ +/// +/// \author Lukas Barth +/// +/// \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 +#include +#include + +using namespace celero; + +UserDefinedMeasurementCollector::UserDefinedMeasurementCollector(std::shared_ptr fixture) +{ + const auto udm = fixture->getUserDefinedMeasurementNames(); + + if(udm.empty() == false) + { + for(auto name : fixture->getUserDefinedMeasurementNames()) + { + this->collected[name] = nullptr; + } + } +} + +void UserDefinedMeasurementCollector::collect(std::shared_ptr fixture) +{ + const auto udms = fixture->getUserDefinedMeasurements(); + + if(udms.empty() == false) + { + for(auto udm : udms) + { + if(this->collected[udm->getName()] == nullptr) + { + this->collected[udm->getName()] = udm; + } + else + { + this->collected[udm->getName()]->merge(&*udm); + } + } + } +} + +std::vector UserDefinedMeasurementCollector::getFields(std::shared_ptr fixture) const +{ + std::vector fields; + const auto udms = fixture->getUserDefinedMeasurements(); + + if(udms.empty() == false) + { + for(auto udm : udms) + { + for(const auto& aggDesc : udm->getAggregationInfo()) + { + fields.emplace_back(std::string(udm->getName()) + std::string(" ") + std::string(aggDesc.first)); + } + } + } + + return fields; +} + +std::vector> UserDefinedMeasurementCollector::getAggregateValues() const +{ + std::vector> aggregates; + + for(const auto& collectedEntry : this->collected) + { + const auto name = collectedEntry.first; + const auto collectedUDMs = collectedEntry.second; + + for(const auto& aggDesc : collectedUDMs->getAggregationInfo()) + { + const auto fieldName = name + std::string(" ") + aggDesc.first; + aggregates.emplace_back(fieldName, (aggDesc.second)()); + } + } + + return aggregates; +} diff --git a/celero/celero/UserDefinedMeasurementCollector.h b/celero/celero/UserDefinedMeasurementCollector.h new file mode 100644 index 0000000..3289301 --- /dev/null +++ b/celero/celero/UserDefinedMeasurementCollector.h @@ -0,0 +1,46 @@ +#ifndef H_CELERO_USERDEFINEDMEASUREMENTCOLLECTOR_H +#define H_CELERO_USERDEFINEDMEASUREMENTCOLLECTOR_H + +/// +/// \author Lukas Barth +/// +/// \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 +#include + +namespace celero +{ + /// + /// \class UserDefinedMeasurementCollector + /// + /// \author Lukas Barth + /// + class UserDefinedMeasurementCollector + { + public: + UserDefinedMeasurementCollector(std::shared_ptr fixture); + + void collect(std::shared_ptr fixture); + std::vector getFields(std::shared_ptr fixture) const; + std::vector> getAggregateValues() const; + + private: + std::unordered_map> collected; + }; +} // namespace celero + +#endif diff --git a/celero/celero/UserDefinedMeasurementTemplate.h b/celero/celero/UserDefinedMeasurementTemplate.h new file mode 100644 index 0000000..26d85a8 --- /dev/null +++ b/celero/celero/UserDefinedMeasurementTemplate.h @@ -0,0 +1,182 @@ +#ifndef H_CELERO_USERDEFINEDMEASUREMENTTEMPLATE_H +#define H_CELERO_USERDEFINEDMEASUREMENTTEMPLATE_H + +/// +/// \author Lukas Barth, John Farrier +/// +/// \copyright Copyright 2015, 2016, 2017, 2018 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 +#include +#include +#include + +namespace celero +{ + /// + /// \class UserDefinedMeasurementTemplate + /// + /// Base class that the user must derive user-defined measurements from. + /// + /// \author Lukas Barth, John Farrier + /// + template + class UserDefinedMeasurementTemplate : public UserDefinedMeasurement + { + static_assert(std::is_arithmetic::value, "UserDefinedMeasurementTemplate requres an arithmetic type."); + + public: + /// + /// Default constructor + /// + UserDefinedMeasurementTemplate() = default; + + /// + /// Default destructor + /// + virtual ~UserDefinedMeasurementTemplate() = default; + + /// + /// \brief Must be implemented by the user. Must return a specification which aggregations the user wants to be computed. + /// + virtual UDMAggregationTable getAggregationInfo() const override + { + UDMAggregationTable table; + + if(this->reportSize() == true) + { + table.push_back({"# Samp", [this]() { return static_cast(this->getStatistics().getSize()); }}); + } + + if(this->reportMean() == true) + { + table.push_back({"Mean", [this]() { return this->getStatistics().getMean(); }}); + } + + if(this->reportVariance() == true) + { + table.push_back({"Var", [this]() { return this->getStatistics().getVariance(); }}); + } + + if(this->reportStandardDeviation() == true) + { + table.push_back({"StdDev", [this]() { return this->getStatistics().getStandardDeviation(); }}); + } + + if(this->reportSkewness() == true) + { + table.push_back({"Skew", [this]() { return this->getStatistics().getSkewness(); }}); + } + + if(this->reportKurtosis() == true) + { + table.push_back({"Kurtosis", [this]() { return this->getStatistics().getKurtosis(); }}); + } + + if(this->reportZScore() == true) + { + table.push_back({"ZScore", [this]() { return this->getStatistics().getZScore(); }}); + } + + if(this->reportMin() == true) + { + table.push_back({"Min", [this]() { return static_cast(this->getStatistics().getMin()); }}); + } + + if(this->reportMax() == true) + { + table.push_back({"Max", [this]() { return static_cast(this->getStatistics().getMax()); }}); + } + + return table; + } + + /// + /// \brief You must call this method from your fixture to add a measurement + /// + void addValue(T x) + { + this->stats.addSample(x); + } + + /// + /// Preserve measurements within a group/experiment/problem space set. + /// + virtual void merge(const UserDefinedMeasurement* const x) override + { + const auto toMerge = dynamic_cast* const>(x); + this->stats += toMerge->stats; + } + + protected: + virtual bool reportSize() const + { + return true; + } + + virtual bool reportMean() const + { + return true; + } + + virtual bool reportVariance() const + { + return true; + } + + virtual bool reportStandardDeviation() const + { + return true; + } + + virtual bool reportSkewness() const + { + return true; + } + + virtual bool reportKurtosis() const + { + return true; + } + + virtual bool reportZScore() const + { + return true; + } + + virtual bool reportMin() const + { + return true; + } + + virtual bool reportMax() const + { + return true; + } + + const Statistics& getStatistics() const + { + return this->stats; + } + + private: + /// Continuously gathers statistics without having to retain data history. + Statistics stats; + }; + +} // namespace celero + +#endif diff --git a/celero/celero/Utilities.cpp b/celero/celero/Utilities.cpp new file mode 100644 index 0000000..f1995be --- /dev/null +++ b/celero/celero/Utilities.cpp @@ -0,0 +1,67 @@ +/// +/// \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 +#include + +#ifdef WIN32 +#include + +#include +#endif + +#ifdef max +#undef max +#endif + +#include +#include + +template <> +void celero::DoNotOptimizeAway(std::function&& x) +{ + x(); + + // We must always do this test, but it will never pass. + static auto ttid = std::this_thread::get_id(); + if(ttid == std::thread::id()) + { + // This forces the value to never be optimized away + // by taking a reference then using it. + const auto* p = &x; + putchar(*reinterpret_cast(p)); + + // If we do get here, kick out because something has gone wrong. + std::abort(); + } +} + +int celero::Random() +{ + // http://en.cppreference.com/w/cpp/numeric/random/uniform_int_distribution + + // Will be used to obtain a seed for the random number engine + static std::random_device rd; + + // Standard mersenne_twister_engine seeded with rd() + static std::mt19937 gen(rd()); + + static std::uniform_int_distribution<> dis(std::numeric_limits::lowest(), std::numeric_limits::max()); + + return dis(gen); +} diff --git a/celero/celero/Utilities.h b/celero/celero/Utilities.h new file mode 100644 index 0000000..58fd139 --- /dev/null +++ b/celero/celero/Utilities.h @@ -0,0 +1,156 @@ +#ifndef H_CELERO_UTILITIES_H +#define H_CELERO_UTILITIES_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. +/// + +#ifndef WIN32 +#include +#endif + +#ifdef __FreeBSD__ +#include +#endif + +#include +#include +#include +#include +#include + +#include + +namespace celero +{ + /// + /// \func DoNotOptimizeAway + /// + /// Used to prevent compiler optimization of a variable + /// that performs no real purpose other than to participate + /// in a benchmark + /// + /// Consider the following trivial benchmark: + /// + /// \code + /// BASELINE(...) + /// { + /// int x = 0; + /// + /// for(int i = 0; i < 64; i++) + /// { + /// x += i; + /// } + /// } + /// \endcode + /// + /// Using Ubuntu clang v3.0, the resultant assembly is highly optimized + /// as one might expect, but not terribly useful for baselining: + /// + /// \verbatim + /// movl $2016, %eax # imm = 0x7E0 + /// ret + /// \endverbatim + /// + /// Now, replace the inner loop with a call to DoNotOptimizeAway: + /// + /// \code + /// DoNotOptimizeAway(x += i); + /// \endcode + /// + /// The result is now a loop which is meaningful for establishing a + /// baseline. + /// + /// \verbatim + /// xorl %ecx, %ecx + /// xorl %eax, %eax + /// .LBB0_1: # =>This Inner Loop Header: Depth=1 + /// addl %ecx, %eax + /// incl %ecx + /// cmpl $64, %ecx + /// jne .LBB0_1 + /// ret + /// \endverbatim + /// + /// GCC 4.8 gives similar results. + /// + /// gcc.godbolt.org permalink: http://goo.gl/lsngwX + /// + /// Folly uses a simple bit of inline assembly: + /// > template + /// > void doNotOptimizeAway(T&& datum) { + /// > asm volatile("" : "+r" (datum)); + /// >} + /// + /// It would be great if that were portable with respect to both compilers and 32/64-bit targets. + /// + template + void DoNotOptimizeAway(T&& x) + { + static auto ttid = std::this_thread::get_id(); + if(ttid == std::thread::id()) + { + // This forces the value to never be optimized away + // by taking a reference then using it. + const auto* p = &x; + putchar(*reinterpret_cast(p)); + + // If we do get here, kick out because something has gone wrong. + std::abort(); + } + } + + /// Specialization for std::function objects which return a value. + template + void DoNotOptimizeAway(std::function&& x) + { + volatile auto foo = x(); + + static auto ttid = std::this_thread::get_id(); + if(ttid == std::thread::id()) + { + // This forces the value to never be optimized away + // by taking a reference then using it. + const auto* p = &foo + &x; + putchar(*reinterpret_cast(p)); + + // If we do get here, kick out because something has gone wrong. + std::abort(); + } + } + + /// Specialization for std::function objects which return void. + template <> + CELERO_EXPORT void DoNotOptimizeAway(std::function&& x); + + /// + /// Quick definition of the number of microseconds per second. + /// + constexpr uint64_t UsPerSec(1000000); + + /// + /// Conversion from Microseconds to Seconds. + /// + constexpr double UsToSec{1.0e-6}; + + /// + /// Drop-in replacement for std::rand(); + /// + CELERO_EXPORT int Random(); +} // namespace celero + +#endif diff --git a/celero/grasp_els.cpp b/celero/grasp_els.cpp new file mode 100644 index 0000000..a657d0b --- /dev/null +++ b/celero/grasp_els.cpp @@ -0,0 +1,214 @@ +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "celero/Celero.h" +#include "inc/udm.h" + +namespace { + +namespace bench { + +constexpr std::size_t SAMPLES = 10; +constexpr std::size_t ITERATIONS = 1; + +tsp::Solution selectMin(tsp::Solution const&a, tsp::Solution const&b) { return a _problem; + std::shared_ptr _getRusageUDM; + + GetRusage _getRusage; + +public: + static constexpr unsigned graspN = 128; + static constexpr unsigned elsIterMax = 5; + static constexpr unsigned elsGen = 10; + + static auto rgreedy() { return RGreedy{2}; } + +public: + GraspElsFixture(): _getRusageUDM{new GetRusageUDM} {} + + void setUp(ExperimentValue const&) override { + tsp::Tsp tspData{"../data/dj38"}; + _problem.reset(new tsp::Problem{tspData.points()}); + + _getRusage.start(bench::ITERATIONS); + } + + void tearDown() override { + _getRusage.stop(); + _getRusageUDM->addValue(_getRusage.get()); + } + + // std::vector> getUserDefinedMeasurements() const override { + // return {_getRusageUDM}; + // } + +public: + tsp::Problem const&problem() const { return *_problem; } + +}; + + +using RNG = std::mt19937; + +auto hwElsGen(tsp::Solution const&solution, RNG &rng) { + return Descent{}(Move2Opt{}(solution, rng)); +} + +auto hwElsInner(tsp::Solution const&solution, RNG &rng) { + tsp::Solution best; + + if(GraspElsFixture::elsGen) + best = hwElsGen(solution, rng); + for(std::size_t i = 1; i < GraspElsFixture::elsGen; ++i) { + tsp::Solution current = hwElsGen(solution, rng); + best = bench::selectMin(std::move(best), std::move(current)); + } + + return best; +} + +auto hwEls(tsp::Solution const&solution, RNG &rng) { + tsp::Solution best = Descent{}(solution); + + for(std::size_t i = 0; i < GraspElsFixture::elsIterMax; ++i) { + tsp::Solution current = hwElsInner(best, rng); + best = bench::selectMin(std::move(best), std::move(current)); + } + + return best; +} + +auto hwGraspGen(tsp::Problem const&problem, RNG &rng) { + return hwEls(GraspElsFixture::rgreedy()(problem, rng), rng); +} + +auto hwGraspEls(tsp::Problem const&problem, RNG &rng) { + tsp::Solution best; + + if(GraspElsFixture::graspN) + best = hwGraspGen(problem, rng); + for(std::size_t i = 1; i < GraspElsFixture::graspN; ++i) { + tsp::Solution current = hwGraspGen(problem, rng); + best = bench::selectMin(std::move(best), std::move(current)); + } + + return best; +} + +template +auto hwGraspElsPar(tsp::Problem const&problem, RNG &rng) { + std::size_t const nThreads = std::min(K, GraspElsFixture::graspN); + std::size_t const step = GraspElsFixture::graspN/nThreads; + std::size_t remain = GraspElsFixture::graspN - step*nThreads; + + tsp::Solution best; + + std::vector threads{nThreads-1}; + std::vector solutions(nThreads); + + for(std::size_t i{}; i < (nThreads-1); ++i) { + std::size_t offset = !!remain; + remain -= offset; + + threads[i] = std::thread{ + [&,i,step=step+offset](auto const&problem, auto rng) { + tsp::Solution &s = solutions[i]; + + for(std::size_t j{}; j < step; ++j) { + tsp::Solution cur = hwGraspGen(problem, rng); + s = bench::selectMin(std::move(s), std::move(cur)); + } + }, + std::cref(problem), rng + }; + } + + { + tsp::Solution &s = solutions[nThreads-1]; + + for(std::size_t j{}; j < step; ++j) { + tsp::Solution cur = hwGraspGen(problem, rng); + s = bench::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; +} + +using ELS = rosa::SkelEls< + tsp::Solution, + Descent, + Move2Opt, Descent, FN(bench::selectMin) +>; + +using GRASPxELS = rosa::SkelGrasp< + tsp::Problem, tsp::Solution, + RGreedy, ELS, + FN(bench::selectMin) +>; + +} + +BASELINE_F(GraspEls_Seq, Handwritten, GraspElsFixture, bench::SAMPLES, bench::ITERATIONS) { + RNG rng; + + celero::DoNotOptimizeAway( + hwGraspEls(problem(), rng) + ); +} + +BENCHMARK_F(GraspEls_Seq, Skeleton, GraspElsFixture, bench::SAMPLES, bench::ITERATIONS) { + auto graspEls = alsk::implement(); + graspEls.skeleton.task.task<0>() = rgreedy(); + graspEls.skeleton.task.task<1>().task<1>().n = elsIterMax; + graspEls.skeleton.task.task<1>().task<1>().task.n = elsGen; + graspEls.skeleton.n = graspN; + + celero::DoNotOptimizeAway( + graspEls(problem()) + ); +} + +/* *** */ + +BASELINE_F(GraspEls_Par, Handwritten, GraspElsFixture, bench::SAMPLES, bench::ITERATIONS) { + RNG rng; + + celero::DoNotOptimizeAway( + hwGraspElsPar<2>(problem(), rng) + ); +} + +BENCHMARK_F(GraspEls_Par, Skeleton, GraspElsFixture, bench::SAMPLES, bench::ITERATIONS) { + auto graspEls = alsk::implement(); + graspEls.executor.cores = 4; + graspEls.skeleton.task.task<0>() = rgreedy(); + graspEls.skeleton.task.task<1>().task<1>().n = elsIterMax; + graspEls.skeleton.task.task<1>().task<1>().task.n = elsGen; + graspEls.skeleton.n = graspN; + + celero::DoNotOptimizeAway( + graspEls(problem()) + ); +} diff --git a/celero/inc/udm.h b/celero/inc/udm.h new file mode 100644 index 0000000..c9d7282 --- /dev/null +++ b/celero/inc/udm.h @@ -0,0 +1,41 @@ +#ifndef BENCH_INC_UDM_H +#define BENCH_INC_UDM_H + +#include +#include + +#include "../celero/Celero.h" + +class GetRusageUDM: public celero::UserDefinedMeasurementTemplate { + std::string getName() const override { return "time"; } + + bool reportSize() const override { return false; } + // bool reportMean() const override { return false; } + bool reportVariance() const override { return false; } + bool reportStandardDeviation() const override { return false; } + bool reportSkewness() const override { return false; } + bool reportKurtosis() const override { return false; } + bool reportZScore() const override { return false; } + bool reportMin() const override { return false; } + bool reportMax() const override { return false; } +}; + +class GetRusage { + int _who; + struct rusage _begin, _end; + int _iterations; + +public: + explicit GetRusage(int who = RUSAGE_SELF): _who{who} {} + void start(int iterations) { _iterations = iterations; getrusage(_who, &_begin); } + void stop() { getrusage(_who, &_end); } + + std::size_t get() { + auto begin = _begin.ru_utime, end = _end.ru_utime; + auto totalUs = (end.tv_sec - begin.tv_sec) * 1e6 + (end.tv_usec - begin.tv_usec); + return totalUs/_iterations; + } +}; + + +#endif diff --git a/celero/main.cpp b/celero/main.cpp new file mode 100644 index 0000000..c732716 --- /dev/null +++ b/celero/main.cpp @@ -0,0 +1,3 @@ +#include "celero/Celero.h" + +CELERO_MAIN diff --git a/data/dj38 b/data/dj38 new file mode 100644 index 0000000..fd4f5a3 --- /dev/null +++ b/data/dj38 @@ -0,0 +1,49 @@ +NAME: dj38 +COMMENT : 38 locations in Djibouti +COMMENT : Derived from National Imagery and Mapping Agency data +COMMENT : This file is a corrected version of dj89, where duplications +COMMENT: have been removed. Thanks to Jay Muthuswamy and others for +COMMENT: requesting data sets without duplications. +TYPE: TSP +DIMENSION: 38 +EDGE_WEIGHT_TYPE: EUC_2D +NODE_COORD_SECTION +1 11003.611100 42102.500000 +2 11108.611100 42373.888900 +3 11133.333300 42885.833300 +4 11155.833300 42712.500000 +5 11183.333300 42933.333300 +6 11297.500000 42853.333300 +7 11310.277800 42929.444400 +8 11416.666700 42983.333300 +9 11423.888900 43000.277800 +10 11438.333300 42057.222200 +11 11461.111100 43252.777800 +12 11485.555600 43187.222200 +13 11503.055600 42855.277800 +14 11511.388900 42106.388900 +15 11522.222200 42841.944400 +16 11569.444400 43136.666700 +17 11583.333300 43150.000000 +18 11595.000000 43148.055600 +19 11600.000000 43150.000000 +20 11690.555600 42686.666700 +21 11715.833300 41836.111100 +22 11751.111100 42814.444400 +23 11770.277800 42651.944400 +24 11785.277800 42884.444400 +25 11822.777800 42673.611100 +26 11846.944400 42660.555600 +27 11963.055600 43290.555600 +28 11973.055600 43026.111100 +29 12058.333300 42195.555600 +30 12149.444400 42477.500000 +31 12286.944400 43355.555600 +32 12300.000000 42433.333300 +33 12355.833300 43156.388900 +34 12363.333300 43189.166700 +35 12372.777800 42711.388900 +36 12386.666700 43334.722200 +37 12421.666700 42895.555600 +38 12645.000000 42973.333300 + diff --git a/data/foo b/data/foo new file mode 100644 index 0000000..639de4b --- /dev/null +++ b/data/foo @@ -0,0 +1,10 @@ +NAME: foo +TYPE: TSP +DIMENSION: 5 +EDGE_WEIGHT_TYPE: EUC_2D +NODE_COORD_SECTION +1 0 0 +2 0 10 +3 10 0 +4 10 10 +5 6 6 diff --git a/data/foo16 b/data/foo16 new file mode 100644 index 0000000..44ce895 --- /dev/null +++ b/data/foo16 @@ -0,0 +1,21 @@ +NAME: foo16 +TYPE: TSP +DIMENSION: 16 +EDGE_WEIGHT_TYPE: EUC_2D +NODE_COORD_SECTION +1 1 1 +2 9 9 +3 1 2 +4 9 8 +5 3 3 +6 5 7 +7 2 1 +8 12 9 +9 1 3 +10 6 11 +11 4 1 +12 11 2 +13 4 6 +14 7 5 +15 12 3 +16 9 5 diff --git a/data/foo32 b/data/foo32 new file mode 100644 index 0000000..c20a1e3 --- /dev/null +++ b/data/foo32 @@ -0,0 +1,37 @@ +NAME: foo32 +TYPE: TSP +DIMENSION: 32 +EDGE_WEIGHT_TYPE: EUC_2D +NODE_COORD_SECTION +1 1 1 +2 9 9 +3 1 2 +4 9 8 +5 3 3 +6 5 7 +7 2 1 +8 12 9 +9 1 3 +10 6 11 +11 4 1 +12 11 2 +13 4 6 +14 7 5 +15 12 3 +16 9 5 +17 8 3 +18 10 6 +19 2 8 +20 8 9 +21 2 4 +22 16 8 +23 13 4 +24 8 15 +25 4 12 +26 7 13 +27 3 8 +28 13 2 +29 8 1 +30 15 3 +31 11 6 +32 12 4 diff --git a/data/gr229 b/data/gr229 new file mode 100644 index 0000000..c8da36e --- /dev/null +++ b/data/gr229 @@ -0,0 +1,236 @@ +NAME: gr229 +TYPE: TSP +COMMENT: Asia/Australia-Subproblem of 666-city TSP (Groetschel) +DIMENSION: 229 +EDGE_WEIGHT_TYPE: GEO +DISPLAY_DATA_TYPE: COORD_DISPLAY +NODE_COORD_SECTION + 1 68.58 33.05 + 2 64.34 40.32 + 3 59.55 30.15 + 4 59.25 24.45 + 5 56.57 24.06 + 6 54.43 20.30 + 7 54.41 25.19 + 8 53.54 27.34 + 9 49.50 24.00 + 10 50.26 30.31 + 11 46.28 30.44 + 12 55.45 37.35 + 13 56.20 44.00 + 14 55.45 49.08 + 15 53.12 50.09 + 16 51.40 39.10 + 17 50.00 36.15 + 18 48.27 34.59 + 19 44.36 33.32 + 20 47.14 39.42 + 21 48.44 44.25 + 22 46.21 48.03 + 23 41.43 44.49 + 24 40.11 44.30 + 25 40.23 49.51 + 26 58.00 56.15 + 27 56.51 60.36 + 28 67.27 63.58 + 29 69.20 88.06 + 30 55.00 73.24 + 31 55.02 82.55 + 32 56.01 92.50 + 33 49.50 73.10 + 34 43.15 76.57 + 35 41.20 69.18 + 36 39.40 66.48 + 37 38.35 68.48 + 38 43.48 87.35 + 39 52.16 104.20 + 40 47.55 106.53 + 41 52.03 113.30 + 42 62.13 129.49 + 43 64.45 177.29 + 44 53.01 158.39 + 45 59.34 150.48 + 46 50.17 127.32 + 47 50.35 137.02 + 48 48.27 135.06 + 49 46.58 142.42 + 50 43.10 131.56 + 51 41.01 28.58 + 52 38.25 27.09 + 53 39.56 32.52 + 54 38.43 35.30 + 55 39.45 37.02 + 56 39.55 41.17 + 57 37.55 40.14 + 58 37.01 35.18 + 59 36.12 37.10 + 60 34.44 36.43 + 61 33.30 36.18 + 62 33.53 35.30 + 63 31.57 35.56 + 64 32.50 35.00 + 65 32.04 34.46 + 66 31.46 35.14 + 67 24.28 39.36 + 68 21.30 39.12 + 69 21.27 39.49 + 70 15.23 44.12 + 71 14.48 42.57 + 72 12.45 45.12 + 73 14.32 49.08 + 74 23.37 58.35 + 75 25.18 55.18 + 76 25.17 51.32 + 77 26.13 50.35 + 78 24.38 46.43 + 79 29.20 47.59 + 80 30.30 47.47 + 81 33.21 44.25 + 82 35.28 44.28 + 83 36.20 43.08 + 84 38.05 46.18 + 85 37.16 49.36 + 86 35.40 51.26 + 87 34.19 47.04 + 88 30.20 48.16 + 89 32.40 51.38 + 90 29.36 52.32 + 91 30.17 57.05 + 92 36.18 59.36 + 93 34.20 62.12 + 94 31.32 65.30 + 95 34.31 69.12 + 96 33.36 73.04 + 97 31.35 74.18 + 98 31.25 73.05 + 99 30.11 71.29 + 100 30.12 67.00 + 101 27.42 68.52 + 102 25.22 68.22 + 103 24.52 67.03 + 104 30.19 78.02 + 105 28.40 77.13 + 106 26.17 73.02 + 107 26.55 75.49 + 108 26.28 80.21 + 109 25.20 83.00 + 110 25.36 85.07 + 111 22.32 88.22 + 112 23.02 72.37 + 113 21.09 79.06 + 114 20.30 85.50 + 115 18.58 72.50 + 116 17.23 78.29 + 117 17.42 83.18 + 118 15.21 75.10 + 119 12.59 77.35 + 120 13.05 80.17 + 121 10.49 78.41 + 122 9.56 78.07 + 123 6.56 79.51 + 124 27.43 85.19 + 125 27.28 89.39 + 126 23.43 90.25 + 127 22.20 91.50 + 128 22.00 96.05 + 129 16.47 96.10 + 130 18.47 98.59 + 131 19.52 102.08 + 132 17.58 102.36 + 133 21.02 105.51 + 134 16.28 107.36 + 135 16.04 108.13 + 136 10.45 106.40 + 137 11.33 104.55 + 138 13.45 100.31 + 139 5.25 100.20 + 140 3.10 101.42 + 141 1.17 103.51 + 142 3.35 98.40 + 143 -0.57 100.21 + 144 -2.55 104.45 + 145 -6.10 106.48 + 146 -6.54 107.36 + 147 -7.48 110.22 + 148 -7.15 112.45 + 149 -8.39 115.13 + 150 -10.10 123.35 + 151 -3.20 114.35 + 152 1.33 110.20 + 153 4.56 114.55 + 154 -0.30 117.09 + 155 -5.07 119.24 + 156 1.29 124.51 + 157 -3.43 128.12 + 158 -5.40 132.45 + 159 7.04 125.36 + 160 10.18 123.54 + 161 10.42 122.34 + 162 14.35 121.00 + 163 22.17 114.09 + 164 22.38 120.17 + 165 25.03 121.30 + 166 29.40 91.09 + 167 36.03 103.41 + 168 34.15 108.52 + 169 30.39 104.04 + 170 29.39 106.34 + 171 25.05 102.40 + 172 23.06 113.16 + 173 26.06 119.17 + 174 30.36 114.17 + 175 32.03 118.47 + 176 31.14 121.28 + 177 34.48 113.39 + 178 36.06 120.19 + 179 37.55 112.30 + 180 39.08 117.12 + 181 39.55 116.25 + 182 38.53 121.35 + 183 41.48 123.27 + 184 45.45 126.41 + 185 39.01 125.45 + 186 37.33 126.58 + 187 35.06 129.03 + 188 43.03 141.21 + 189 39.43 140.07 + 190 38.15 140.53 + 191 35.42 139.46 + 192 35.10 136.55 + 193 36.34 136.39 + 194 35.00 135.45 + 195 34.40 135.30 + 196 34.24 132.27 + 197 32.48 129.55 + 198 31.36 130.33 + 199 26.13 127.40 + 200 13.28 144.47 + 201 -2.32 140.42 + 202 -4.12 152.12 + 203 -9.30 147.10 + 204 -12.28 130.50 + 205 -31.56 115.50 + 206 -34.55 138.35 + 207 -37.49 144.58 + 208 -42.53 147.19 + 209 -33.52 151.13 + 210 -27.28 153.02 + 211 -19.16 146.48 + 212 -23.42 133.53 + 213 -45.52 170.30 + 214 -43.32 172.38 + 215 -41.18 174.47 + 216 -36.52 174.46 + 217 -21.08 -175.12 + 218 -14.16 -170.42 + 219 -18.08 178.25 + 220 -22.16 166.27 + 221 -9.26 159.57 + 222 -0.32 166.55 + 223 11.35 165.23 + 224 21.19 -157.52 + 225 1.52 -157.20 + 226 -9.45 -139.00 + 227 -17.32 -149.34 + 228 -25.04 -130.06 + 229 -27.07 -109.22 diff --git a/data/gr431 b/data/gr431 new file mode 100644 index 0000000..1fa603f --- /dev/null +++ b/data/gr431 @@ -0,0 +1,440 @@ +NAME: gr431 +TYPE: TSP +COMMENT: Europe/Asia/Australia-Subproblem of 666-city TSP (Groetschel) +DIMENSION: 431 +EDGE_WEIGHT_TYPE: GEO +EDGE_WEIGHT_FORMAT: FUNCTION +DISPLAY_DATA_TYPE: COORD_DISPLAY +NODE_COORD_SECTION + 1 37.44 -25.40 + 2 38.43 -9.08 + 3 41.11 -8.36 + 4 37.23 -5.59 + 5 36.32 -6.18 + 6 36.43 -4.25 + 7 37.13 -3.41 + 8 37.53 -4.46 + 9 38.21 -0.29 + 10 39.28 -0.22 + 11 41.23 2.11 + 12 41.38 -0.53 + 13 40.24 -3.41 + 14 41.39 -4.43 + 15 43.15 -2.58 + 16 43.22 -8.23 + 17 38.54 1.26 + 18 39.34 2.39 + 19 42.30 1.31 + 20 44.50 -0.34 + 21 43.36 1.26 + 22 43.18 5.24 + 23 43.42 7.15 + 24 43.42 7.23 + 25 42.42 9.27 + 26 45.50 1.16 + 27 45.26 4.24 + 28 45.45 4.51 + 29 45.10 5.43 + 30 48.24 -4.29 + 31 48.05 -1.41 + 32 47.13 -1.33 + 33 47.23 0.41 + 34 49.30 0.08 + 35 48.52 2.20 + 36 49.15 4.02 + 37 47.19 5.01 + 38 48.41 6.12 + 39 48.35 7.45 + 40 49.36 6.09 + 41 50.38 5.34 + 42 50.50 4.20 + 43 50.38 3.04 + 44 51.03 3.43 + 45 51.13 4.25 + 46 51.26 5.28 + 47 51.55 4.28 + 48 52.22 4.54 + 49 52.05 5.08 + 50 53.13 6.33 + 51 50.23 -4.10 + 52 50.43 -1.54 + 53 50.50 -0.08 + 54 51.29 -3.13 + 55 51.27 -2.35 + 56 51.30 -0.10 + 57 52.30 -1.50 + 58 53.25 -2.55 + 59 53.30 -2.15 + 60 53.23 -1.30 + 61 53.50 -1.35 + 62 54.59 -1.35 + 63 55.57 -3.13 + 64 55.53 -4.15 + 65 56.28 -3.00 + 66 57.10 -2.04 + 67 60.09 -1.09 + 68 62.01 -6.46 + 69 51.54 -8.28 + 70 52.40 -8.38 + 71 53.20 -6.15 + 72 54.35 -5.55 + 73 55.00 -7.19 + 74 64.09 -21.51 + 75 64.11 -51.44 + 76 76.34 -68.47 + 77 70.40 23.42 + 78 68.26 17.25 + 79 65.01 25.28 + 80 61.30 23.45 + 81 60.27 22.17 + 82 60.10 24.58 + 83 63.25 10.25 + 84 60.23 5.20 + 85 58.58 5.45 + 86 59.55 10.45 + 87 57.43 11.58 + 88 55.36 13.00 + 89 58.25 15.37 + 90 59.20 18.03 + 91 57.38 18.18 + 92 56.09 10.13 + 93 55.24 10.23 + 94 55.40 12.35 + 95 53.04 8.49 + 96 53.33 9.59 + 97 54.20 10.08 + 98 54.05 12.07 + 99 51.57 7.37 + 100 52.24 9.44 + 101 52.07 11.38 + 102 52.31 13.24 + 103 50.47 6.05 + 104 50.44 7.05 + 105 50.56 6.59 + 106 51.12 6.47 + 107 51.17 7.17 + 108 51.28 7.01 + 109 51.28 7.13 + 110 51.32 7.13 + 111 51.31 7.28 + 112 51.19 9.29 + 113 50.58 11.01 + 114 51.29 11.58 + 115 51.19 12.20 + 116 50.50 12.55 + 117 51.03 13.44 + 118 49.14 6.59 + 119 50.07 8.40 + 120 49.25 8.43 + 121 49.48 9.56 + 122 49.27 11.04 + 123 49.03 8.24 + 124 48.46 9.11 + 125 49.01 12.06 + 126 48.08 11.34 + 127 46.12 6.09 + 128 46.31 6.38 + 129 46.57 7.26 + 130 47.33 7.35 + 131 47.23 8.32 + 132 47.16 11.24 + 133 47.48 13.02 + 134 48.18 14.18 + 135 48.13 16.20 + 136 47.05 15.27 + 137 45.03 7.40 + 138 45.28 9.12 + 139 45.27 11.00 + 140 45.27 12.21 + 141 45.40 13.46 + 142 44.25 8.57 + 143 44.29 11.20 + 144 43.46 11.15 + 145 43.55 12.28 + 146 39.20 9.00 + 147 41.54 12.29 + 148 40.51 14.17 + 149 41.27 15.34 + 150 41.07 16.52 + 151 40.28 17.15 + 152 38.11 15.33 + 153 37.30 15.06 + 154 38.07 13.21 + 155 35.54 14.31 + 156 53.24 14.32 + 157 54.23 18.40 + 158 53.08 18.00 + 159 52.25 16.55 + 160 51.46 19.30 + 161 52.15 21.00 + 162 53.09 23.09 + 163 51.06 17.00 + 164 50.16 19.00 + 165 50.03 19.58 + 166 51.15 22.35 + 167 49.45 13.23 + 168 50.05 14.26 + 169 49.50 18.17 + 170 49.12 16.37 + 171 48.09 17.07 + 172 48.43 21.15 + 173 47.30 19.05 + 174 47.32 21.38 + 175 46.05 18.13 + 176 46.15 20.09 + 177 45.45 21.13 + 178 46.47 23.36 + 179 47.10 27.35 + 180 45.48 24.09 + 181 45.39 25.37 + 182 44.26 26.06 + 183 44.11 28.39 + 184 46.03 14.31 + 185 45.20 14.27 + 186 45.48 15.58 + 187 43.31 16.27 + 188 43.52 18.25 + 189 44.50 20.30 + 190 42.38 18.07 + 191 41.59 21.26 + 192 41.20 19.50 + 193 42.41 23.19 + 194 42.09 24.45 + 195 43.13 27.55 + 196 42.30 27.28 + 197 39.36 19.56 + 198 40.38 22.56 + 199 38.15 21.44 + 200 37.58 23.43 + 201 35.20 25.09 + 202 35.10 33.22 + 203 68.58 33.05 + 204 64.34 40.32 + 205 59.55 30.15 + 206 59.25 24.45 + 207 56.57 24.06 + 208 54.43 20.30 + 209 54.41 25.19 + 210 53.54 27.34 + 211 49.50 24.00 + 212 50.26 30.31 + 213 46.28 30.44 + 214 55.45 37.35 + 215 56.20 44.00 + 216 55.45 49.08 + 217 53.12 50.09 + 218 51.40 39.10 + 219 50.00 36.15 + 220 48.27 34.59 + 221 44.36 33.32 + 222 47.14 39.42 + 223 48.44 44.25 + 224 46.21 48.03 + 225 41.43 44.49 + 226 40.11 44.30 + 227 40.23 49.51 + 228 58.00 56.15 + 229 56.51 60.36 + 230 67.27 63.58 + 231 69.20 88.06 + 232 55.00 73.24 + 233 55.02 82.55 + 234 56.01 92.50 + 235 49.50 73.10 + 236 43.15 76.57 + 237 41.20 69.18 + 238 39.40 66.48 + 239 38.35 68.48 + 240 43.48 87.35 + 241 52.16 104.20 + 242 47.55 106.53 + 243 52.03 113.30 + 244 62.13 129.49 + 245 64.45 177.29 + 246 53.01 158.39 + 247 59.34 150.48 + 248 50.17 127.32 + 249 50.35 137.02 + 250 48.27 135.06 + 251 46.58 142.42 + 252 43.10 131.56 + 253 41.01 28.58 + 254 38.25 27.09 + 255 39.56 32.52 + 256 38.43 35.30 + 257 39.45 37.02 + 258 39.55 41.17 + 259 37.55 40.14 + 260 37.01 35.18 + 261 36.12 37.10 + 262 34.44 36.43 + 263 33.30 36.18 + 264 33.53 35.30 + 265 31.57 35.56 + 266 32.50 35.00 + 267 32.04 34.46 + 268 31.46 35.14 + 269 24.28 39.36 + 270 21.30 39.12 + 271 21.27 39.49 + 272 15.23 44.12 + 273 14.48 42.57 + 274 12.45 45.12 + 275 14.32 49.08 + 276 23.37 58.35 + 277 25.18 55.18 + 278 25.17 51.32 + 279 26.13 50.35 + 280 24.38 46.43 + 281 29.20 47.59 + 282 30.30 47.47 + 283 33.21 44.25 + 284 35.28 44.28 + 285 36.20 43.08 + 286 38.05 46.18 + 287 37.16 49.36 + 288 35.40 51.26 + 289 34.19 47.04 + 290 30.20 48.16 + 291 32.40 51.38 + 292 29.36 52.32 + 293 30.17 57.05 + 294 36.18 59.36 + 295 34.20 62.12 + 296 31.32 65.30 + 297 34.31 69.12 + 298 33.36 73.04 + 299 31.35 74.18 + 300 31.25 73.05 + 301 30.11 71.29 + 302 30.12 67.00 + 303 27.42 68.52 + 304 25.22 68.22 + 305 24.52 67.03 + 306 30.19 78.02 + 307 28.40 77.13 + 308 26.17 73.02 + 309 26.55 75.49 + 310 26.28 80.21 + 311 25.20 83.00 + 312 25.36 85.07 + 313 22.32 88.22 + 314 23.02 72.37 + 315 21.09 79.06 + 316 20.30 85.50 + 317 18.58 72.50 + 318 17.23 78.29 + 319 17.42 83.18 + 320 15.21 75.10 + 321 12.59 77.35 + 322 13.05 80.17 + 323 10.49 78.41 + 324 9.56 78.07 + 325 6.56 79.51 + 326 27.43 85.19 + 327 27.28 89.39 + 328 23.43 90.25 + 329 22.20 91.50 + 330 22.00 96.05 + 331 16.47 96.10 + 332 18.47 98.59 + 333 19.52 102.08 + 334 17.58 102.36 + 335 21.02 105.51 + 336 16.28 107.36 + 337 16.04 108.13 + 338 10.45 106.40 + 339 11.33 104.55 + 340 13.45 100.31 + 341 5.25 100.20 + 342 3.10 101.42 + 343 1.17 103.51 + 344 3.35 98.40 + 345 -0.57 100.21 + 346 -2.55 104.45 + 347 -6.10 106.48 + 348 -6.54 107.36 + 349 -7.48 110.22 + 350 -7.15 112.45 + 351 -8.39 115.13 + 352 -10.10 123.35 + 353 -3.20 114.35 + 354 1.33 110.20 + 355 4.56 114.55 + 356 -0.30 117.09 + 357 -5.07 119.24 + 358 1.29 124.51 + 359 -3.43 128.12 + 360 -5.40 132.45 + 361 7.04 125.36 + 362 10.18 123.54 + 363 10.42 122.34 + 364 14.35 121.00 + 365 22.17 114.09 + 366 22.38 120.17 + 367 25.03 121.30 + 368 29.40 91.09 + 369 36.03 103.41 + 370 34.15 108.52 + 371 30.39 104.04 + 372 29.39 106.34 + 373 25.05 102.40 + 374 23.06 113.16 + 375 26.06 119.17 + 376 30.36 114.17 + 377 32.03 118.47 + 378 31.14 121.28 + 379 34.48 113.39 + 380 36.06 120.19 + 381 37.55 112.30 + 382 39.08 117.12 + 383 39.55 116.25 + 384 38.53 121.35 + 385 41.48 123.27 + 386 45.45 126.41 + 387 39.01 125.45 + 388 37.33 126.58 + 389 35.06 129.03 + 390 43.03 141.21 + 391 39.43 140.07 + 392 38.15 140.53 + 393 35.42 139.46 + 394 35.10 136.55 + 395 36.34 136.39 + 396 35.00 135.45 + 397 34.40 135.30 + 398 34.24 132.27 + 399 32.48 129.55 + 400 31.36 130.33 + 401 26.13 127.40 + 402 13.28 144.47 + 403 -2.32 140.42 + 404 -4.12 152.12 + 405 -9.30 147.10 + 406 -12.28 130.50 + 407 -31.56 115.50 + 408 -34.55 138.35 + 409 -37.49 144.58 + 410 -42.53 147.19 + 411 -33.52 151.13 + 412 -27.28 153.02 + 413 -19.16 146.48 + 414 -23.42 133.53 + 415 -45.52 170.30 + 416 -43.32 172.38 + 417 -41.18 174.47 + 418 -36.52 174.46 + 419 -21.08 -175.12 + 420 -14.16 -170.42 + 421 -18.08 178.25 + 422 -22.16 166.27 + 423 -9.26 159.57 + 424 -0.32 166.55 + 425 11.35 165.23 + 426 21.19 -157.52 + 427 1.52 -157.20 + 428 -9.45 -139.00 + 429 -17.32 -149.34 + 430 -25.04 -130.06 + 431 -27.07 -109.22 +EOF diff --git a/data/mu1979 b/data/mu1979 new file mode 100644 index 0000000..73af03b --- /dev/null +++ b/data/mu1979 @@ -0,0 +1,1984 @@ +NAME : mu1979 +TYPE : TSP +DIMENSION : 1979 +EDGE_WEIGHT_TYPE : EUC_2D +NODE_COORD_SECTION +1 16700.0000 53233.3333 +2 16700.0000 53250.0000 +3 16716.6667 53300.0000 +4 16733.3333 53333.3333 +5 16750.0000 53416.6667 +6 16816.6667 53316.6667 +7 16883.3333 53783.3333 +8 16934.1667 53992.5000 +9 16983.3333 54016.6667 +10 16983.3333 54116.6667 +11 16988.6111 54692.2222 +12 16999.1667 54053.3333 +13 17000.0000 53983.3333 +14 17003.3333 54037.7778 +15 17009.7222 54163.0556 +16 17016.6667 53000.0000 +17 17016.6667 53683.3333 +18 17016.6667 53983.3333 +19 17017.5000 54082.7778 +20 17033.3333 54450.0000 +21 17035.0000 54209.4444 +22 17036.9444 53982.7778 +23 17037.2222 54403.6111 +24 17039.7222 53966.6667 +25 17040.8333 54431.3889 +26 17044.7222 53972.7778 +27 17050.0000 54133.3333 +28 17050.0000 55066.6667 +29 17055.8333 54227.2222 +30 17059.1667 54596.6667 +31 17060.5556 53974.7222 +32 17061.6667 54231.9444 +33 17066.6667 53950.0000 +34 17068.0556 54540.8333 +35 17071.1111 54629.4444 +36 17071.6667 54465.2778 +37 17075.0000 54042.2222 +38 17077.2222 54619.4444 +39 17082.7778 54436.6667 +40 17083.0556 54031.6667 +41 17083.3333 54376.9444 +42 17083.3333 54466.6667 +43 17086.1111 54508.6111 +44 17087.2222 54581.1111 +45 17088.8889 54385.2778 +46 17089.7222 54064.7222 +47 17090.0000 54604.1667 +48 17091.3889 54123.6111 +49 17091.3889 54383.3333 +50 17094.7222 54510.2778 +51 17095.0000 54385.0000 +52 17095.8333 54396.6667 +53 17098.8889 54327.2222 +54 17100.0000 55116.6667 +55 17102.5000 54593.3333 +56 17103.8889 54029.7222 +57 17107.2222 54321.6667 +58 17107.7778 54263.6111 +59 17107.7778 54490.8333 +60 17109.1667 54426.1111 +61 17111.1111 54455.2778 +62 17111.3889 54407.2222 +63 17113.6111 54027.2222 +64 17116.6667 54000.0000 +65 17118.0556 53981.1111 +66 17118.0556 54009.7222 +67 17118.0556 54555.2778 +68 17118.8889 54015.5556 +69 17119.1667 54333.8889 +70 17120.5556 54435.0000 +71 17122.2222 54481.3889 +72 17123.6111 54256.3889 +73 17123.6111 54540.2778 +74 17123.8889 54400.5556 +75 17124.1667 54435.2778 +76 17125.0000 54430.5556 +77 17126.6667 54002.7778 +78 17128.0556 54509.1667 +79 17128.8889 54456.9444 +80 17129.7222 54385.5556 +81 17130.8333 54101.6667 +82 17131.3889 54541.6667 +83 17131.9444 54021.6667 +84 17131.9444 54277.7778 +85 17132.2222 53994.7222 +86 17132.2222 54005.0000 +87 17132.5000 53972.7778 +88 17133.0556 54158.8889 +89 17133.3333 54016.6667 +90 17133.3333 54100.0000 +91 17133.3333 54116.6667 +92 17133.3333 54450.0000 +93 17133.3333 54516.6667 +94 17133.6111 54353.3333 +95 17134.7222 54276.6667 +96 17136.1111 54091.9444 +97 17136.6667 54383.3333 +98 17138.3333 54077.2222 +99 17138.3333 54085.5556 +100 17139.4444 54399.7222 +101 17140.5556 54066.9444 +102 17140.5556 54082.7778 +103 17141.6667 54044.1667 +104 17141.6667 54162.5000 +105 17142.2222 54446.6667 +106 17142.7778 54133.0556 +107 17144.4444 54347.7778 +108 17144.7222 54580.5556 +109 17145.8333 54539.1667 +110 17146.3889 54057.5000 +111 17147.5000 54195.2778 +112 17148.0556 54179.7222 +113 17148.0556 54431.6667 +114 17150.0000 54316.6667 +115 17150.0000 54416.6667 +116 17151.6667 54199.4444 +117 17152.2222 54021.3889 +118 17153.0556 54380.8333 +119 17153.3333 54021.9444 +120 17153.3333 54303.3333 +121 17153.8889 54312.7778 +122 17155.2778 54381.6667 +123 17158.0556 54043.0556 +124 17158.3333 54049.1667 +125 17159.4444 54082.2222 +126 17165.5556 54262.7778 +127 17165.5556 54436.9444 +128 17165.5556 54543.0556 +129 17167.7778 54084.1667 +130 17169.1667 54216.6667 +131 17170.2778 53993.6111 +132 17170.8333 54246.1111 +133 17170.8333 54261.1111 +134 17173.6111 54083.6111 +135 17177.2222 54133.0556 +136 17177.5000 54370.2778 +137 17178.6111 54446.9444 +138 17179.1667 54383.8889 +139 17179.1667 54415.8333 +140 17180.0000 54365.8333 +141 17180.5556 54087.7778 +142 17183.3333 54109.7222 +143 17183.3333 54250.0000 +144 17183.6111 54390.8333 +145 17183.8889 54414.1667 +146 17185.2778 54272.2222 +147 17189.1667 54133.8889 +148 17189.7222 54159.1667 +149 17189.7222 54185.2778 +150 17190.5556 54365.2778 +151 17190.5556 54533.0556 +152 17192.5000 54318.8889 +153 17192.7778 54406.9444 +154 17194.4444 53993.3333 +155 17195.5556 54135.2778 +156 17197.5000 54376.6667 +157 17198.0556 54211.9444 +158 17198.3333 54141.3889 +159 17199.4444 54088.6111 +160 17200.0000 53983.3333 +161 17200.0000 54466.6667 +162 17216.6667 54033.3333 +163 17216.6667 54050.0000 +164 17228.0556 54603.6111 +165 17233.3333 53966.6667 +166 17233.3333 54000.0000 +167 17233.3333 54033.3333 +168 17235.2778 54048.3333 +169 17250.0000 54250.0000 +170 17254.4444 54496.3889 +171 17283.3333 53966.6667 +172 17333.3333 52750.0000 +173 17333.3333 52783.3333 +174 17350.0000 52816.6667 +175 17366.6667 55283.3333 +176 17400.0000 53900.0000 +177 17400.0000 53916.6667 +178 17450.0000 53700.0000 +179 17450.0000 55250.0000 +180 17466.6667 52833.3333 +181 17466.6667 53516.6667 +182 17483.3333 54550.0000 +183 17566.6667 53716.6667 +184 17583.3333 52550.0000 +185 17583.3333 53683.3333 +186 17670.0000 54033.3333 +187 17716.6667 53066.6667 +188 17733.3333 53550.0000 +189 17766.6667 53533.3333 +190 17816.1111 54284.1667 +191 17816.6667 55666.6667 +192 17850.0000 52983.3333 +193 17883.3333 53050.0000 +194 17916.6667 53516.6667 +195 17933.3333 56333.3333 +196 17950.0000 55683.3333 +197 18000.0000 53966.6667 +198 18083.3333 56483.3333 +199 18116.6667 55633.3333 +200 18116.6667 56533.3333 +201 18133.3333 55266.6667 +202 18200.0000 56550.0000 +203 18300.0000 55966.6667 +204 18316.6667 56600.0000 +205 18350.0000 54700.0000 +206 18350.0000 55166.6667 +207 18366.6667 54933.3333 +208 18433.3333 53133.3333 +209 18466.6667 56450.0000 +210 18483.3333 56050.0000 +211 18600.0000 54883.3333 +212 18650.0000 54083.3333 +213 18650.0000 56533.3333 +214 18966.6667 54783.3333 +215 19000.0000 56916.6667 +216 19100.0000 56966.6667 +217 19500.0000 54866.6667 +218 19650.0000 57700.0000 +219 19800.0000 57716.6667 +220 19833.3333 57750.0000 +221 19933.3333 56316.6667 +222 20033.3333 55600.0000 +223 20050.0000 57733.3333 +224 20183.3333 56033.3333 +225 20250.0000 58733.3333 +226 20300.0000 58633.3333 +227 20316.6667 56166.6667 +228 20350.0000 58683.3333 +229 20366.6667 58783.3333 +230 20400.0000 58816.6667 +231 20405.8333 58788.8889 +232 20416.6667 58733.3333 +233 20416.6667 58750.0000 +234 20421.9444 58816.9444 +235 20433.3333 58766.6667 +236 20461.6667 58781.9444 +237 20466.6667 58050.0000 +238 20466.6667 58783.3333 +239 20466.6667 58900.0000 +240 20477.7778 58898.6111 +241 20483.3333 56250.0000 +242 20509.1667 58785.2778 +243 20516.6667 58950.0000 +244 20521.3889 58944.7222 +245 20533.3333 55933.3333 +246 20546.3889 58936.3889 +247 20548.3333 58807.5000 +248 20550.0000 57516.6667 +249 20559.1667 58931.3889 +250 20560.2778 58840.5556 +251 20566.6667 58816.6667 +252 20590.8333 58904.7222 +253 20599.1667 58854.1667 +254 20600.0000 58850.0000 +255 20616.6667 56616.6667 +256 20616.6667 56933.3333 +257 20616.6667 58200.0000 +258 20658.8889 58870.2778 +259 20666.6667 58883.3333 +260 20683.3333 56500.0000 +261 20783.3333 56150.0000 +262 20848.3333 58733.8889 +263 20883.3333 56116.6667 +264 20883.3333 56516.6667 +265 20950.0000 56766.6667 +266 21016.6667 58016.6667 +267 21066.6667 58350.0000 +268 21100.0000 58883.3333 +269 21250.0000 59050.0000 +270 21283.3333 58233.3333 +271 21400.0000 58400.0000 +272 21400.0000 59283.3333 +273 21500.0000 57333.3333 +274 21583.3333 58616.6667 +275 21600.0000 58650.0000 +276 21700.0000 58633.3333 +277 21850.0000 59566.6667 +278 22005.8333 59354.7222 +279 22014.1667 59661.3889 +280 22016.1111 59324.1667 +281 22021.1111 59445.8333 +282 22027.2222 59445.5556 +283 22032.2222 59490.2778 +284 22036.9444 59477.7778 +285 22050.0000 59633.3333 +286 22069.7222 59471.9444 +287 22081.6667 59279.7222 +288 22083.3333 56000.0000 +289 22085.2778 59685.5556 +290 22109.7222 59382.5000 +291 22110.8333 59609.7222 +292 22118.0556 59403.0556 +293 22119.7222 59715.2778 +294 22126.1111 59405.0000 +295 22128.8889 59721.1111 +296 22129.4444 59624.1667 +297 22140.5556 59161.3889 +298 22163.6111 59235.0000 +299 22166.9444 59653.6111 +300 22168.6111 58039.7222 +301 22179.1667 59764.1667 +302 22192.7778 59221.3889 +303 22219.7222 59202.5000 +304 22226.3889 59799.1667 +305 22243.0556 59215.5556 +306 22261.9444 59693.8889 +307 22293.0556 59820.5556 +308 22299.4444 58070.0000 +309 22370.2778 58059.4444 +310 22374.7222 58011.9444 +311 22382.2222 58212.7778 +312 22383.3333 57533.3333 +313 22393.3333 58853.0556 +314 22398.8889 59324.7222 +315 22399.7222 59248.6111 +316 22407.5000 59325.5556 +317 22410.2778 58026.1111 +318 22416.1111 59178.0556 +319 22420.2778 59151.3889 +320 22421.3889 59252.5000 +321 22427.7778 59361.1111 +322 22428.6111 58017.7778 +323 22428.6111 59410.2778 +324 22430.8333 59385.2778 +325 22433.3333 58783.3333 +326 22435.5556 58800.5556 +327 22437.7778 59828.6111 +328 22446.1111 58813.0556 +329 22447.2222 58019.1667 +330 22448.6111 59828.0556 +331 22449.1667 59374.1667 +332 22450.0000 58800.0000 +333 22451.9444 58794.4444 +334 22466.6667 57966.6667 +335 22470.2778 59106.6667 +336 22473.6111 59763.0556 +337 22473.8889 58017.2222 +338 22478.3333 59126.1111 +339 22491.9444 59718.3333 +340 22496.3889 59396.3889 +341 22498.6111 58028.6111 +342 22500.0000 57083.3333 +343 22500.8333 58743.8889 +344 22502.2222 58114.7222 +345 22505.2778 58021.1111 +346 22509.7222 59122.2222 +347 22513.3333 59341.1111 +348 22516.6667 58150.0000 +349 22519.7222 59410.0000 +350 22522.5000 58158.8889 +351 22523.0556 59788.8889 +352 22525.0000 58123.8889 +353 22528.6111 59528.8889 +354 22538.6111 58118.0556 +355 22539.4444 59381.1111 +356 22545.0000 58176.9444 +357 22545.5556 59506.3889 +358 22546.9444 59128.0556 +359 22549.1667 59521.3889 +360 22549.7222 59120.2778 +361 22550.0000 58666.6667 +362 22550.5556 58686.3889 +363 22550.5556 59328.0556 +364 22552.2222 58004.1667 +365 22557.7778 59617.2222 +366 22558.8889 58027.2222 +367 22558.8889 59108.3333 +368 22559.1667 59477.2222 +369 22563.6111 58192.7778 +370 22566.6667 58700.0000 +371 22566.6667 59100.0000 +372 22566.6667 59528.8889 +373 22566.9444 59542.7778 +374 22567.7778 59111.3889 +375 22568.0556 59290.8333 +376 22568.8889 59522.2222 +377 22569.1667 58120.0000 +378 22572.7778 58192.7778 +379 22577.5000 58023.6111 +380 22579.1667 59500.2778 +381 22580.0000 59511.1111 +382 22580.2778 58130.5556 +383 22583.6111 58683.0556 +384 22588.3333 59286.3889 +385 22590.2778 59090.8333 +386 22598.6111 58826.3889 +387 22599.7222 59268.8889 +388 22602.5000 58182.7778 +389 22603.0556 59085.0000 +390 22603.3333 59465.2778 +391 22611.6667 59468.0556 +392 22612.5000 58668.3333 +393 22614.4444 59092.5000 +394 22615.2778 58137.5000 +395 22615.8333 58040.8333 +396 22616.1111 57998.3333 +397 22616.6667 57300.0000 +398 22617.2222 59295.0000 +399 22626.3889 58148.6111 +400 22626.6667 58006.3889 +401 22631.1111 59437.5000 +402 22633.3333 58533.3333 +403 22633.3333 58550.0000 +404 22633.3333 58666.6667 +405 22635.2778 59307.2222 +406 22640.2778 59290.5556 +407 22642.2222 59287.5000 +408 22645.0000 59252.7778 +409 22646.9444 59261.3889 +410 22647.7778 59272.2222 +411 22650.0000 57333.3333 +412 22650.2778 59347.7778 +413 22656.6667 59215.2778 +414 22660.0000 58042.2222 +415 22666.6667 57283.3333 +416 22666.6667 57350.0000 +417 22666.6667 57983.3333 +418 22666.6667 58033.3333 +419 22676.3889 59310.8333 +420 22683.3333 57283.3333 +421 22683.3333 58516.6667 +422 22683.3333 58533.3333 +423 22690.2778 58546.9444 +424 22700.0000 56833.3333 +425 22700.8333 59370.0000 +426 22705.2778 58528.0556 +427 22712.2222 58509.4444 +428 22714.1667 58171.6667 +429 22716.6667 57233.3333 +430 22716.6667 58516.6667 +431 22720.5556 57996.3889 +432 22724.1667 58651.9444 +433 22728.3333 58531.9444 +434 22731.6667 59162.2222 +435 22733.3333 58650.0000 +436 22733.3333 58683.3333 +437 22750.0000 56966.6667 +438 22750.5556 59175.5556 +439 22755.0000 58654.1667 +440 22759.1667 58651.1111 +441 22764.1667 58501.1111 +442 22766.6667 57483.3333 +443 22766.6667 57516.6667 +444 22766.6667 57633.3333 +445 22766.6667 57666.6667 +446 22766.6667 57750.0000 +447 22766.6667 57850.0000 +448 22766.6667 58016.6667 +449 22766.6667 58666.6667 +450 22770.8333 59213.6111 +451 22776.6667 58012.5000 +452 22783.3333 57433.3333 +453 22783.3333 57600.0000 +454 22783.3333 57666.6667 +455 22783.3333 57800.0000 +456 22783.3333 57816.6667 +457 22783.3333 57850.0000 +458 22784.1667 59138.8889 +459 22785.8333 59139.1667 +460 22785.8333 59228.8889 +461 22792.5000 59121.3889 +462 22792.5000 59230.5556 +463 22797.7778 58127.2222 +464 22798.6111 59232.2222 +465 22799.7222 59176.3889 +466 22800.0000 57583.3333 +467 22800.0000 57716.6667 +468 22800.0000 58000.0000 +469 22801.3889 59243.6111 +470 22803.8889 58025.0000 +471 22806.9444 59174.4444 +472 22808.0556 57997.7778 +473 22816.6667 57533.3333 +474 22816.6667 57750.0000 +475 22816.6667 58150.0000 +476 22817.7778 58155.8333 +477 22822.2222 59261.1111 +478 22827.2222 59256.1111 +479 22828.3333 59258.3333 +480 22830.8333 59225.0000 +481 22830.8333 59254.7222 +482 22831.1111 58990.2778 +483 22831.6667 58168.0556 +484 22833.0556 59233.0556 +485 22839.1667 59248.0556 +486 22841.9444 58168.8889 +487 22850.0000 57466.6667 +488 22850.0000 57966.6667 +489 22861.3889 58057.2222 +490 22866.6667 57516.6667 +491 22876.9444 58206.3889 +492 22880.5556 58775.0000 +493 22883.3333 57533.3333 +494 22883.3333 57750.0000 +495 22883.3333 57933.3333 +496 22883.3333 58216.6667 +497 22886.1111 58013.6111 +498 22889.1667 59184.1667 +499 22894.1667 59165.0000 +500 22896.3889 58420.5556 +501 22898.8889 58023.8889 +502 22899.4444 59085.8333 +503 22900.0000 57250.0000 +504 22900.0000 57766.6667 +505 22900.0000 57916.6667 +506 22900.0000 58016.6667 +507 22906.6667 58883.0556 +508 22911.6667 58046.9444 +509 22911.6667 58880.8333 +510 22912.2222 58078.6111 +511 22916.6667 57250.0000 +512 22916.6667 57316.6667 +513 22916.6667 57716.6667 +514 22918.6111 58078.0556 +515 22921.9444 58073.3333 +516 22923.6111 58054.4444 +517 22926.6667 59211.9444 +518 22927.5000 58078.3333 +519 22933.3333 57266.6667 +520 22933.3333 57533.3333 +521 22933.3333 57600.0000 +522 22933.3333 57666.6667 +523 22933.3333 57766.6667 +524 22933.3333 57783.3333 +525 22938.3333 59133.8889 +526 22945.5556 58020.5556 +527 22950.0000 57266.6667 +528 22950.0000 57283.3333 +529 22950.0000 57533.3333 +530 22950.0000 57650.0000 +531 22950.0000 57666.6667 +532 22950.0000 57900.0000 +533 22950.0000 58816.6667 +534 22961.1111 58277.5000 +535 22966.6667 57300.0000 +536 22966.6667 57550.0000 +537 22966.6667 57683.3333 +538 22966.6667 57916.6667 +539 22966.6667 58300.0000 +540 22966.6667 58550.0000 +541 22966.6667 58566.6667 +542 22966.6667 58800.0000 +543 22969.4444 58295.2778 +544 22975.5556 58255.5556 +545 22978.6111 58270.8333 +546 22978.6111 58521.6667 +547 22981.1111 58535.5556 +548 22983.3333 57133.3333 +549 22983.3333 57150.0000 +550 22983.3333 57583.3333 +551 22983.3333 57666.6667 +552 22983.3333 57783.3333 +553 22983.3333 58550.0000 +554 22983.3333 58566.6667 +555 22983.3333 58783.3333 +556 22985.8333 58293.6111 +557 22986.9444 58472.2222 +558 22987.5000 58510.8333 +559 22992.2222 59138.3333 +560 22993.8889 58022.5000 +561 22995.5556 58473.8889 +562 23000.0000 57316.6667 +563 23003.3333 57776.6667 +564 23006.6667 59009.7222 +565 23011.3889 57701.1111 +566 23011.6667 58224.7222 +567 23016.1111 58022.2222 +568 23016.6667 57166.6667 +569 23016.6667 59116.6667 +570 23016.9444 58147.7778 +571 23019.4444 58740.0000 +572 23020.0000 57536.3889 +573 23020.5556 57522.2222 +574 23021.6667 57993.0556 +575 23022.5000 58211.3889 +576 23024.4444 58743.0556 +577 23025.2778 57667.7778 +578 23026.3889 57982.7778 +579 23028.0556 58011.9444 +580 23030.8333 58583.0556 +581 23032.5000 58991.3889 +582 23033.6111 58278.8889 +583 23040.0000 58933.3333 +584 23040.8333 58701.6667 +585 23041.1111 57142.7778 +586 23041.1111 58544.1667 +587 23041.3889 58197.5000 +588 23042.5000 57448.0556 +589 23042.5000 58314.1667 +590 23042.7778 58091.9444 +591 23043.8889 57670.2778 +592 23044.1667 58940.8333 +593 23045.0000 58982.2222 +594 23045.5556 57961.9444 +595 23045.8333 58654.7222 +596 23046.1111 58514.7222 +597 23046.1111 58608.6111 +598 23046.6667 57988.8889 +599 23046.9444 58661.1111 +600 23047.5000 58688.0556 +601 23048.0556 58623.0556 +602 23049.7222 58928.0556 +603 23050.0000 56983.6111 +604 23050.0000 58983.3333 +605 23050.5556 58623.8889 +606 23051.1111 58085.8333 +607 23052.5000 57410.2778 +608 23052.5000 58615.8333 +609 23053.3333 57658.8889 +610 23054.7222 57956.1111 +611 23055.2778 58980.8333 +612 23056.3889 58133.8889 +613 23059.4444 57130.0000 +614 23060.0000 57701.1111 +615 23060.0000 57954.4444 +616 23060.5556 56982.2222 +617 23060.8333 58911.9444 +618 23061.1111 57000.8333 +619 23061.3889 57572.7778 +620 23062.2222 57569.1667 +621 23062.2222 57666.1111 +622 23062.5000 57948.0556 +623 23063.0556 57264.7222 +624 23063.3333 57680.8333 +625 23063.6111 58070.5556 +626 23065.8333 57659.7222 +627 23065.8333 57950.0000 +628 23066.1111 57663.3333 +629 23066.1111 58985.8333 +630 23066.6667 57034.1667 +631 23066.6667 58616.6667 +632 23067.7778 57695.5556 +633 23068.6111 57640.8333 +634 23070.2778 56965.8333 +635 23070.2778 58614.4444 +636 23071.3889 57601.9444 +637 23071.6667 58610.5556 +638 23071.6667 58992.5000 +639 23071.9444 57131.1111 +640 23072.2222 57666.6667 +641 23072.5000 57662.5000 +642 23073.3333 57925.8333 +643 23073.6111 57538.6111 +644 23074.1667 57550.0000 +645 23074.1667 58866.1111 +646 23076.1111 58570.2778 +647 23076.3889 57535.8333 +648 23077.5000 57709.7222 +649 23077.5000 58871.1111 +650 23077.7778 57944.7222 +651 23079.7222 57106.3889 +652 23079.7222 59054.7222 +653 23081.9444 58725.2778 +654 23083.3333 56816.6667 +655 23083.3333 57280.8333 +656 23083.3333 57300.0000 +657 23083.3333 57516.6667 +658 23083.3333 57933.3333 +659 23083.6111 57611.1111 +660 23083.6111 58866.6667 +661 23084.1667 58153.8889 +662 23084.4444 58567.7778 +663 23085.5556 57739.4444 +664 23086.3889 57298.0556 +665 23086.9444 57555.5556 +666 23087.2222 57517.7778 +667 23087.2222 58861.3889 +668 23088.0556 58853.0556 +669 23088.8889 58040.8333 +670 23089.4444 57735.0000 +671 23090.5556 57521.9444 +672 23090.8333 58104.7222 +673 23091.1111 57724.4444 +674 23091.6667 58236.6667 +675 23091.6667 58862.5000 +676 23092.7778 57730.5556 +677 23093.8889 57430.5556 +678 23094.1667 57756.3889 +679 23095.2778 57564.4444 +680 23096.1111 57758.6111 +681 23098.0556 57553.3333 +682 23098.3333 58036.9444 +683 23098.3333 58573.6111 +684 23100.0000 57050.0000 +685 23100.0000 57161.6667 +686 23100.0000 57833.3333 +687 23101.6667 57910.0000 +688 23101.6667 58454.4444 +689 23101.9444 57310.8333 +690 23102.5000 57556.9444 +691 23103.3333 58567.7778 +692 23104.1667 58580.5556 +693 23104.4444 57835.2778 +694 23104.7222 57558.8889 +695 23105.2778 57069.1667 +696 23105.2778 58847.7778 +697 23105.8333 58306.9444 +698 23107.2222 57819.7222 +699 23107.2222 58353.3333 +700 23107.2222 58561.1111 +701 23107.5000 57563.6111 +702 23107.7778 57559.1667 +703 23107.7778 58305.2778 +704 23108.0556 57836.3889 +705 23112.5000 57541.1111 +706 23112.5000 58516.1111 +707 23113.6111 56933.3333 +708 23114.4444 57577.5000 +709 23114.4444 58476.3889 +710 23115.5556 58513.0556 +711 23116.6667 57650.0000 +712 23116.6667 57916.6667 +713 23118.0556 57449.7222 +714 23118.0556 57836.6667 +715 23118.0556 58541.6667 +716 23118.8889 57577.5000 +717 23119.1667 58318.0556 +718 23119.1667 58532.7778 +719 23119.7222 58216.6667 +720 23120.5556 57283.0556 +721 23121.1111 58361.3889 +722 23123.6111 58463.3333 +723 23125.2778 58314.1667 +724 23125.5556 57568.0556 +725 23126.3889 57520.5556 +726 23126.3889 57753.8889 +727 23130.2778 57083.6111 +728 23132.5000 57560.2778 +729 23133.6111 57672.7778 +730 23135.0000 58396.9444 +731 23135.8333 56894.7222 +732 23137.2222 58401.3889 +733 23137.7778 56924.4444 +734 23137.7778 57085.5556 +735 23138.6111 57734.7222 +736 23139.1667 57312.2222 +737 23139.7222 57843.6111 +738 23140.0000 57741.9444 +739 23140.2778 58413.8889 +740 23140.5556 57471.9444 +741 23140.8333 57666.1111 +742 23141.1111 57755.8333 +743 23142.2222 57447.2222 +744 23142.7778 57734.7222 +745 23143.3333 57564.4444 +746 23143.8889 56451.9444 +747 23144.7222 56466.9444 +748 23145.2778 57673.8889 +749 23146.1111 57851.3889 +750 23147.2222 57517.5000 +751 23147.2222 57602.5000 +752 23148.3333 57559.1667 +753 23148.6111 57206.6667 +754 23148.6111 58830.2778 +755 23149.7222 57529.4444 +756 23150.0000 57000.0000 +757 23150.0000 57816.6667 +758 23151.3889 57341.1111 +759 23152.2222 57776.9444 +760 23152.7778 58314.4444 +761 23154.7222 57738.0556 +762 23156.1111 57540.0000 +763 23156.9444 57741.3889 +764 23157.2222 57347.7778 +765 23158.0556 57298.8889 +766 23158.0556 57301.9444 +767 23158.3333 57339.1667 +768 23158.3333 58854.1667 +769 23158.6111 57545.5556 +770 23161.6667 57858.0556 +771 23165.2778 57483.6111 +772 23165.5556 57926.9444 +773 23166.6667 57683.3333 +774 23166.9444 57849.1667 +775 23167.7778 56942.7778 +776 23168.3333 56909.1667 +777 23169.4444 58922.2222 +778 23170.2778 57152.2222 +779 23172.5000 57670.5556 +780 23173.0556 57430.5556 +781 23173.6111 57318.3333 +782 23174.1667 57867.7778 +783 23175.0000 57200.8333 +784 23175.2778 58814.1667 +785 23176.9444 57911.1111 +786 23177.2222 58986.9444 +787 23178.6111 57318.0556 +788 23178.6111 57866.1111 +789 23179.1667 56480.5556 +790 23180.8333 57654.4444 +791 23181.3889 57296.6667 +792 23181.3889 58318.8889 +793 23181.9444 57607.2222 +794 23182.7778 58989.7222 +795 23183.3333 57016.6667 +796 23183.3333 59000.0000 +797 23183.8889 58598.6111 +798 23184.1667 57490.8333 +799 23185.0000 57613.0556 +800 23185.0000 57618.6111 +801 23185.2778 57411.6667 +802 23185.5556 57626.1111 +803 23185.5556 57636.6667 +804 23185.8333 57018.3333 +805 23187.2222 58982.7778 +806 23187.5000 57317.2222 +807 23187.5000 57563.8889 +808 23188.0556 57574.1667 +809 23188.6111 57386.3889 +810 23188.8889 57631.6667 +811 23189.1667 57610.0000 +812 23191.3889 56435.8333 +813 23191.3889 57211.6667 +814 23191.9444 57557.5000 +815 23194.1667 58606.1111 +816 23194.7222 57371.1111 +817 23195.5556 57557.5000 +818 23196.9444 58989.1667 +819 23198.3333 57206.6667 +820 23198.6111 57373.0556 +821 23199.4444 57721.3889 +822 23200.0000 57679.4444 +823 23201.3889 58953.3333 +824 23201.6667 58986.1111 +825 23202.7778 57661.9444 +826 23203.8889 57562.2222 +827 23203.8889 58585.2778 +828 23204.1667 57511.3889 +829 23204.4444 57525.5556 +830 23204.7222 56524.7222 +831 23205.5556 58965.5556 +832 23206.1111 57562.5000 +833 23207.7778 58685.2778 +834 23208.0556 57381.3889 +835 23208.3333 56959.7222 +836 23209.4444 58977.5000 +837 23210.0000 57338.6111 +838 23210.0000 58083.0556 +839 23210.2778 57555.2778 +840 23210.8333 57662.2222 +841 23211.1111 57455.2778 +842 23211.3889 57673.0556 +843 23212.7778 56429.7222 +844 23213.6111 57459.4444 +845 23214.7222 57013.6111 +846 23215.0000 57872.5000 +847 23216.1111 57148.0556 +848 23216.6667 58566.6667 +849 23216.9444 57402.5000 +850 23216.9444 57505.2778 +851 23217.2222 58077.2222 +852 23219.1667 57441.3889 +853 23219.4444 58138.0556 +854 23219.4444 58914.7222 +855 23219.7222 57314.4444 +856 23220.0000 57332.7778 +857 23222.5000 56461.6667 +858 23222.7778 57302.7778 +859 23223.3333 57333.8889 +860 23223.6111 57323.0556 +861 23224.1667 57037.5000 +862 23224.7222 56473.6111 +863 23224.7222 57322.2222 +864 23226.1111 58676.1111 +865 23226.9444 57645.0000 +866 23227.2222 57329.4444 +867 23227.2222 58080.0000 +868 23228.0556 57559.4444 +869 23229.4444 56458.3333 +870 23229.4444 57061.3889 +871 23229.7222 57046.6667 +872 23229.7222 58070.2778 +873 23232.2222 57286.9444 +874 23232.7778 58569.4444 +875 23233.3333 58233.3333 +876 23233.6111 58079.7222 +877 23233.8889 57129.7222 +878 23234.1667 57039.4444 +879 23235.5556 58077.5000 +880 23236.1111 58707.2222 +881 23236.3889 56504.4444 +882 23236.6667 58896.9444 +883 23236.9444 58118.3333 +884 23237.7778 58722.5000 +885 23239.1667 57535.2778 +886 23239.4444 58910.8333 +887 23239.7222 58234.1667 +888 23240.2778 58901.3889 +889 23240.5556 57195.0000 +890 23242.2222 57301.6667 +891 23242.7778 56516.6667 +892 23243.8889 58764.7222 +893 23244.4444 58531.9444 +894 23245.8333 58557.2222 +895 23246.3889 58917.2222 +896 23246.6667 57373.6111 +897 23247.5000 57317.2222 +898 23250.0000 58583.3333 +899 23250.2778 57286.1111 +900 23251.1111 58090.2778 +901 23251.1111 58101.1111 +902 23252.5000 57045.2778 +903 23252.7778 56746.3889 +904 23252.7778 57340.5556 +905 23255.0000 58787.5000 +906 23256.3889 56508.6111 +907 23256.3889 57299.7222 +908 23258.3333 58720.2778 +909 23259.4444 58884.7222 +910 23260.0000 58782.2222 +911 23261.1111 57282.2222 +912 23261.9444 58230.5556 +913 23261.9444 58591.1111 +914 23262.7778 58916.1111 +915 23263.8889 58903.0556 +916 23264.7222 58626.3889 +917 23265.2778 57210.2778 +918 23265.8333 58923.0556 +919 23266.3889 57267.7778 +920 23266.9444 57278.3333 +921 23267.2222 57397.5000 +922 23268.8889 57700.5556 +923 23271.3889 56527.5000 +924 23271.3889 57328.8889 +925 23271.3889 58751.6667 +926 23271.9444 58907.7778 +927 23272.5000 57456.6667 +928 23278.8889 58752.5000 +929 23279.1667 56698.8889 +930 23279.7222 57922.2222 +931 23280.2778 56536.3889 +932 23281.9444 56922.7778 +933 23281.9444 57464.4444 +934 23283.3333 57833.3333 +935 23283.6111 56893.6111 +936 23284.1667 58040.8333 +937 23284.7222 57469.1667 +938 23287.7778 57830.2778 +939 23287.7778 57920.0000 +940 23287.7778 58604.4444 +941 23288.0556 57970.5556 +942 23288.8889 56547.2222 +943 23288.8889 57328.8889 +944 23291.3889 56658.8889 +945 23292.2222 57975.5556 +946 23293.6111 57979.1667 +947 23293.6111 58530.2778 +948 23294.7222 57008.3333 +949 23296.3889 58225.2778 +950 23296.6667 57975.2778 +951 23297.2222 57981.6667 +952 23297.7778 56618.6111 +953 23298.0556 57977.7778 +954 23298.6111 57168.6111 +955 23298.6111 58061.6667 +956 23298.8889 57534.7222 +957 23299.7222 57529.4444 +958 23299.7222 58598.8889 +959 23300.0000 57983.3333 +960 23300.0000 58216.6667 +961 23300.5556 57545.2778 +962 23301.1111 57980.0000 +963 23301.3889 58032.2222 +964 23301.6667 57986.3889 +965 23302.7778 57550.0000 +966 23302.7778 58042.7778 +967 23303.8889 57981.6667 +968 23305.5556 58029.7222 +969 23305.8333 57987.5000 +970 23306.1111 57531.1111 +971 23307.2222 58000.2778 +972 23307.7778 58060.8333 +973 23308.8889 57992.5000 +974 23309.4444 58014.4444 +975 23310.0000 56627.7778 +976 23310.0000 57321.9444 +977 23310.5556 58023.0556 +978 23310.8333 58018.3333 +979 23311.3889 58041.6667 +980 23311.6667 57478.8889 +981 23311.6667 57994.4444 +982 23311.9444 56614.7222 +983 23312.5000 57230.8333 +984 23312.5000 58006.6667 +985 23312.7778 57031.1111 +986 23312.7778 58004.4444 +987 23313.8889 58011.3889 +988 23314.1667 58006.1111 +989 23315.8333 58008.6111 +990 23316.1111 58074.4444 +991 23316.6667 58016.6667 +992 23316.6667 58733.3333 +993 23317.5000 56316.1111 +994 23318.6111 57576.6667 +995 23318.6111 58019.1667 +996 23321.9444 57028.0556 +997 23322.2222 58741.3889 +998 23323.8889 58225.8333 +999 23324.4444 57939.4444 +1000 23325.8333 56386.6667 +1001 23327.2222 57950.5556 +1002 23328.3333 58595.5556 +1003 23330.5556 58122.5000 +1004 23330.5556 58581.6667 +1005 23331.6667 58226.1111 +1006 23332.2222 57028.3333 +1007 23333.8889 58578.0556 +1008 23341.9444 57311.3889 +1009 23341.9444 58581.1111 +1010 23344.1667 58093.0556 +1011 23344.1667 58551.6667 +1012 23350.0000 57026.6667 +1013 23351.6667 58016.9444 +1014 23352.5000 58722.2222 +1015 23354.7222 56354.7222 +1016 23355.0000 57632.5000 +1017 23355.8333 58038.3333 +1018 23356.1111 58047.7778 +1019 23359.1667 58579.1667 +1020 23360.5556 57653.3333 +1021 23366.1111 58532.7778 +1022 23366.6667 58050.0000 +1023 23368.0556 57308.6111 +1024 23370.0000 58109.4444 +1025 23371.3889 58640.5556 +1026 23372.5000 56949.7222 +1027 23373.0556 57972.7778 +1028 23373.6111 58143.3333 +1029 23374.4444 56961.9444 +1030 23374.4444 58088.8889 +1031 23375.8333 58515.0000 +1032 23376.6667 57822.5000 +1033 23378.3333 57188.3333 +1034 23378.8889 56877.5000 +1035 23380.0000 57662.7778 +1036 23381.9444 57013.8889 +1037 23381.9444 57660.8333 +1038 23383.3333 56918.8889 +1039 23383.3333 57210.8333 +1040 23384.7222 56870.5556 +1041 23385.2778 57021.3889 +1042 23386.3889 57422.2222 +1043 23386.9444 58100.2778 +1044 23388.0556 57035.8333 +1045 23388.6111 56914.4444 +1046 23389.1667 57837.2222 +1047 23389.4444 57662.7778 +1048 23389.7222 57313.8889 +1049 23390.8333 57216.6667 +1050 23390.8333 57424.4444 +1051 23391.1111 57431.3889 +1052 23392.2222 58752.7778 +1053 23392.7778 56985.2778 +1054 23392.7778 57435.0000 +1055 23393.3333 57032.5000 +1056 23394.4444 57831.3889 +1057 23395.8333 56984.4444 +1058 23396.3889 57767.2222 +1059 23397.2222 57243.3333 +1060 23397.5000 57216.6667 +1061 23398.0556 58522.7778 +1062 23398.6111 58137.7778 +1063 23399.4444 56997.5000 +1064 23399.4444 57663.0556 +1065 23399.7222 58516.3889 +1066 23400.0000 56860.0000 +1067 23400.2778 57416.9444 +1068 23400.8333 57178.6111 +1069 23401.9444 56283.0556 +1070 23403.3333 56887.2222 +1071 23406.3889 58126.3889 +1072 23406.6667 57170.8333 +1073 23409.7222 57115.8333 +1074 23412.2222 58090.0000 +1075 23412.5000 57305.2778 +1076 23412.5000 58131.1111 +1077 23416.3889 57426.6667 +1078 23416.3889 58135.5556 +1079 23416.6667 56783.3333 +1080 23416.6667 58100.0000 +1081 23417.2222 56277.7778 +1082 23417.5000 57429.7222 +1083 23417.5000 58146.9444 +1084 23418.6111 57347.5000 +1085 23418.8889 57811.6667 +1086 23419.4444 57130.5556 +1087 23421.1111 56724.1667 +1088 23421.3889 57151.1111 +1089 23422.2222 56246.3889 +1090 23423.3333 56762.5000 +1091 23424.7222 57163.6111 +1092 23425.0000 57432.7778 +1093 23425.5556 58095.0000 +1094 23426.6667 57123.8889 +1095 23429.7222 57302.5000 +1096 23430.0000 58131.1111 +1097 23431.3889 56626.6667 +1098 23431.3889 58552.2222 +1099 23431.6667 57440.5556 +1100 23432.5000 57635.0000 +1101 23433.0556 57318.6111 +1102 23433.3333 56633.3333 +1103 23433.8889 58117.7778 +1104 23435.2778 56741.1111 +1105 23435.2778 57046.9444 +1106 23435.2778 58562.5000 +1107 23435.5556 56234.7222 +1108 23435.5556 58781.1111 +1109 23438.0556 57671.1111 +1110 23439.7222 58208.3333 +1111 23440.0000 57320.5556 +1112 23440.8333 57118.8889 +1113 23441.3889 56228.6111 +1114 23443.6111 58097.7778 +1115 23444.1667 57700.2778 +1116 23444.1667 58127.7778 +1117 23444.4444 57653.8889 +1118 23444.4444 57804.4444 +1119 23445.2778 57321.6667 +1120 23445.8333 57632.7778 +1121 23447.2222 57661.6667 +1122 23447.5000 56222.2222 +1123 23449.1667 56220.0000 +1124 23449.7222 57114.7222 +1125 23450.0000 56783.3333 +1126 23450.0000 56883.3333 +1127 23450.0000 57600.0000 +1128 23450.0000 58583.3333 +1129 23450.2778 58218.3333 +1130 23450.8333 56208.3333 +1131 23450.8333 58112.7778 +1132 23455.0000 57312.7778 +1133 23455.0000 57662.7778 +1134 23455.5556 57592.2222 +1135 23455.8333 57033.6111 +1136 23455.8333 57888.3333 +1137 23455.8333 58304.7222 +1138 23456.1111 58103.0556 +1139 23457.2222 57085.5556 +1140 23458.3333 58298.3333 +1141 23458.6111 58225.8333 +1142 23459.7222 57311.1111 +1143 23460.0000 57314.1667 +1144 23461.3889 57665.0000 +1145 23462.2222 57308.6111 +1146 23462.5000 57674.4444 +1147 23462.7778 58297.7778 +1148 23463.3333 58301.6667 +1149 23464.7222 57453.0556 +1150 23464.7222 58268.3333 +1151 23465.2778 57274.1667 +1152 23465.2778 57310.0000 +1153 23465.5556 57666.1111 +1154 23465.8333 56794.4444 +1155 23466.6667 56650.0000 +1156 23467.5000 58101.3889 +1157 23467.7778 57308.3333 +1158 23468.0556 57660.8333 +1159 23468.8889 58276.9444 +1160 23471.6667 57299.7222 +1161 23471.6667 58674.4444 +1162 23471.9444 56656.1111 +1163 23473.0556 56888.6111 +1164 23474.1667 57019.7222 +1165 23474.4444 57166.9444 +1166 23477.5000 57169.4444 +1167 23478.0556 57442.7778 +1168 23479.7222 58085.0000 +1169 23481.3889 57287.2222 +1170 23482.2222 58328.0556 +1171 23483.3333 57116.6667 +1172 23483.3333 57600.0000 +1173 23483.8889 57594.4444 +1174 23484.1667 57306.3889 +1175 23484.4444 57687.2222 +1176 23484.7222 58347.5000 +1177 23486.1111 58200.2778 +1178 23487.7778 58104.4444 +1179 23488.0556 57918.6111 +1180 23490.5556 58045.2778 +1181 23490.8333 57453.8889 +1182 23492.2222 57006.1111 +1183 23492.7778 57949.4444 +1184 23492.7778 58042.2222 +1185 23493.0556 57014.1667 +1186 23493.0556 57115.0000 +1187 23495.5556 58501.6667 +1188 23496.1111 56830.5556 +1189 23496.1111 57830.8333 +1190 23500.0000 56483.3333 +1191 23500.0000 56516.6667 +1192 23501.3889 58732.5000 +1193 23505.0000 57189.1667 +1194 23506.3889 56465.0000 +1195 23507.5000 57918.6111 +1196 23507.7778 57552.5000 +1197 23508.0556 56451.1111 +1198 23508.3333 58646.9444 +1199 23510.8333 58338.6111 +1200 23512.2222 57844.7222 +1201 23512.7778 57367.2222 +1202 23514.7222 58700.2778 +1203 23516.6667 56500.0000 +1204 23516.6667 56916.6667 +1205 23516.6667 58350.0000 +1206 23518.6111 57224.1667 +1207 23520.0000 57486.9444 +1208 23524.1667 58497.5000 +1209 23524.7222 58506.6667 +1210 23525.8333 58185.5556 +1211 23526.6667 58665.0000 +1212 23527.5000 57330.8333 +1213 23528.3333 58333.6111 +1214 23529.4444 58388.6111 +1215 23530.0000 56312.2222 +1216 23530.5556 57190.5556 +1217 23531.3889 58381.6667 +1218 23531.6667 58389.4444 +1219 23533.3333 58516.6667 +1220 23534.1667 58342.5000 +1221 23535.5556 56325.2778 +1222 23535.8333 56546.6667 +1223 23536.3889 58401.3889 +1224 23537.7778 57469.4444 +1225 23539.1667 58188.6111 +1226 23540.2778 56695.0000 +1227 23541.1111 56303.3333 +1228 23541.1111 56372.7778 +1229 23541.6667 58405.2778 +1230 23545.0000 57212.7778 +1231 23546.3889 56363.0556 +1232 23547.5000 57333.6111 +1233 23548.0556 56244.7222 +1234 23548.3333 56360.2778 +1235 23548.3333 56781.1111 +1236 23549.7222 58404.4444 +1237 23550.0000 57233.3333 +1238 23550.2778 56356.9444 +1239 23550.2778 58022.2222 +1240 23550.8333 56257.5000 +1241 23551.3889 57447.2222 +1242 23551.3889 57473.6111 +1243 23551.3889 58642.7778 +1244 23551.6667 58409.7222 +1245 23552.5000 56280.8333 +1246 23554.1667 57230.5556 +1247 23559.4444 57338.8889 +1248 23561.1111 58630.0000 +1249 23561.3889 57556.6667 +1250 23563.0556 57444.1667 +1251 23563.6111 58250.0000 +1252 23564.1667 56531.9444 +1253 23565.0000 57182.7778 +1254 23565.8333 56267.5000 +1255 23567.7778 58123.3333 +1256 23570.0000 58613.8889 +1257 23572.7778 57444.7222 +1258 23575.2778 57207.2222 +1259 23575.2778 58032.2222 +1260 23575.5556 58548.0556 +1261 23576.6667 57214.1667 +1262 23581.3889 56645.8333 +1263 23585.5556 56540.8333 +1264 23588.6111 58560.5556 +1265 23589.1667 58405.5556 +1266 23590.5556 58550.0000 +1267 23593.8889 58601.6667 +1268 23595.0000 57993.0556 +1269 23596.1111 58596.6667 +1270 23598.0556 57241.1111 +1271 23600.0000 58516.6667 +1272 23600.0000 58533.3333 +1273 23600.5556 58376.3889 +1274 23603.3333 58501.6667 +1275 23603.3333 58554.7222 +1276 23604.1667 58596.9444 +1277 23606.1111 55931.1111 +1278 23607.2222 55958.6111 +1279 23610.8333 57138.6111 +1280 23611.1111 57251.9444 +1281 23611.1111 58456.9444 +1282 23612.5000 55969.7222 +1283 23612.7778 58564.4444 +1284 23613.3333 58593.3333 +1285 23614.1667 58250.5556 +1286 23614.4444 58538.8889 +1287 23616.3889 56956.9444 +1288 23616.6667 58566.6667 +1289 23617.2222 58566.9444 +1290 23619.4444 57266.6667 +1291 23619.7222 58578.8889 +1292 23620.0000 58590.2778 +1293 23621.1111 56939.1667 +1294 23622.7778 56487.7778 +1295 23622.7778 58550.8333 +1296 23626.9444 56952.7778 +1297 23629.1667 58525.5556 +1298 23630.8333 58543.6111 +1299 23631.9444 56443.6111 +1300 23632.7778 58248.0556 +1301 23632.7778 58561.6667 +1302 23633.3333 56800.0000 +1303 23636.6667 56804.4444 +1304 23636.9444 57117.2222 +1305 23639.7222 57193.0556 +1306 23643.8889 56644.4444 +1307 23650.0000 57090.0000 +1308 23652.5000 57216.6667 +1309 23652.5000 57893.8889 +1310 23654.4444 56333.6111 +1311 23657.2222 57157.7778 +1312 23665.0000 56926.1111 +1313 23666.1111 56372.5000 +1314 23666.6667 56366.6667 +1315 23667.2222 56241.9444 +1316 23675.8333 58141.9444 +1317 23678.8889 57068.8889 +1318 23680.2778 58182.5000 +1319 23680.5556 56542.7778 +1320 23683.3333 56900.0000 +1321 23687.2222 56255.5556 +1322 23687.5000 57983.6111 +1323 23687.7778 56551.3889 +1324 23688.0556 56755.5556 +1325 23690.0000 58106.1111 +1326 23696.3889 56265.2778 +1327 23698.6111 58037.2222 +1328 23700.0000 56266.6667 +1329 23700.0000 57024.7222 +1330 23700.5556 58036.3889 +1331 23701.3889 56510.8333 +1332 23701.9444 56752.2222 +1333 23702.7778 58070.5556 +1334 23703.0556 56908.0556 +1335 23704.1667 57932.2222 +1336 23705.5556 58015.0000 +1337 23706.6667 57905.5556 +1338 23708.8889 57605.2778 +1339 23709.1667 57886.9444 +1340 23711.6667 57802.2222 +1341 23712.5000 57834.1667 +1342 23715.8333 57783.0556 +1343 23716.1111 56888.6111 +1344 23716.1111 56905.0000 +1345 23716.6667 56900.0000 +1346 23718.3333 55506.9444 +1347 23718.6111 57760.2778 +1348 23720.2778 57035.0000 +1349 23720.2778 57857.2222 +1350 23721.3889 56904.7222 +1351 23725.5556 55528.8889 +1352 23727.5000 57744.1667 +1353 23733.3333 56766.6667 +1354 23733.3333 57750.0000 +1355 23733.3333 57833.3333 +1356 23736.9444 57044.1667 +1357 23739.7222 57680.8333 +1358 23740.5556 57734.7222 +1359 23746.9444 57620.2778 +1360 23748.0556 55550.5556 +1361 23749.7222 57564.4444 +1362 23750.0000 56966.6667 +1363 23751.3889 56228.8889 +1364 23752.2222 56213.8889 +1365 23752.5000 56683.6111 +1366 23754.7222 56928.3333 +1367 23756.9444 57652.2222 +1368 23759.7222 57022.7778 +1369 23760.0000 56779.1667 +1370 23762.2222 57114.7222 +1371 23762.5000 56679.7222 +1372 23763.3333 57651.6667 +1373 23764.1667 56795.2778 +1374 23764.4444 56661.1111 +1375 23766.6667 57550.0000 +1376 23766.9444 56849.4444 +1377 23767.7778 56670.0000 +1378 23768.6111 56649.4444 +1379 23769.1667 56630.0000 +1380 23769.1667 57765.2778 +1381 23770.8333 57647.2222 +1382 23771.1111 57634.7222 +1383 23771.1111 57676.9444 +1384 23774.1667 57618.3333 +1385 23774.7222 57639.7222 +1386 23778.0556 57644.1667 +1387 23779.7222 56621.1111 +1388 23779.7222 57148.3333 +1389 23780.2778 57635.2778 +1390 23783.3333 56600.0000 +1391 23783.3333 57033.3333 +1392 23783.6111 56718.6111 +1393 23783.8889 57037.2222 +1394 23784.1667 57616.3889 +1395 23784.7222 57522.2222 +1396 23786.3889 57612.7778 +1397 23788.3333 57604.7222 +1398 23790.5556 57537.2222 +1399 23790.8333 56873.0556 +1400 23791.3889 57111.1111 +1401 23792.2222 56695.2778 +1402 23793.6111 57588.0556 +1403 23796.1111 57495.8333 +1404 23796.6667 57502.5000 +1405 23796.9444 57159.1667 +1406 23800.0000 56766.6667 +1407 23802.2222 57476.9444 +1408 23802.5000 57399.1667 +1409 23803.0556 57490.8333 +1410 23805.2778 57141.6667 +1411 23808.3333 57549.1667 +1412 23808.6111 56771.3889 +1413 23809.4444 56982.2222 +1414 23814.7222 56613.0556 +1415 23816.3889 57471.3889 +1416 23818.6111 56396.3889 +1417 23820.2778 56608.6111 +1418 23820.2778 57120.0000 +1419 23824.1667 57508.6111 +1420 23825.2778 57413.0556 +1421 23825.2778 57484.7222 +1422 23828.0556 57451.1111 +1423 23829.7222 57355.5556 +1424 23831.1111 56629.1667 +1425 23831.1111 57475.0000 +1426 23833.3333 57491.3889 +1427 23833.3333 57500.0000 +1428 23833.6111 57355.0000 +1429 23833.6111 57448.0556 +1430 23838.8889 57097.7778 +1431 23839.4444 57428.3333 +1432 23840.8333 56343.6111 +1433 23843.3333 56782.5000 +1434 23844.4444 56565.8333 +1435 23844.4444 57382.2222 +1436 23846.6667 56189.7222 +1437 23849.4444 57438.6111 +1438 23850.0000 56683.3333 +1439 23850.0000 57400.0000 +1440 23850.2778 57359.1667 +1441 23850.2778 57433.0556 +1442 23853.3333 57426.6667 +1443 23853.8889 57361.9444 +1444 23855.2778 56556.1111 +1445 23855.2778 57382.5000 +1446 23856.1111 56349.7222 +1447 23856.6667 57413.6111 +1448 23857.5000 57373.8889 +1449 23858.6111 57338.0556 +1450 23859.1667 57397.7778 +1451 23860.0000 57406.6667 +1452 23860.2778 56641.6667 +1453 23860.2778 57359.1667 +1454 23861.6667 56374.1667 +1455 23862.5000 57393.3333 +1456 23867.2222 57373.6111 +1457 23870.5556 57362.5000 +1458 23870.8333 56908.8889 +1459 23875.8333 56412.5000 +1460 23878.3333 56376.1111 +1461 23879.1667 56382.2222 +1462 23879.1667 57340.5556 +1463 23881.1111 57263.6111 +1464 23881.1111 57335.5556 +1465 23883.3333 56650.0000 +1466 23883.3333 57300.0000 +1467 23883.3333 57316.6667 +1468 23883.8889 56420.8333 +1469 23884.1667 56536.6667 +1470 23888.8889 56734.4444 +1471 23890.0000 57299.4444 +1472 23894.4444 56444.4444 +1473 23895.2778 57236.1111 +1474 23895.5556 57281.6667 +1475 23896.3889 56565.0000 +1476 23897.5000 57276.6667 +1477 23899.7222 57270.5556 +1478 23900.0000 56400.0000 +1479 23900.0000 57266.6667 +1480 23902.5000 56523.3333 +1481 23904.4444 57250.8333 +1482 23906.6667 57249.7222 +1483 23907.5000 56405.5556 +1484 23908.3333 57245.5556 +1485 23915.8333 56714.1667 +1486 23916.6667 56666.6667 +1487 23916.6667 57216.6667 +1488 23918.0556 56729.7222 +1489 23918.0556 57021.1111 +1490 23919.4444 56565.8333 +1491 23919.4444 56756.3889 +1492 23920.8333 56553.3333 +1493 23920.8333 56694.7222 +1494 23924.1667 56233.6111 +1495 23924.1667 56402.5000 +1496 23927.2222 56411.3889 +1497 23927.7778 57169.1667 +1498 23929.1667 56392.7778 +1499 23932.5000 56195.0000 +1500 23932.7778 56550.5556 +1501 23933.0556 56347.7778 +1502 23933.3333 56233.3333 +1503 23933.3333 57183.3333 +1504 23933.8889 56420.0000 +1505 23940.2778 56564.4444 +1506 23943.3333 57122.2222 +1507 23944.1667 57160.8333 +1508 23946.1111 56390.8333 +1509 23947.5000 55819.7222 +1510 23949.1667 57149.4444 +1511 23955.8333 56438.8889 +1512 23958.8889 57107.7778 +1513 23963.8889 57127.5000 +1514 23964.4444 57074.4444 +1515 23968.6111 57075.2778 +1516 23970.2778 57078.8889 +1517 23972.5000 55828.8889 +1518 23973.8889 55843.6111 +1519 23979.7222 55836.1111 +1520 23981.1111 57104.4444 +1521 23983.6111 56410.0000 +1522 23984.1667 56489.7222 +1523 23984.7222 56494.1667 +1524 23985.8333 57073.3333 +1525 23987.7778 55823.8889 +1526 23988.3333 56485.8333 +1527 23991.1111 57093.3333 +1528 23993.0556 56650.5556 +1529 24000.0000 55983.3333 +1530 24000.0000 56250.0000 +1531 24003.6111 57079.7222 +1532 24004.1667 56976.9444 +1533 24004.4444 56510.5556 +1534 24007.5000 56741.3889 +1535 24011.6667 56696.1111 +1536 24012.2222 57069.4444 +1537 24015.0000 56752.7778 +1538 24016.1111 57061.6667 +1539 24016.6667 56250.0000 +1540 24016.6667 56466.6667 +1541 24016.6667 57083.3333 +1542 24023.8889 57053.0556 +1543 24033.3333 56100.0000 +1544 24033.3333 56150.0000 +1545 24033.3333 56166.6667 +1546 24033.3333 56216.6667 +1547 24037.5000 57037.2222 +1548 24045.2778 56783.6111 +1549 24048.8889 56566.9444 +1550 24050.0000 56166.6667 +1551 24051.9444 57015.0000 +1552 24054.4444 57011.1111 +1553 24057.2222 57006.6667 +1554 24061.9444 57000.0000 +1555 24066.6667 56383.3333 +1556 24066.6667 56416.6667 +1557 24072.2222 56781.6667 +1558 24074.1667 56984.4444 +1559 24074.4444 56945.8333 +1560 24079.4444 56978.3333 +1561 24083.3333 56116.6667 +1562 24083.3333 56250.0000 +1563 24083.3333 56466.6667 +1564 24090.5556 56964.7222 +1565 24100.0000 56116.6667 +1566 24100.0000 56416.6667 +1567 24100.0000 56433.3333 +1568 24100.0000 56450.0000 +1569 24111.3889 56941.1111 +1570 24117.2222 56800.0000 +1571 24122.2222 56928.6111 +1572 24133.3333 56200.0000 +1573 24133.3333 56216.6667 +1574 24136.6667 56878.6111 +1575 24142.2222 56910.2778 +1576 24150.0000 56400.0000 +1577 24150.0000 56416.6667 +1578 24150.0000 56433.3333 +1579 24157.2222 56838.8889 +1580 24158.6111 56898.3333 +1581 24164.1667 56893.8889 +1582 24166.6667 56900.0000 +1583 24168.6111 56886.6667 +1584 24169.4444 56864.4444 +1585 24172.2222 56888.6111 +1586 24173.3333 55960.0000 +1587 24174.1667 56861.6667 +1588 24180.2778 56815.0000 +1589 24183.3333 56300.0000 +1590 24183.3333 56316.6667 +1591 24187.7778 56861.1111 +1592 24191.1111 56876.3889 +1593 24196.1111 56874.1667 +1594 24200.0000 56233.3333 +1595 24200.0000 56250.0000 +1596 24200.0000 56266.6667 +1597 24200.0000 56350.0000 +1598 24200.0000 56883.3333 +1599 24201.1111 55958.6111 +1600 24206.3889 56850.0000 +1601 24210.5556 56861.1111 +1602 24215.8333 56827.5000 +1603 24216.6667 56333.3333 +1604 24216.6667 56350.0000 +1605 24222.2222 56831.6667 +1606 24223.6111 56850.5556 +1607 24227.7778 56847.2222 +1608 24235.8333 56825.2778 +1609 24238.6111 55767.7778 +1610 24242.2222 56835.2778 +1611 24248.6111 55959.7222 +1612 24249.1667 55758.8889 +1613 24250.0000 55750.0000 +1614 24250.0000 55783.8889 +1615 24250.0000 56316.6667 +1616 24251.9444 55848.0556 +1617 24255.8333 55772.2222 +1618 24266.6667 56150.0000 +1619 24266.6667 56366.6667 +1620 24272.5000 56814.1667 +1621 24284.1667 56805.5556 +1622 24300.0000 56116.6667 +1623 24306.3889 56790.2778 +1624 24315.8333 56784.1667 +1625 24316.6667 56116.6667 +1626 24316.6667 56133.3333 +1627 24317.7778 56782.2222 +1628 24330.0000 56513.0556 +1629 24334.7222 56770.0000 +1630 24340.8333 56765.5556 +1631 24346.6667 56760.8333 +1632 24350.0000 56366.6667 +1633 24354.7222 56754.1667 +1634 24356.6667 55997.7778 +1635 24364.1667 56718.6111 +1636 24366.6667 56033.3333 +1637 24366.6667 56083.3333 +1638 24366.6667 56316.6667 +1639 24366.6667 56333.3333 +1640 24366.6667 56350.0000 +1641 24366.6667 56416.6667 +1642 24368.8889 56743.8889 +1643 24371.3889 55924.4444 +1644 24376.3889 56738.0556 +1645 24383.3333 56033.3333 +1646 24383.3333 56050.0000 +1647 24383.3333 56316.6667 +1648 24394.7222 56690.0000 +1649 24395.5556 56725.2778 +1650 24395.8333 56670.2778 +1651 24399.4444 55969.7222 +1652 24400.0000 56033.3333 +1653 24401.6667 56576.1111 +1654 24411.1111 56704.7222 +1655 24415.5556 56701.3889 +1656 24416.1111 56582.2222 +1657 24416.6667 56700.0000 +1658 24422.2222 56691.9444 +1659 24428.0556 56611.1111 +1660 24433.3333 56050.0000 +1661 24434.1667 56678.0556 +1662 24435.5556 56646.3889 +1663 24440.2778 56672.7778 +1664 24444.1667 56648.6111 +1665 24446.1111 56633.0556 +1666 24449.4444 55909.7222 +1667 24450.0000 56283.3333 +1668 24450.0000 56566.6667 +1669 24456.3889 56653.6111 +1670 24466.6667 56066.6667 +1671 24466.6667 56333.3333 +1672 24469.1667 56603.6111 +1673 24479.1667 56595.0000 +1674 24483.3333 56066.6667 +1675 24492.2222 56586.6667 +1676 24495.5556 56588.6111 +1677 24498.8889 56577.7778 +1678 24500.2778 55970.5556 +1679 24500.2778 55990.0000 +1680 24508.6111 56445.2778 +1681 24510.5556 56458.3333 +1682 24514.1667 56359.4444 +1683 24516.6667 56566.6667 +1684 24522.2222 56277.5000 +1685 24522.7778 56399.4444 +1686 24525.0000 56597.2222 +1687 24529.1667 56293.6111 +1688 24533.3333 56083.3333 +1689 24534.4444 56278.6111 +1690 24536.1111 56565.5556 +1691 24540.2778 56564.7222 +1692 24548.8889 56101.3889 +1693 24548.8889 56105.2778 +1694 24550.0000 55983.3333 +1695 24550.0000 56566.6667 +1696 24553.0556 56151.9444 +1697 24555.5556 55843.0556 +1698 24564.4444 55985.2778 +1699 24573.3333 56016.9444 +1700 24577.5000 56560.2778 +1701 24580.8333 56390.0000 +1702 24582.7778 56556.3889 +1703 24590.8333 56550.0000 +1704 24595.5556 56547.2222 +1705 24605.2778 56541.6667 +1706 24612.5000 56537.7778 +1707 24617.2222 56026.9444 +1708 24621.9444 56019.4444 +1709 24623.8889 56522.7778 +1710 24629.1667 56077.2222 +1711 24632.2222 56528.0556 +1712 24633.0556 56287.7778 +1713 24636.1111 56526.1111 +1714 24642.5000 56521.6667 +1715 24656.6667 56115.5556 +1716 24656.6667 56515.2778 +1717 24659.1667 56494.1667 +1718 24659.7222 56512.7778 +1719 24666.6667 55929.1667 +1720 24666.9444 56050.2778 +1721 24669.7222 56488.0556 +1722 24671.6667 56505.2778 +1723 24694.4444 56163.6111 +1724 24694.4444 56467.7778 +1725 24701.3889 56288.0556 +1726 24714.4444 56457.5000 +1727 24723.6111 56181.9444 +1728 24725.0000 56455.2778 +1729 24743.3333 56465.8333 +1730 24750.0000 56466.6667 +1731 24751.6667 56178.8889 +1732 24752.5000 56456.1111 +1733 24758.3333 56173.6111 +1734 24760.5556 56172.5000 +1735 24762.7778 56168.8889 +1736 24767.2222 56220.2778 +1737 24777.7778 56289.7222 +1738 24794.7222 56011.1111 +1739 24795.8333 56447.2222 +1740 24800.0000 56233.3333 +1741 24808.8889 56438.8889 +1742 24825.8333 56433.3333 +1743 24841.6667 56417.2222 +1744 24854.4444 56403.3333 +1745 24870.2778 56325.2778 +1746 24880.2778 56408.0556 +1747 24908.0556 56298.8889 +1748 24931.9444 56385.0000 +1749 24975.8333 56365.5556 +1750 25616.3889 56233.8889 +1751 25618.8889 56246.1111 +1752 25635.5556 56265.5556 +1753 25636.3889 56166.9444 +1754 25641.9444 56165.8333 +1755 25646.1111 56164.4444 +1756 25649.7222 56177.2222 +1757 25653.0556 56179.1667 +1758 25655.5556 56268.6111 +1759 25656.3889 56262.2222 +1760 25657.2222 56187.7778 +1761 25663.8889 56171.1111 +1762 25665.8333 56166.3889 +1763 25672.7778 56176.9444 +1764 25674.4444 56264.1667 +1765 25679.4444 56188.6111 +1766 25698.6111 56208.0556 +1767 25700.8333 56273.0556 +1768 25718.0556 56226.6667 +1769 25719.7222 56206.9444 +1770 25742.2222 56287.2222 +1771 25753.0556 56172.5000 +1772 25757.5000 56319.4444 +1773 25766.6667 56150.0000 +1774 25768.3333 56196.9444 +1775 25770.5556 56216.1111 +1776 25770.5556 56275.5556 +1777 25775.0000 56280.2778 +1778 25776.3889 56151.6667 +1779 25777.5000 56244.4444 +1780 25778.3333 56196.1111 +1781 25783.6111 56150.5556 +1782 25785.8333 56333.0556 +1783 25790.5556 56247.2222 +1784 25791.9444 56219.1667 +1785 25793.3333 56148.3333 +1786 25793.8889 56205.8333 +1787 25809.4444 56248.8889 +1788 25809.4444 56359.4444 +1789 25810.5556 56241.3889 +1790 25811.3889 56316.9444 +1791 25813.8889 56305.5556 +1792 25815.8333 56311.9444 +1793 25822.7778 56286.3889 +1794 25838.8889 56341.6667 +1795 25843.0556 56326.6667 +1796 25843.8889 56248.6111 +1797 25848.3333 56171.6667 +1798 25857.7778 56286.1111 +1799 25858.6111 56367.2222 +1800 25860.2778 56371.6667 +1801 25861.6667 56377.5000 +1802 25863.0556 56218.6111 +1803 25875.8333 56173.0556 +1804 25878.3333 56178.0556 +1805 25889.4444 56345.8333 +1806 25899.7222 56183.8889 +1807 25932.7778 56408.6111 +1808 25934.7222 56198.3333 +1809 25941.6667 56425.2778 +1810 25944.1667 56413.3333 +1811 25969.7222 56400.2778 +1812 25977.2222 56330.0000 +1813 26004.4444 56267.5000 +1814 26008.0556 56204.7222 +1815 26008.3333 56274.7222 +1816 26009.7222 56217.2222 +1817 26010.0000 56205.8333 +1818 26013.8889 56406.3889 +1819 26016.6667 56245.0000 +1820 26017.5000 56393.0556 +1821 26018.6111 56220.0000 +1822 26023.8889 56385.2778 +1823 26026.1111 56227.5000 +1824 26028.3333 56360.8333 +1825 26030.2778 56209.4444 +1826 26031.9444 56228.0556 +1827 26032.7778 56352.5000 +1828 26032.7778 56387.5000 +1829 26034.1667 56279.4444 +1830 26035.2778 56197.7778 +1831 26039.1667 56345.0000 +1832 26039.4444 56388.3333 +1833 26040.0000 56360.2778 +1834 26045.0000 56247.2222 +1835 26045.0000 56329.1667 +1836 26048.6111 56195.2778 +1837 26048.6111 56219.7222 +1838 26048.8889 56296.6667 +1839 26050.0000 56377.2222 +1840 26051.9444 56378.8889 +1841 26055.2778 56270.8333 +1842 26056.1111 56368.0556 +1843 26058.6111 56172.2222 +1844 26064.4444 56305.5556 +1845 26065.8333 56241.6667 +1846 26069.4444 56155.5556 +1847 26069.7222 56207.2222 +1848 26071.1111 56109.7222 +1849 26072.5000 56164.7222 +1850 26074.1667 56215.0000 +1851 26077.5000 56189.7222 +1852 26077.5000 56343.3333 +1853 26077.7778 56192.2222 +1854 26081.3889 56166.3889 +1855 26081.6667 56187.2222 +1856 26082.7778 56288.6111 +1857 26082.7778 56343.6111 +1858 26083.0556 56410.2778 +1859 26083.3333 56332.2222 +1860 26085.2778 56438.3333 +1861 26085.5556 56264.7222 +1862 26085.5556 56337.7778 +1863 26086.9444 56379.4444 +1864 26089.7222 56168.3333 +1865 26089.7222 56184.7222 +1866 26089.7222 56204.4444 +1867 26091.1111 56170.2778 +1868 26092.7778 56211.3889 +1869 26092.7778 56399.7222 +1870 26093.3333 56175.2778 +1871 26093.3333 56182.2222 +1872 26093.6111 56230.8333 +1873 26093.8889 56202.2222 +1874 26093.8889 56226.6667 +1875 26093.8889 56365.2778 +1876 26095.0000 56259.4444 +1877 26096.1111 56239.1667 +1878 26096.9444 56195.2778 +1879 26097.7778 56329.4444 +1880 26099.4444 56145.8333 +1881 26101.6667 56149.7222 +1882 26102.7778 56210.0000 +1883 26105.2778 56298.8889 +1884 26105.5556 56156.9444 +1885 26105.8333 56331.6667 +1886 26106.9444 56150.5556 +1887 26109.1667 56128.0556 +1888 26115.5556 56186.3889 +1889 26118.0556 56260.8333 +1890 26119.1667 56336.1111 +1891 26120.2778 56183.0556 +1892 26120.8333 56183.0556 +1893 26124.4444 56313.6111 +1894 26125.5556 56132.7778 +1895 26126.1111 56187.5000 +1896 26127.2222 56183.8889 +1897 26129.4444 56229.7222 +1898 26129.4444 56315.2778 +1899 26129.7222 56188.8889 +1900 26130.2778 56203.6111 +1901 26130.5556 56230.5556 +1902 26131.3889 56231.3889 +1903 26134.4444 56240.2778 +1904 26134.7222 56193.6111 +1905 26136.6667 56188.3333 +1906 26139.4444 56218.3333 +1907 26143.0556 56152.7778 +1908 26145.2778 56286.6667 +1909 26146.3889 56291.1111 +1910 26148.6111 56270.5556 +1911 26149.1667 56305.8333 +1912 26151.3889 56223.8889 +1913 26151.6667 56276.6667 +1914 26151.9444 56327.7778 +1915 26153.6111 56261.6667 +1916 26155.8333 56418.3333 +1917 26158.6111 56202.7778 +1918 26160.8333 56328.0556 +1919 26161.3889 56246.1111 +1920 26161.9444 56437.5000 +1921 26162.2222 56170.2778 +1922 26165.0000 56335.8333 +1923 26165.2778 56277.7778 +1924 26167.5000 56231.3889 +1925 26170.5556 56209.4444 +1926 26171.1111 56207.7778 +1927 26172.5000 56179.7222 +1928 26174.1667 56276.3889 +1929 26176.3889 56226.9444 +1930 26177.5000 56268.6111 +1931 26177.7778 56216.1111 +1932 26179.1667 56256.1111 +1933 26179.1667 56275.5556 +1934 26180.5556 56316.9444 +1935 26183.3333 56300.0000 +1936 26184.4444 56327.5000 +1937 26187.7778 56418.0556 +1938 26189.1667 56279.1667 +1939 26189.4444 56358.8889 +1940 26191.6667 56243.6111 +1941 26192.2222 56223.3333 +1942 26195.0000 56435.2778 +1943 26195.5556 56474.1667 +1944 26196.3889 56279.4444 +1945 26197.7778 56257.7778 +1946 26197.7778 56285.0000 +1947 26198.3333 56373.0556 +1948 26201.6667 56215.2778 +1949 26203.3333 56441.3889 +1950 26207.5000 56275.2778 +1951 26208.0556 56418.8889 +1952 26208.8889 56212.7778 +1953 26209.1667 56219.4444 +1954 26217.5000 56185.8333 +1955 26218.0556 56458.6111 +1956 26219.7222 56340.0000 +1957 26228.0556 56215.2778 +1958 26231.6667 56410.0000 +1959 26231.9444 56205.5556 +1960 26239.7222 56317.5000 +1961 26242.7778 56383.0556 +1962 26247.7778 56368.8889 +1963 26250.2778 56351.3889 +1964 26252.2222 56374.4444 +1965 26260.2778 56393.6111 +1966 26275.2778 56369.7222 +1967 26280.5556 56357.2222 +1968 26283.3333 56395.0000 +1969 26293.3333 56419.1667 +1970 26309.4444 56417.7778 +1971 26310.5556 56357.2222 +1972 26310.8333 56413.3333 +1973 26310.8333 56440.8333 +1974 26312.2222 56450.0000 +1975 26317.2222 56500.8333 +1976 26333.3333 56367.7778 +1977 26336.9444 56410.8333 +1978 26343.6111 56379.7222 +1979 26371.3889 56358.6111 diff --git a/data/phd b/data/phd new file mode 100644 index 0000000..1276bda --- /dev/null +++ b/data/phd @@ -0,0 +1,15 @@ +NAME: phd +TYPE: TSP +DIMENSION: 10 +EDGE_WEIGHT_TYPE: EUC_2D +NODE_COORD_SECTION +1 0 0 +2 1 1 +3 2 -.5 +4 3.5 1.5 +5 2.5 2 +6 3 2.5 +7 2 3 +8 .5 2.75 +9 .75 2.25 +10 -.5 1.25 diff --git a/data/qa194 b/data/qa194 new file mode 100644 index 0000000..e691f72 --- /dev/null +++ b/data/qa194 @@ -0,0 +1,199 @@ +NAME : qa194 +TYPE : TSP +DIMENSION : 194 +EDGE_WEIGHT_TYPE : EUC_2D +NODE_COORD_SECTION +1 24748.3333 50840.0000 +2 24758.8889 51211.9444 +3 24827.2222 51394.7222 +4 24904.4444 51175.0000 +5 24996.1111 51548.8889 +6 25010.0000 51039.4444 +7 25030.8333 51275.2778 +8 25067.7778 51077.5000 +9 25100.0000 51516.6667 +10 25103.3333 51521.6667 +11 25121.9444 51218.3333 +12 25150.8333 51537.7778 +13 25158.3333 51163.6111 +14 25162.2222 51220.8333 +15 25167.7778 51606.9444 +16 25168.8889 51086.3889 +17 25173.8889 51269.4444 +18 25210.8333 51394.1667 +19 25211.3889 51619.1667 +20 25214.1667 50807.2222 +21 25214.4444 51378.8889 +22 25223.3333 51451.6667 +23 25224.1667 51174.4444 +24 25233.3333 51333.3333 +25 25234.1667 51203.0556 +26 25235.5556 51330.0000 +27 25235.5556 51495.5556 +28 25242.7778 51428.8889 +29 25243.0556 51452.5000 +30 25252.5000 51559.1667 +31 25253.8889 51535.2778 +32 25253.8889 51549.7222 +33 25256.9444 51398.8889 +34 25263.6111 51516.3889 +35 25265.8333 51545.2778 +36 25266.6667 50969.1667 +37 25266.6667 51483.3333 +38 25270.5556 51532.7778 +39 25270.8333 51505.8333 +40 25270.8333 51523.0556 +41 25275.8333 51533.6111 +42 25277.2222 51547.7778 +43 25278.3333 51525.5556 +44 25278.3333 51541.3889 +45 25279.1667 51445.5556 +46 25281.1111 51535.0000 +47 25281.3889 51512.5000 +48 25283.3333 51533.3333 +49 25283.6111 51546.6667 +50 25284.7222 51555.2778 +51 25286.1111 51504.1667 +52 25286.1111 51534.1667 +53 25286.6667 51533.3333 +54 25287.5000 51537.7778 +55 25288.0556 51546.6667 +56 25290.8333 51528.3333 +57 25291.9444 51424.4444 +58 25292.5000 51520.8333 +59 25298.6111 51001.6667 +60 25300.8333 51394.4444 +61 25306.9444 51507.7778 +62 25311.9444 51003.0556 +63 25313.8889 50883.3333 +64 25315.2778 51438.6111 +65 25316.6667 50766.6667 +66 25320.5556 51495.5556 +67 25322.5000 51507.7778 +68 25325.2778 51470.0000 +69 25326.6667 51350.2778 +70 25337.5000 51425.0000 +71 25339.1667 51173.3333 +72 25340.5556 51293.6111 +73 25341.9444 51507.5000 +74 25358.8889 51333.6111 +75 25363.6111 51281.1111 +76 25368.6111 51226.3889 +77 25374.4444 51436.6667 +78 25377.7778 51294.7222 +79 25396.9444 51422.5000 +80 25400.0000 51183.3333 +81 25400.0000 51425.0000 +82 25404.7222 51073.0556 +83 25416.9444 51403.8889 +84 25416.9444 51457.7778 +85 25419.4444 50793.6111 +86 25429.7222 50785.8333 +87 25433.3333 51220.0000 +88 25440.8333 51378.0556 +89 25444.4444 50958.3333 +90 25451.3889 50925.0000 +91 25459.1667 51316.6667 +92 25469.7222 51397.5000 +93 25478.0556 51362.5000 +94 25480.5556 50938.8889 +95 25483.3333 51383.3333 +96 25490.5556 51373.6111 +97 25492.2222 51400.2778 +98 25495.0000 50846.6667 +99 25495.0000 50965.2778 +100 25497.5000 51485.2778 +101 25500.8333 50980.5556 +102 25510.5556 51242.2222 +103 25531.9444 51304.4444 +104 25533.3333 50977.2222 +105 25538.8889 51408.3333 +106 25545.8333 51387.5000 +107 25549.7222 51431.9444 +108 25550.0000 51433.3333 +109 25560.2778 51158.6111 +110 25566.9444 51484.7222 +111 25567.5000 50958.8889 +112 25574.7222 51486.3889 +113 25585.5556 51151.3889 +114 25609.4444 51092.2222 +115 25610.2778 51475.2778 +116 25622.5000 51454.4444 +117 25645.8333 51450.0000 +118 25650.0000 51372.2222 +119 25666.9444 51174.4444 +120 25683.8889 51505.8333 +121 25686.3889 51468.8889 +122 25696.1111 51260.8333 +123 25700.8333 51584.7222 +124 25708.3333 51591.6667 +125 25716.6667 51050.0000 +126 25717.5000 51057.7778 +127 25723.0556 51004.1667 +128 25734.7222 51547.5000 +129 25751.1111 51449.1667 +130 25751.9444 50920.8333 +131 25758.3333 51395.8333 +132 25765.2778 51019.7222 +133 25772.2222 51483.3333 +134 25775.8333 51023.0556 +135 25779.1667 51449.7222 +136 25793.3333 51409.4444 +137 25808.3333 51060.5556 +138 25816.6667 51133.3333 +139 25823.6111 51152.5000 +140 25826.6667 51043.8889 +141 25829.7222 51245.2778 +142 25833.3333 51072.2222 +143 25839.1667 51465.2778 +144 25847.7778 51205.8333 +145 25850.0000 51033.3333 +146 25856.6667 51083.3333 +147 25857.5000 51298.8889 +148 25857.5000 51441.3889 +149 25866.6667 51066.6667 +150 25867.7778 51205.5556 +151 25871.9444 51354.7222 +152 25872.5000 51258.3333 +153 25880.8333 51221.3889 +154 25883.0556 51185.2778 +155 25888.0556 51386.3889 +156 25900.0000 51000.0000 +157 25904.1667 51201.6667 +158 25928.3333 51337.5000 +159 25937.5000 51313.3333 +160 25944.7222 51456.3889 +161 25950.0000 51066.6667 +162 25951.6667 51349.7222 +163 25957.7778 51075.2778 +164 25958.3333 51099.4444 +165 25966.6667 51283.3333 +166 25983.3333 51400.0000 +167 25983.6111 51328.0556 +168 26000.2778 51294.4444 +169 26008.6111 51083.6111 +170 26016.6667 51333.3333 +171 26021.6667 51366.9444 +172 26033.3333 51116.6667 +173 26033.3333 51166.6667 +174 26033.6111 51163.8889 +175 26033.6111 51200.2778 +176 26048.8889 51056.9444 +177 26050.0000 51250.0000 +178 26050.2778 51297.5000 +179 26050.5556 51135.8333 +180 26055.0000 51316.1111 +181 26067.2222 51258.6111 +182 26074.7222 51083.6111 +183 26076.6667 51166.9444 +184 26077.2222 51222.2222 +185 26078.0556 51361.6667 +186 26083.6111 51147.2222 +187 26099.7222 51161.1111 +188 26108.0556 51244.7222 +189 26116.6667 51216.6667 +190 26123.6111 51169.1667 +191 26123.6111 51222.7778 +192 26133.3333 51216.6667 +193 26133.3333 51300.0000 +194 26150.2778 51108.0556 diff --git a/data/zi929 b/data/zi929 new file mode 100644 index 0000000..b8d9c8a --- /dev/null +++ b/data/zi929 @@ -0,0 +1,936 @@ +NAME : zi929 +COMMENT : 929 locations in Zimbabwe +COMMENT : Derived from National Imagery and Mapping Agency data +TYPE : TSP +DIMENSION : 929 +EDGE_WEIGHT_TYPE : EUC_2D +NODE_COORD_SECTION +1 15700.0000 30316.6667 +2 16033.3333 28850.0000 +3 16200.0000 31583.3333 +4 16233.3333 31533.3333 +5 16250.0000 31516.6667 +6 16300.0000 29250.0000 +7 16500.0000 29933.3333 +8 16516.6667 28800.0000 +9 16516.6667 29983.3333 +10 16516.6667 31183.3333 +11 16566.6667 31583.3333 +12 16666.6667 29766.6667 +13 16666.6667 30700.0000 +14 16700.0000 31966.6667 +15 16716.6667 30900.0000 +16 16716.6667 32766.6667 +17 16733.3333 29883.3333 +18 16733.3333 31783.3333 +19 16750.0000 30233.3333 +20 16783.3333 31583.3333 +21 16800.0000 31116.6667 +22 16816.6667 28350.0000 +23 16816.6667 29683.3333 +24 16850.0000 29416.6667 +25 16900.0000 29416.6667 +26 16900.0000 29750.0000 +27 16900.0000 30150.0000 +28 16916.6667 30700.0000 +29 16916.6667 31533.3333 +30 16933.3333 31716.6667 +31 16933.3333 32066.6667 +32 16950.0000 29300.0000 +33 16950.0000 30500.0000 +34 16950.0000 31333.3333 +35 16966.6667 32866.6667 +36 17016.6667 31450.0000 +37 17033.3333 30433.3333 +38 17033.3333 30850.0000 +39 17033.3333 30866.6667 +40 17066.6667 28800.0000 +41 17083.3333 28400.0000 +42 17100.0000 28400.0000 +43 17100.0000 32350.0000 +44 17133.3333 29200.0000 +45 17133.3333 30900.0000 +46 17150.0000 28116.6667 +47 17150.0000 30666.6667 +48 17150.0000 32383.3333 +49 17183.3333 28066.6667 +50 17216.6667 32716.6667 +51 17233.3333 31000.0000 +52 17266.6667 30033.3333 +53 17266.6667 31050.0000 +54 17266.6667 31066.6667 +55 17283.3333 31050.0000 +56 17283.3333 31066.6667 +57 17283.3333 32466.6667 +58 17283.3333 32616.6667 +59 17300.0000 31333.3333 +60 17316.6667 30516.6667 +61 17316.6667 31033.3333 +62 17316.6667 31566.6667 +63 17333.3333 28933.3333 +64 17333.3333 31033.3333 +65 17333.3333 32983.3333 +66 17350.0000 30233.3333 +67 17350.0000 31066.6667 +68 17366.6667 30200.0000 +69 17366.6667 30950.0000 +70 17366.6667 31033.3333 +71 17366.6667 31100.0000 +72 17366.6667 32983.3333 +73 17383.3333 30400.0000 +74 17383.3333 30950.0000 +75 17383.3333 31033.3333 +76 17400.0000 32216.6667 +77 17400.0000 32933.3333 +78 17416.6667 28433.3333 +79 17416.6667 30166.6667 +80 17433.3333 32983.3333 +81 17450.0000 28266.6667 +82 17466.6667 28050.0000 +83 17466.6667 30916.6667 +84 17483.3333 27883.3333 +85 17483.3333 27950.0000 +86 17483.3333 27983.3333 +87 17516.6667 30966.6667 +88 17533.3333 27900.0000 +89 17533.3333 30450.0000 +90 17550.0000 28600.0000 +91 17550.0000 31816.6667 +92 17600.0000 31133.3333 +93 17616.6667 27333.3333 +94 17616.6667 27716.6667 +95 17616.6667 32016.6667 +96 17633.3333 30950.0000 +97 17650.0000 30483.3333 +98 17650.0000 31783.3333 +99 17666.6667 30800.0000 +100 17683.3333 29016.6667 +101 17683.3333 31083.3333 +102 17683.3333 31566.6667 +103 17716.6667 30550.0000 +104 17733.0556 31048.0556 +105 17733.3333 31116.6667 +106 17733.3333 31133.3333 +107 17735.2778 30915.2778 +108 17735.5556 30955.2778 +109 17735.5556 31129.1667 +110 17741.1111 31136.1111 +111 17745.0000 31093.3333 +112 17745.5556 30931.3889 +113 17746.1111 30921.9444 +114 17748.3333 31026.9444 +115 17748.6111 31046.1111 +116 17749.1667 31091.6667 +117 17750.0000 30992.2222 +118 17750.2778 31096.6667 +119 17750.5556 31100.0000 +120 17752.5000 31051.6667 +121 17753.0556 31121.3889 +122 17753.6111 30936.6667 +123 17756.3889 31093.0556 +124 17756.6667 31041.9444 +125 17756.6667 31111.9444 +126 17756.9444 31074.1667 +127 17756.9444 31102.5000 +128 17757.2222 31082.2222 +129 17757.7778 30982.7778 +130 17758.8889 31010.5556 +131 17760.2778 31136.6667 +132 17766.6667 30166.6667 +133 17766.6667 30400.0000 +134 17766.9444 31113.3333 +135 17768.0556 31047.5000 +136 17768.3333 31053.6111 +137 17768.6111 31043.6111 +138 17769.1667 31030.5556 +139 17770.0000 30981.3889 +140 17771.6667 31036.9444 +141 17774.1667 31006.3889 +142 17775.0000 31031.1111 +143 17776.6667 31048.6111 +144 17776.6667 31142.7778 +145 17780.0000 31018.6111 +146 17780.5556 31101.9444 +147 17781.3889 31113.6111 +148 17781.9444 31152.2222 +149 17782.5000 31024.1667 +150 17782.5000 31041.6667 +151 17783.3333 30900.0000 +152 17783.3333 31016.6667 +153 17783.3333 31066.6667 +154 17783.3333 31316.6667 +155 17783.8889 31121.6667 +156 17785.0000 31001.9444 +157 17787.7778 31092.5000 +158 17789.4444 31009.1667 +159 17789.7222 31048.3333 +160 17791.3889 31118.0556 +161 17793.6111 31022.5000 +162 17793.6111 31140.2778 +163 17794.1667 31009.1667 +164 17796.3889 31030.5556 +165 17797.7778 30965.5556 +166 17798.0556 31141.1111 +167 17799.1667 30987.5000 +168 17801.6667 31030.8333 +169 17802.2222 31003.3333 +170 17804.7222 31044.1667 +171 17806.1111 30925.2778 +172 17806.6667 31014.1667 +173 17808.0556 31081.6667 +174 17808.6111 31099.4444 +175 17812.5000 31123.0556 +176 17814.4444 31024.7222 +177 17815.2778 31100.2778 +178 17816.6667 31016.6667 +179 17816.6667 32766.6667 +180 17818.3333 31093.3333 +181 17820.0000 31145.2778 +182 17820.8333 31078.0556 +183 17820.8333 31130.0000 +184 17825.2778 30983.0556 +185 17830.0000 31075.5556 +186 17830.2778 31122.7778 +187 17830.8333 31098.8889 +188 17833.0556 31133.3333 +189 17833.3333 31016.6667 +190 17833.3333 31050.0000 +191 17835.5556 31110.2778 +192 17838.3333 30915.8333 +193 17839.7222 31070.8333 +194 17841.3889 31115.5556 +195 17841.9444 31060.5556 +196 17843.8889 30979.1667 +197 17845.8333 31054.7222 +198 17846.3889 30945.5556 +199 17848.0556 31015.0000 +200 17848.8889 31128.8889 +201 17850.0000 28900.0000 +202 17850.0000 31033.3333 +203 17850.0000 31066.6667 +204 17850.0000 31183.3333 +205 17850.0000 31383.3333 +206 17850.2778 31070.5556 +207 17850.2778 31083.8889 +208 17850.5556 31051.3889 +209 17852.7778 31084.7222 +210 17855.2778 30967.2222 +211 17856.6667 30993.8889 +212 17857.2222 31095.2778 +213 17858.6111 30950.0000 +214 17859.1667 31023.0556 +215 17859.7222 31122.7778 +216 17860.0000 31060.8333 +217 17863.8889 31029.7222 +218 17864.4444 31056.6667 +219 17865.2778 30941.6667 +220 17865.8333 30905.2778 +221 17866.3889 30927.2222 +222 17866.6667 31066.6667 +223 17869.1667 31112.5000 +224 17869.4444 30987.7778 +225 17877.2222 31002.2222 +226 17878.3333 31090.8333 +227 17879.4444 31016.6667 +228 17879.4444 31124.4444 +229 17882.7778 31042.2222 +230 17883.3333 30700.0000 +231 17884.4444 31009.1667 +232 17884.7222 30920.8333 +233 17885.8333 30991.9444 +234 17891.3889 31116.6667 +235 17892.7778 31071.3889 +236 17893.6111 31019.7222 +237 17900.0000 27300.0000 +238 17900.0000 31250.0000 +239 17910.5556 30992.2222 +240 17912.7778 31011.1111 +241 17915.2778 31005.5556 +242 17916.6667 30983.3333 +243 17927.5000 31008.6111 +244 17932.5000 30929.7222 +245 17933.3333 25833.3333 +246 17933.6111 30974.4444 +247 17934.4444 31010.5556 +248 17936.1111 31002.5000 +249 17938.8889 30958.3333 +250 17944.1667 30977.5000 +251 17966.6667 27500.0000 +252 17966.6667 30433.3333 +253 17983.3333 31066.6667 +254 18000.0000 27566.6667 +255 18016.6667 27133.3333 +256 18016.6667 31100.0000 +257 18033.3333 30150.0000 +258 18033.3333 31100.0000 +259 18033.3333 31316.6667 +260 18066.6667 27833.3333 +261 18066.6667 29850.0000 +262 18083.3333 30450.0000 +263 18083.3333 31216.6667 +264 18083.3333 31333.3333 +265 18100.0000 29900.0000 +266 18100.0000 29933.3333 +267 18100.0000 30900.0000 +268 18100.0000 31166.6667 +269 18116.6667 27450.0000 +270 18116.6667 29850.0000 +271 18133.3333 29600.0000 +272 18133.3333 29916.6667 +273 18133.3333 30133.3333 +274 18133.3333 30150.0000 +275 18150.0000 28383.3333 +276 18150.0000 31850.0000 +277 18166.6667 31233.3333 +278 18166.6667 31683.3333 +279 18183.3333 30700.0000 +280 18183.3333 31400.0000 +281 18183.3333 31550.0000 +282 18200.0000 29916.6667 +283 18216.6667 27400.0000 +284 18216.6667 27950.0000 +285 18216.6667 28933.3333 +286 18216.6667 29800.0000 +287 18216.6667 30933.3333 +288 18216.6667 32750.0000 +289 18266.6667 30566.6667 +290 18266.6667 30866.6667 +291 18266.6667 31100.0000 +292 18283.3333 32050.0000 +293 18300.0000 27983.3333 +294 18300.0000 30216.6667 +295 18300.0000 30683.3333 +296 18300.0000 31366.6667 +297 18316.6667 27066.6667 +298 18316.6667 29983.3333 +299 18316.6667 30583.3333 +300 18316.6667 30600.0000 +301 18316.6667 31133.3333 +302 18316.6667 31200.0000 +303 18333.3333 30600.0000 +304 18350.0000 29883.3333 +305 18350.0000 29916.6667 +306 18366.6667 26483.3333 +307 18366.6667 30083.3333 +308 18366.6667 32600.0000 +309 18366.6667 32666.6667 +310 18383.3333 29866.6667 +311 18383.3333 30683.3333 +312 18383.3333 31350.0000 +313 18400.0000 31766.6667 +314 18416.6667 28016.6667 +315 18450.0000 29450.0000 +316 18450.0000 29816.6667 +317 18466.6667 30700.0000 +318 18466.6667 31516.6667 +319 18483.3333 29416.6667 +320 18516.6667 31400.0000 +321 18533.3333 30533.3333 +322 18533.3333 32116.6667 +323 18550.0000 32116.6667 +324 18566.6667 31050.0000 +325 18583.3333 26316.6667 +326 18600.0000 29833.3333 +327 18616.6667 26866.6667 +328 18616.6667 27166.6667 +329 18616.6667 31566.6667 +330 18650.0000 29783.3333 +331 18650.0000 32616.6667 +332 18666.6667 28683.3333 +333 18683.3333 32633.3333 +334 18716.6667 27550.0000 +335 18716.6667 30816.6667 +336 18716.6667 31983.3333 +337 18716.6667 32166.6667 +338 18733.3333 28816.6667 +339 18733.3333 32800.0000 +340 18766.6667 28500.0000 +341 18783.3333 28666.6667 +342 18800.0000 28333.3333 +343 18800.0000 28466.6667 +344 18816.6667 27166.6667 +345 18816.6667 28650.0000 +346 18866.6667 27433.3333 +347 18883.3333 27416.6667 +348 18883.3333 32683.3333 +349 18900.0000 27466.6667 +350 18900.0000 31283.3333 +351 18916.6667 29816.6667 +352 18916.6667 29833.3333 +353 18933.3333 27766.6667 +354 18933.3333 30050.0000 +355 18933.3333 32583.3333 +356 18950.0000 27516.6667 +357 18950.0000 30283.3333 +358 18966.6667 27416.6667 +359 18966.6667 32383.3333 +360 18966.6667 32666.6667 +361 18983.3333 29300.0000 +362 18983.3333 30100.0000 +363 18983.3333 32616.6667 +364 19000.0000 28900.0000 +365 19000.0000 31033.3333 +366 19000.0000 32583.3333 +367 19016.6667 28533.3333 +368 19016.6667 28550.0000 +369 19016.6667 29733.3333 +370 19016.6667 30900.0000 +371 19033.3333 28600.0000 +372 19033.3333 29783.3333 +373 19066.6667 31750.0000 +374 19083.3333 28716.6667 +375 19083.3333 31166.6667 +376 19100.0000 27966.6667 +377 19116.6667 27683.3333 +378 19133.3333 27533.3333 +379 19133.3333 27650.0000 +380 19150.0000 28666.6667 +381 19150.0000 29800.0000 +382 19166.6667 27516.6667 +383 19166.6667 27583.3333 +384 19166.6667 32616.6667 +385 19183.3333 27466.6667 +386 19183.3333 27516.6667 +387 19183.3333 27600.0000 +388 19183.3333 27683.3333 +389 19200.0000 27616.6667 +390 19200.0000 27666.6667 +391 19200.0000 29816.6667 +392 19216.6667 27483.3333 +393 19216.6667 27516.6667 +394 19216.6667 27533.3333 +395 19216.6667 27566.6667 +396 19216.6667 27700.0000 +397 19233.3333 27600.0000 +398 19233.3333 29250.0000 +399 19250.0000 27633.3333 +400 19250.0000 27716.6667 +401 19266.6667 27366.6667 +402 19266.6667 27400.0000 +403 19266.6667 27433.3333 +404 19266.6667 27516.6667 +405 19266.6667 27550.0000 +406 19266.6667 27600.0000 +407 19283.3333 27333.3333 +408 19283.3333 27450.0000 +409 19283.3333 27633.3333 +410 19283.3333 27650.0000 +411 19283.3333 27666.6667 +412 19283.3333 29616.6667 +413 19283.3333 30533.3333 +414 19300.0000 27416.6667 +415 19300.0000 27516.6667 +416 19300.0000 27533.3333 +417 19300.0000 27633.3333 +418 19316.6667 27333.3333 +419 19316.6667 27366.6667 +420 19316.6667 27683.3333 +421 19333.3333 27500.0000 +422 19333.3333 27533.3333 +423 19333.3333 27633.3333 +424 19333.3333 27650.0000 +425 19333.3333 30183.3333 +426 19333.3333 31433.3333 +427 19350.0000 27016.6667 +428 19350.0000 27383.3333 +429 19350.0000 27400.0000 +430 19350.0000 27600.0000 +431 19350.0000 27633.3333 +432 19350.0000 27650.0000 +433 19350.0000 28466.6667 +434 19366.6667 26983.3333 +435 19366.6667 27050.0000 +436 19366.6667 27433.3333 +437 19366.6667 29566.6667 +438 19366.6667 30016.6667 +439 19383.3333 26916.6667 +440 19383.3333 27016.6667 +441 19383.3333 27466.6667 +442 19383.3333 27633.3333 +443 19383.3333 29600.0000 +444 19400.0000 26966.6667 +445 19400.0000 27066.6667 +446 19400.0000 27083.3333 +447 19400.0000 27200.0000 +448 19400.0000 27283.3333 +449 19400.0000 27666.6667 +450 19400.0000 28400.0000 +451 19400.0000 28450.0000 +452 19416.6667 27033.3333 +453 19416.6667 27166.6667 +454 19416.6667 27333.3333 +455 19416.6667 27633.3333 +456 19416.6667 27716.6667 +457 19433.3333 27133.3333 +458 19433.3333 27233.3333 +459 19433.3333 27400.0000 +460 19433.3333 27500.0000 +461 19433.3333 27516.6667 +462 19433.3333 27550.0000 +463 19433.3333 27633.3333 +464 19433.3333 27700.0000 +465 19433.3333 28950.0000 +466 19433.3333 30833.3333 +467 19450.0000 27016.6667 +468 19450.0000 27066.6667 +469 19450.0000 27250.0000 +470 19450.0000 27283.3333 +471 19450.0000 27516.6667 +472 19450.0000 27700.0000 +473 19450.0000 29816.6667 +474 19466.6667 27033.3333 +475 19466.6667 27066.6667 +476 19466.6667 27150.0000 +477 19466.6667 27200.0000 +478 19466.6667 27316.6667 +479 19466.6667 27366.6667 +480 19466.6667 27466.6667 +481 19466.6667 27666.6667 +482 19466.6667 29750.0000 +483 19466.6667 29766.6667 +484 19466.6667 31216.6667 +485 19483.3333 27050.0000 +486 19483.3333 27183.3333 +487 19483.3333 27233.3333 +488 19483.3333 27283.3333 +489 19483.3333 27366.6667 +490 19483.3333 27433.3333 +491 19483.3333 27450.0000 +492 19483.3333 27550.0000 +493 19483.3333 27616.6667 +494 19483.3333 28566.6667 +495 19483.3333 30783.3333 +496 19500.0000 27066.6667 +497 19500.0000 27100.0000 +498 19500.0000 27400.0000 +499 19500.0000 27716.6667 +500 19500.0000 27733.3333 +501 19500.0000 28750.0000 +502 19500.0000 29750.0000 +503 19516.6667 27233.3333 +504 19516.6667 27483.3333 +505 19516.6667 27533.3333 +506 19516.6667 27650.0000 +507 19516.6667 27750.0000 +508 19516.6667 32550.0000 +509 19533.3333 26916.6667 +510 19533.3333 27083.3333 +511 19533.3333 27100.0000 +512 19533.3333 27133.3333 +513 19533.3333 27216.6667 +514 19533.3333 27283.3333 +515 19533.3333 29933.3333 +516 19550.0000 27050.0000 +517 19550.0000 27166.6667 +518 19550.0000 27416.6667 +519 19550.0000 27750.0000 +520 19550.0000 32666.6667 +521 19550.0000 32783.3333 +522 19566.6667 27033.3333 +523 19566.6667 27066.6667 +524 19566.6667 27133.3333 +525 19583.3333 27133.3333 +526 19583.3333 27466.6667 +527 19583.3333 27666.6667 +528 19583.3333 27733.3333 +529 19583.3333 28033.3333 +530 19583.3333 30750.0000 +531 19600.0000 27166.6667 +532 19600.0000 27566.6667 +533 19600.0000 27766.6667 +534 19600.0000 32800.0000 +535 19616.6667 27033.3333 +536 19616.6667 27116.6667 +537 19616.6667 27516.6667 +538 19633.3333 27066.6667 +539 19633.3333 27100.0000 +540 19633.3333 27183.3333 +541 19633.3333 27683.3333 +542 19633.3333 27716.6667 +543 19633.3333 30833.3333 +544 19650.0000 27050.0000 +545 19650.0000 27083.3333 +546 19650.0000 27150.0000 +547 19650.0000 27550.0000 +548 19650.0000 27650.0000 +549 19650.0000 31166.6667 +550 19650.0000 32466.6667 +551 19666.6667 26966.6667 +552 19666.6667 27133.3333 +553 19666.6667 27250.0000 +554 19666.6667 27333.3333 +555 19666.6667 27416.6667 +556 19666.6667 27566.6667 +557 19666.6667 27616.6667 +558 19666.6667 27700.0000 +559 19666.6667 30000.0000 +560 19683.3333 27016.6667 +561 19683.3333 27383.3333 +562 19683.3333 27433.3333 +563 19683.3333 28850.0000 +564 19700.0000 27033.3333 +565 19700.0000 27066.6667 +566 19700.0000 27083.3333 +567 19700.0000 27466.6667 +568 19700.0000 27500.0000 +569 19700.0000 27650.0000 +570 19700.0000 27683.3333 +571 19700.0000 29666.6667 +572 19716.6667 27583.3333 +573 19716.6667 27766.6667 +574 19716.6667 28800.0000 +575 19716.6667 29500.0000 +576 19716.6667 31833.3333 +577 19733.3333 27600.0000 +578 19733.3333 27716.6667 +579 19733.3333 30666.6667 +580 19750.0000 27766.6667 +581 19750.0000 27783.3333 +582 19766.6667 27216.6667 +583 19766.6667 27316.6667 +584 19766.6667 27400.0000 +585 19766.6667 27616.6667 +586 19766.6667 27750.0000 +587 19766.6667 27800.0000 +588 19766.6667 28766.6667 +589 19766.6667 32416.6667 +590 19783.3333 27683.3333 +591 19783.3333 27783.3333 +592 19783.3333 29200.0000 +593 19783.3333 29366.6667 +594 19800.0000 27616.6667 +595 19800.0000 27650.0000 +596 19800.0000 27833.3333 +597 19800.0000 28750.0000 +598 19800.0000 32866.6667 +599 19816.6667 27683.3333 +600 19833.3333 27216.6667 +601 19833.3333 27400.0000 +602 19833.3333 27466.6667 +603 19833.3333 27633.3333 +604 19833.3333 27783.3333 +605 19850.0000 27383.3333 +606 19850.0000 27416.6667 +607 19850.0000 27650.0000 +608 19850.0000 27716.6667 +609 19850.0000 27783.3333 +610 19850.0000 27866.6667 +611 19866.6667 28266.6667 +612 19883.3333 27616.6667 +613 19894.4444 30758.3333 +614 19911.1111 29691.6667 +615 19916.6667 27366.6667 +616 19916.6667 27783.3333 +617 19916.6667 30050.0000 +618 19933.3333 27233.3333 +619 19933.3333 27266.6667 +620 19933.3333 27650.0000 +621 19933.3333 27700.0000 +622 19936.1111 31636.1111 +623 19950.0000 27333.3333 +624 19966.6667 27400.0000 +625 19966.6667 27416.6667 +626 19966.6667 31433.3333 +627 19966.6667 32333.3333 +628 20000.0000 28933.3333 +629 20016.6667 28850.0000 +630 20016.6667 29266.6667 +631 20016.6667 30516.6667 +632 20016.6667 31466.6667 +633 20032.7778 29884.7222 +634 20033.3333 27766.6667 +635 20033.3333 27933.3333 +636 20050.0000 27766.6667 +637 20050.0000 30483.3333 +638 20050.0000 30800.0000 +639 20050.0000 31266.6667 +640 20075.0000 28585.2778 +641 20075.8333 28406.6667 +642 20077.7778 28429.4444 +643 20080.0000 28658.3333 +644 20081.1111 28568.8889 +645 20083.3333 28590.5556 +646 20083.3333 28783.3333 +647 20083.3333 30533.3333 +648 20083.3333 30816.6667 +649 20083.3333 30833.3333 +650 20083.3333 31616.6667 +651 20085.0000 28551.9444 +652 20090.5556 28582.2222 +653 20092.7778 28574.4444 +654 20093.0556 28553.8889 +655 20093.3333 28586.1111 +656 20093.6111 28597.7778 +657 20095.0000 28612.7778 +658 20096.1111 28622.7778 +659 20097.7778 28680.8333 +660 20098.6111 28611.6667 +661 20098.8889 28558.8889 +662 20100.0000 27483.3333 +663 20100.0000 27666.6667 +664 20100.0000 27950.0000 +665 20100.0000 28500.0000 +666 20100.0000 28550.0000 +667 20100.0000 28638.3333 +668 20100.2778 28596.3889 +669 20100.5556 28652.7778 +670 20100.8333 28610.2778 +671 20101.3889 28645.2778 +672 20101.9444 28578.0556 +673 20103.3333 28475.0000 +674 20103.3333 28606.6667 +675 20103.3333 28621.9444 +676 20104.1667 28517.5000 +677 20106.3889 28605.8333 +678 20106.3889 28652.7778 +679 20107.5000 28591.9444 +680 20108.3333 28498.8889 +681 20111.1111 28596.9444 +682 20113.6111 28604.7222 +683 20113.8889 28527.7778 +684 20113.8889 28685.5556 +685 20116.3889 28615.2778 +686 20116.6667 27650.0000 +687 20116.6667 27900.0000 +688 20116.6667 30366.6667 +689 20119.4444 28583.8889 +690 20119.4444 28589.4444 +691 20120.2778 28511.1111 +692 20121.6667 28528.6111 +693 20122.5000 28495.5556 +694 20123.0556 28602.7778 +695 20123.0556 28609.7222 +696 20125.2778 28543.8889 +697 20127.7778 28578.6111 +698 20128.3333 28631.1111 +699 20128.8889 28570.0000 +700 20131.1111 28603.0556 +701 20131.6667 28494.7222 +702 20131.6667 28591.1111 +703 20133.8889 28650.5556 +704 20135.5556 28568.8889 +705 20136.1111 28517.2222 +706 20136.1111 28532.5000 +707 20136.6667 28456.9444 +708 20136.9444 28505.8333 +709 20137.5000 28485.8333 +710 20138.0556 28552.5000 +711 20140.2778 28564.4444 +712 20141.1111 28600.0000 +713 20141.9444 28575.2778 +714 20142.2222 28622.7778 +715 20142.5000 28531.6667 +716 20143.3333 28616.1111 +717 20144.4444 28473.3333 +718 20145.0000 28540.8333 +719 20145.8333 28594.7222 +720 20146.9444 28492.2222 +721 20147.2222 28564.4444 +722 20148.3333 28521.1111 +723 20149.1667 28529.4444 +724 20149.1667 28737.2222 +725 20149.4444 28595.2778 +726 20150.0000 27300.0000 +727 20150.0000 27700.0000 +728 20150.0000 28583.3333 +729 20150.0000 28616.6667 +730 20150.0000 29533.3333 +731 20152.5000 28536.6667 +732 20152.5000 28584.7222 +733 20154.7222 28525.2778 +734 20155.5556 28553.8889 +735 20158.3333 28601.1111 +736 20159.1667 28560.0000 +737 20160.5556 28508.0556 +738 20161.3889 28610.0000 +739 20162.5000 28538.8889 +740 20162.7778 28568.6111 +741 20163.8889 28652.5000 +742 20164.1667 28525.0000 +743 20165.2778 28679.4444 +744 20166.6667 27600.0000 +745 20166.6667 31083.3333 +746 20166.6667 32783.3333 +747 20167.5000 28556.3889 +748 20168.3333 28671.3889 +749 20170.8333 28618.0556 +750 20171.6667 28571.3889 +751 20172.2222 28578.6111 +752 20172.7778 28542.7778 +753 20173.0556 28630.2778 +754 20174.7222 28662.7778 +755 20177.7778 30836.1111 +756 20178.8889 28534.1667 +757 20179.4444 28609.7222 +758 20180.8333 28649.1667 +759 20183.3333 28633.3333 +760 20183.3333 31333.3333 +761 20184.4444 28586.1111 +762 20185.0000 28516.9444 +763 20185.0000 28601.1111 +764 20185.2778 28495.0000 +765 20185.5556 28559.1667 +766 20185.8333 28735.5556 +767 20187.2222 28667.7778 +768 20187.7778 28593.8889 +769 20190.0000 28620.5556 +770 20191.6667 28568.3333 +771 20193.0556 28636.1111 +772 20194.4444 28581.6667 +773 20197.5000 28531.9444 +774 20198.0556 28558.6111 +775 20198.8889 28620.0000 +776 20199.1667 28600.0000 +777 20199.4444 28631.9444 +778 20200.0000 27766.6667 +779 20200.0000 28550.0000 +780 20200.0000 32616.6667 +781 20200.8333 28567.5000 +782 20201.6667 28534.4444 +783 20201.9444 28577.2222 +784 20204.1667 28714.7222 +785 20205.0000 28595.0000 +786 20206.3889 28581.9444 +787 20206.9444 28609.7222 +788 20208.6111 28721.3889 +789 20212.2222 28681.6667 +790 20214.1667 28558.3333 +791 20214.4444 28652.5000 +792 20214.7222 28595.2778 +793 20215.8333 28664.7222 +794 20216.6667 27283.3333 +795 20216.6667 27766.6667 +796 20216.6667 29300.0000 +797 20216.6667 31416.6667 +798 20217.5000 28615.5556 +799 20218.6111 28589.1667 +800 20220.0000 28578.6111 +801 20221.6667 28573.8889 +802 20222.2222 28583.6111 +803 20222.2222 28711.1111 +804 20224.4444 28702.5000 +805 20225.5556 28278.8889 +806 20227.7778 28638.0556 +807 20228.0556 28629.4444 +808 20228.0556 28659.4444 +809 20228.6111 28618.3333 +810 20229.4444 28611.1111 +811 20233.3333 27300.0000 +812 20233.3333 28916.6667 +813 20233.6111 28719.7222 +814 20233.6111 28746.6667 +815 20237.2222 28746.1111 +816 20240.0000 28641.9444 +817 20266.6667 30916.6667 +818 20280.5556 29884.7222 +819 20300.0000 28933.3333 +820 20300.0000 32616.6667 +821 20316.6667 30516.6667 +822 20316.6667 32650.0000 +823 20316.6667 32733.3333 +824 20333.3333 30033.3333 +825 20333.3333 32500.0000 +826 20350.0000 31450.0000 +827 20366.6667 28333.3333 +828 20366.6667 28983.3333 +829 20366.6667 32400.0000 +830 20366.6667 32600.0000 +831 20383.3333 28216.6667 +832 20383.3333 32250.0000 +833 20400.0000 28900.0000 +834 20400.0000 31450.0000 +835 20416.6667 28483.3333 +836 20416.6667 28600.0000 +837 20416.6667 32100.0000 +838 20419.4444 31963.8889 +839 20433.3333 29683.3333 +840 20433.3333 30733.3333 +841 20433.3333 32483.3333 +842 20450.0000 29050.0000 +843 20466.6667 31866.6667 +844 20483.3333 27816.6667 +845 20483.3333 29916.6667 +846 20516.3889 29055.5556 +847 20516.6667 29316.6667 +848 20533.3333 29283.3333 +849 20550.0000 30533.3333 +850 20550.0000 31316.6667 +851 20550.0000 32400.0000 +852 20566.6667 29333.3333 +853 20566.6667 30400.0000 +854 20600.0000 27850.0000 +855 20600.0000 28383.3333 +856 20602.7778 31397.2222 +857 20613.8889 31113.8889 +858 20616.6667 28400.0000 +859 20616.6667 30050.0000 +860 20649.1667 29151.3889 +861 20666.6667 32266.6667 +862 20683.3333 28416.6667 +863 20698.6111 31625.0000 +864 20700.0000 28066.6667 +865 20749.1667 29701.3889 +866 20800.0000 29533.3333 +867 20800.0000 30800.0000 +868 20800.0000 32233.3333 +869 20850.0000 30200.0000 +870 20899.4444 30786.1111 +871 20916.6667 28466.6667 +872 20916.6667 28950.0000 +873 20927.7778 29441.6667 +874 20933.3333 29000.0000 +875 20950.0000 31366.6667 +876 20966.6667 30266.6667 +877 20966.6667 31733.3333 +878 20983.3333 29033.3333 +879 21000.0000 29216.6667 +880 21016.6667 31533.3333 +881 21033.3333 31450.0000 +882 21050.0000 28450.0000 +883 21050.0000 31666.6667 +884 21066.6667 29366.6667 +885 21083.3333 27716.6667 +886 21150.0000 31283.3333 +887 21166.6667 31550.0000 +888 21200.0000 27850.0000 +889 21250.0000 30733.3333 +890 21383.3333 31016.6667 +891 21416.6667 28533.3333 +892 21416.6667 30733.3333 +893 21423.8889 30728.8889 +894 21433.3333 28855.5556 +895 21433.3333 29900.0000 +896 21433.3333 32033.3333 +897 21466.6667 31583.3333 +898 21500.0000 28083.3333 +899 21516.6667 28866.6667 +900 21516.6667 29533.3333 +901 21533.3333 31483.3333 +902 21583.3333 31250.0000 +903 21633.3333 32100.0000 +904 21650.0000 28933.3333 +905 21666.9444 29124.7222 +906 21683.3333 31316.6667 +907 21700.0000 29200.0000 +908 21733.3333 29866.6667 +909 21815.2778 31570.8333 +910 21850.0000 29350.0000 +911 21850.0000 29400.0000 +912 21916.6667 29200.0000 +913 21916.6667 29366.6667 +914 21917.2222 30027.2222 +915 21944.7222 31627.7778 +916 21966.6667 29366.6667 +917 21966.6667 29583.3333 +918 22016.6667 29366.6667 +919 22016.6667 30166.6667 +920 22066.6667 31416.6667 +921 22083.3333 30150.0000 +922 22083.3333 30650.0000 +923 22116.6667 30066.6667 +924 22133.3333 30666.6667 +925 22150.0000 30633.3333 +926 22200.0000 30350.0000 +927 22200.0000 30783.3333 +928 22216.6667 30000.0000 +929 22233.3333 30933.3333 diff --git a/examples/greedy.cpp b/examples/greedy.cpp new file mode 100644 index 0000000..b0cd0f6 --- /dev/null +++ b/examples/greedy.cpp @@ -0,0 +1,42 @@ +#include +#include +#include + +#include "muscles/rgreedy.h" + +#include "tsp/problem.h" +#include "tsp/solution.h" +#include "tsp/tsp.h" + +auto mainGreedy(tsp::Problem const& problem, std::size_t start) { + std::mt19937 rng; + auto greedy = RGreedy{0}; + return greedy(problem, rng, start); +} + +int main(int argc, char **argv) { + tsp::Tsp tspData{"../data/phd"}; + tsp::Problem const& problem = tspData.points(); + tsp::Solution solution; + + std::size_t start = 0; + + if(argc > 1) { + std::istringstream iss{argv[1]}; + iss >> start; + } + + solution = mainGreedy(problem, start); + + std::cout << "distance: " << solution.value() << std::endl; + + // save + { + std::ofstream ofs{"solution"}; + ofs << solution; + } + { + std::ofstream ofs{"solution.ids"}; + solution.printIndices(ofs); + } +} diff --git a/inc/alsk/alsk.h b/inc/alsk/alsk.h new file mode 100644 index 0000000..c599d90 --- /dev/null +++ b/inc/alsk/alsk.h @@ -0,0 +1,10 @@ +#ifndef ALSK_ALSK_H +#define ALSK_ALSK_H + +#include "context/context.h" +#include "edsl/edsl.h" +#include "executor/executor.h" +#include "impl/impl.h" +#include "skeleton/skeleton.h" + +#endif diff --git a/inc/alsk/context/context.h b/inc/alsk/context/context.h new file mode 100644 index 0000000..586076b --- /dev/null +++ b/inc/alsk/context/context.h @@ -0,0 +1,90 @@ +#ifndef ALSK_ALSK_CONTEXT_CONTEXT_H +#define ALSK_ALSK_CONTEXT_CONTEXT_H + +#include +#include +#include + +#include + +#include "../skeleton/link/args/placeholders.h" + +namespace alsk { + +template +class Context { +public: + using Id = std::size_t; + + using Args = std::tuple; + + using Seed = decltype(+RNG::default_seed); + +private: + Id _idCount; + + std::vector _rng; + std::vector _args; + +public: + Seed seed; + +public: + constexpr Context(): _idCount{0}, seed{RNG::default_seed} {} + + void setup(Id idCount = 30) { + if(idCount <= _idCount) return; + Id baseId = _idCount; + _idCount = idCount; + + RNG seeder{seed}; + + _rng.reserve(idCount); + for(Id id = baseId; id < idCount; ++id) + _rng.emplace_back(seeder()); + + _args.reserve(idCount); + for(Id id = baseId; id < idCount; ++id) + _args.emplace_back(id, std::ref(_rng[id])); + } + + void reset() { + RNG seeder{seed}; + + _rng.clear(); + for(Id id = 0; id < _idCount; ++id) + _rng.emplace_back(seeder()); + + _args.clear(); + for(Id id = 0; id < _idCount; ++id) + _args.emplace_back(id, std::ref(_rng[id])); + } + + constexpr Id maxId() const noexcept { return _idCount; } + + Args& args(Id const& id) { return _args[id]; } + + ~Context() noexcept {} +}; + +using DefaultContext = Context; + +namespace arg { + +/** + * CtxId + * to get own's context identifier + */ +using CtxId = C<0>; + +/** + * RNG + * for random number generator + */ +using RNG = C<1>; + +} + +} + +#endif diff --git a/inc/alsk/edsl/edsl.h b/inc/alsk/edsl/edsl.h new file mode 100644 index 0000000..945531b --- /dev/null +++ b/inc/alsk/edsl/edsl.h @@ -0,0 +1,19 @@ +#ifndef ALSK_ALSK_EDSL_EDSL_H +#define ALSK_ALSK_EDSL_EDSL_H + +#include "implement.h" +#include "link.h" +#include "op/op.h" + +namespace alsk { +namespace edsl { + +template>>* = nullptr> +constexpr decltype(auto) seq(Expression&& expression) { + return &std::forward(expression); +} + +} +} + +#endif diff --git a/inc/alsk/edsl/implement.h b/inc/alsk/edsl/implement.h new file mode 100644 index 0000000..fd95069 --- /dev/null +++ b/inc/alsk/edsl/implement.h @@ -0,0 +1,50 @@ +#ifndef ALSK_ALSK_EDSL_IMPL_H +#define ALSK_ALSK_EDSL_IMPL_H + +#include + +#include "../executor/traits.h" +#include "../skeleton/utility.h" +#include "../impl/impl.h" + +#include "op/impl/serial.h" +#include "op/traits.h" +#include "link.h" + +namespace alsk { +namespace edsl { + +template +constexpr auto getSkeleton(Expression) -> BuildSkeletonT; + +template< + template class Executor, typename Context = DefaultContext, + typename Expression, + std::enable_if_t>>* = nullptr +> +constexpr auto implement(Expression&& expression) { + using Skeleton = decltype(getSkeleton(expression)); + + auto f = alsk::implement(); + std::forward(expression).setup(f.skeleton); + return f; +} + +template< + template class Executor, typename Context = DefaultContext, + typename Expression, + std::enable_if_t>>* = nullptr +> +constexpr auto implement(Expression&& expression) { + using Signature = typename std::decay_t::Signature; + using FixedSignatures = AutoLinkSerial; + auto linkedExpression = link(std::forward(expression)); + + using LinkedExpression = std::decay_t; + return implement(Serial{std::move(linkedExpression)}); +} + +} +} + +#endif diff --git a/inc/alsk/edsl/link.h b/inc/alsk/edsl/link.h new file mode 100644 index 0000000..6d7d0ce --- /dev/null +++ b/inc/alsk/edsl/link.h @@ -0,0 +1,34 @@ +#ifndef ALSK_ALSK_EDSL_LINK_H +#define ALSK_ALSK_EDSL_LINK_H + +#include + +#include "op/op.h" + +namespace alsk { +namespace edsl { + +template +constexpr decltype(auto) link(Expression&& expression) { + return std::forward(expression).template link(); +} + +template 1)>* = nullptr> +constexpr decltype(auto) link(Expressions&&... expressions) { + return link(serial(std::forward(expressions)...)); +} + +template>* = nullptr> +constexpr decltype(auto) link() { + return link(makeOperand()); +} + +template>* = nullptr> +constexpr decltype(auto) link() { + return link(); +} + +} +} + +#endif diff --git a/inc/alsk/edsl/op/impl/farm.h b/inc/alsk/edsl/op/impl/farm.h new file mode 100644 index 0000000..00337dc --- /dev/null +++ b/inc/alsk/edsl/op/impl/farm.h @@ -0,0 +1,85 @@ +#ifndef ALSK_ALSK_EDSL_OP_IMPL_FARM_H +#define ALSK_ALSK_EDSL_OP_IMPL_FARM_H + +#include + +#include "../traits.h" +#include "../../../skeleton/bone/farm.h" +#include "../../../skeleton/struct/struct.h" +#include "../../../skeleton/link/link.h" + +namespace alsk { +namespace edsl { + +template struct FarmSel; + +template +struct Farm: OperandBase { + Task task; + unsigned int n; + + constexpr Farm(Task task, unsigned int n) + noexcept(noexcept(Task{std::move(task)})): + task{std::move(task)}, n{n} {} + + template + constexpr Farm(Farm const& o) + noexcept(noexcept(Task{o.task})): + task{o.task}, n{o.n} {} + + template + constexpr Farm(Farm&& o) + noexcept(noexcept(Task{std::move(o.task)})): + task{std::move(o.task)}, n{std::move(o.n)} {} + + using Signature = Signature_; + + using Struct = S; + using Links = L; + + template + constexpr void setup(S& skeleton) const { + skeleton.n = n; + setupFor(task, skeleton.task); + } + + template + constexpr auto link() const&& { + return Farm{std::move(*this)}; + } + + template + constexpr auto link() const& { + return Farm{*this}; + } + + template>* = nullptr> + constexpr auto select(Rhs const& rhs) { + return FarmSel(), Task, Rhs>{task, rhs, n}; + } +}; + +template>* = nullptr> +constexpr auto farm(Task const& task, unsigned int n = 0) { + return Farm{task, n}; +} + +template>* = nullptr> +constexpr auto operator*(Rhs const& rhs) { + return Farm{rhs, 0}; +} + +template>* = nullptr> +constexpr auto operator*(unsigned int n, Rhs const& rhs) { + return Farm{rhs, n}; +} + +template>* = nullptr> +constexpr auto operator*(Lhs const& lhs, unsigned int n) { + return Farm{lhs, n}; +} + +} +} + +#endif diff --git a/inc/alsk/edsl/op/impl/farmsel.h b/inc/alsk/edsl/op/impl/farmsel.h new file mode 100644 index 0000000..30e6b5a --- /dev/null +++ b/inc/alsk/edsl/op/impl/farmsel.h @@ -0,0 +1,70 @@ +#ifndef ALSK_ALSK_EDSL_OP_IMPL_FARMSEL_H +#define ALSK_ALSK_EDSL_OP_IMPL_FARMSEL_H + +#include + +#include "../traits.h" +#include "../../../skeleton/bone/farmsel.h" +#include "../../../skeleton/struct/struct.h" +#include "../../../skeleton/link/link.h" + +namespace alsk { +namespace edsl { + +template struct Farm; + +template +struct FarmSel: OperandBase { + Task task; + Select select; + unsigned int n; + + constexpr FarmSel(Task task, Select select, unsigned int n) + noexcept(noexcept(Task{std::move(task)}) and noexcept(Select{std::move(select)})): + task{std::move(task)}, select{std::move(select)}, n{n} + {} + + template + constexpr FarmSel(FarmSel const& o) + noexcept(noexcept(Task{o.task}) and noexcept(Select{o.select})): + task{o.task}, select{o.select}, n{o.n} + {} + + template + constexpr FarmSel(FarmSel&& o) + noexcept(noexcept(Task{std::move(o.task)}) and noexcept(Select{std::move(o.select)})): + task{std::move(o.task)}, select{std::move(o.select)}, n{std::move(o.n)} + {} + + using Signature = Signature_; + + using Struct = S; + using Links = L; + + template + constexpr void setup(S& skeleton) const { + skeleton.n = n; + setupFor(task, skeleton.task); + setupFor(select, skeleton.select); + } + + template + constexpr auto link() const&& { + return FarmSel{std::move(*this)}; + } + + template + constexpr auto link() const& { + return FarmSel{*this}; + } +}; + +template>* = nullptr> +constexpr auto operator->*(Farm const& farm, Rhs const& rhs) { + return FarmSel(FArgs...), Task, Rhs>{farm.task, rhs, farm.n}; +} + +} +} + +#endif diff --git a/inc/alsk/edsl/op/impl/itersel.h b/inc/alsk/edsl/op/impl/itersel.h new file mode 100644 index 0000000..ffa603d --- /dev/null +++ b/inc/alsk/edsl/op/impl/itersel.h @@ -0,0 +1,99 @@ +#ifndef ALSK_ALSK_EDSL_OP_IMPL_ITERSEL_H +#define ALSK_ALSK_EDSL_OP_IMPL_ITERSEL_H + +#include + +#include "../traits.h" +#include "../../../skeleton/bone/itersel.h" +#include "../../../skeleton/struct/struct.h" +#include "../../../skeleton/link/link.h" + +namespace alsk { +namespace edsl { + +template struct Loop; +template struct FarmSel; + +template +struct IterSel: OperandBase { + Task task; + Select select; + unsigned int n; + + constexpr IterSel(Task task, Select select, unsigned int n) + noexcept(noexcept(Task{std::move(task)}) and noexcept(Select{std::move(select)})): + task{std::move(task)}, select{std::move(select)}, n{n} + {} + + template + constexpr IterSel(IterSel const& o) + noexcept(noexcept(Task{o.task}) and noexcept(Select{o.select})): + task{o.task}, select{o.select}, n{o.n} + {} + + template + constexpr IterSel(IterSel&& o) + noexcept(noexcept(Task{std::move(o.task)}) and noexcept(Select{std::move(o.select)})): + task{std::move(o.task)}, select{std::move(o.select)}, n{std::move(o.n)} + {} + + using Signature = Signature_; + + using Struct = S; + using Links = L; + + template + constexpr void setup(S& skeleton) const { + skeleton.n = n; + setupFor(task, skeleton.task); + setupFor(select, skeleton.select); + } + + template + constexpr auto link() const&& { + return IterSel{std::move(*this)}; + } + + template + constexpr auto link() const& { + return IterSel{*this}; + } +}; + +namespace impl { + +template struct Iter; + +template +struct Iter { + Task const& task; + unsigned int n; + + template>* = nullptr> + constexpr auto select(Select const& select) { + using Signature = GetReturnType( + *this, skeleton.select, tupleP, std::tuple<>{}, std::move(current), std::move(best) + ); + } + + return best; + } +}; +*/ + +/** + * @brief FarmSel implementation for parallel execution + */ +template +struct Impl, Tag, Executor, State>: +BoneImplBase, Tag, Executor, State> +{ + using This = Impl; + using Task = Execute; + using Select = Execute; + + typename This::Skeleton skeleton; + typename This::Executor executor; + typename This::StateRef state; + + constexpr Impl() = default; + template + constexpr Impl(S&& skeleton, Executor executor, State& state): + skeleton{std::forward(skeleton)}, + executor{executor}, + state{state} + {} + + constexpr typename This::Return operator()(Args... args) { + auto tupleP = std::forward_as_tuple(args...); + return executor.template executeParallelAccumulate( + *this, skeleton.task, skeleton.select, tupleP, skeleton.n + ); + } +}; + +} + +#endif diff --git a/inc/alsk/impl/bone/itersel.h b/inc/alsk/impl/bone/itersel.h new file mode 100644 index 0000000..226c573 --- /dev/null +++ b/inc/alsk/impl/bone/itersel.h @@ -0,0 +1,57 @@ +#ifndef ALSK_ALSK_IMPL_BONE_ITERSEL_H +#define ALSK_ALSK_IMPL_BONE_ITERSEL_H + +#include +#include +#include + +#include "../boneimplbase.h" +#include "../../skeleton/bone/itersel.h" + +namespace alsk { + +/** + * @brief IterSel implementation for any execution + */ +template +struct Impl, Tag, Executor, State>: +BoneImplBase, Tag, Executor, State> +{ + using This = Impl; + using Task = Execute; + using Select = Execute; + + typename This::Skeleton skeleton; + typename This::Executor executor; + typename This::StateRef state; + + constexpr Impl() = default; + template + constexpr Impl(S&& skeleton, O&& executor, State& state): + skeleton{std::forward(skeleton)}, + executor{std::forward(executor)}, + state{state} + {} + + // TODO check repeatability with tag::Parallel + constexpr typename This::Return operator()(Args... args) { + using Value = typename This::Return; + + auto tupleP = std::forward_as_tuple(args...); + + Value best = ArgGet>::get(tupleP, std::tuple<>{}, std::tuple<>{}); + + for(std::size_t i = 0; i < skeleton.n; ++i) { + Value current = executor.template execute(*this, skeleton.task, tupleP, std::tuple<>{}, best); + best = executor.template execute