| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| # How to contribute # | ||
|
|
||
| We'd love to accept your patches and contributions to this project. There are | ||
| a just a few small guidelines you need to follow. | ||
|
|
||
|
|
||
| ## Contributor License Agreement ## | ||
|
|
||
| Contributions to any Google project must be accompanied by a Contributor | ||
| License Agreement. This is not a copyright **assignment**, it simply gives | ||
| Google permission to use and redistribute your contributions as part of the | ||
| project. | ||
|
|
||
| * If you are an individual writing original source code and you're sure you | ||
| own the intellectual property, then you'll need to sign an [individual | ||
| CLA][]. | ||
|
|
||
| * If you work for a company that wants to allow you to contribute your work, | ||
| then you'll need to sign a [corporate CLA][]. | ||
|
|
||
| You generally only need to submit a CLA once, so if you've already submitted | ||
| one (even if it was for a different project), you probably don't need to do it | ||
| again. | ||
|
|
||
| [individual CLA]: https://developers.google.com/open-source/cla/individual | ||
| [corporate CLA]: https://developers.google.com/open-source/cla/corporate | ||
|
|
||
| Once your CLA is submitted (or if you already submitted one for | ||
| another Google project), make a commit adding yourself to the | ||
| [AUTHORS][] and [CONTRIBUTORS][] files. This commit can be part | ||
| of your first [pull request][]. | ||
|
|
||
| [AUTHORS]: AUTHORS | ||
| [CONTRIBUTORS]: CONTRIBUTORS | ||
|
|
||
|
|
||
| ## Submitting a patch ## | ||
|
|
||
| 1. It's generally best to start by opening a new issue describing the bug or | ||
| feature you're intending to fix. Even if you think it's relatively minor, | ||
| it's helpful to know what people are working on. Mention in the initial | ||
| issue that you are planning to work on that bug or feature so that it can | ||
| be assigned to you. | ||
|
|
||
| 1. Follow the normal process of [forking][] the project, and setup a new | ||
| branch to work in. It's important that each group of changes be done in | ||
| separate branches in order to ensure that a pull request only includes the | ||
| commits related to that bug or feature. | ||
|
|
||
| 1. Do your best to have [well-formed commit messages][] for each change. | ||
| This provides consistency throughout the project, and ensures that commit | ||
| messages are able to be formatted properly by various git tools. | ||
|
|
||
| 1. Finally, push the commits to your fork and submit a [pull request][]. | ||
|
|
||
| [forking]: https://help.github.com/articles/fork-a-repo | ||
| [well-formed commit messages]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html | ||
| [pull request]: https://help.github.com/articles/creating-a-pull-request |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| # People who have agreed to one of the CLAs and can contribute patches. | ||
| # The AUTHORS file lists the copyright holders; this file | ||
| # lists people. For example, Google employees are listed here | ||
| # but not in AUTHORS, because Google holds the copyright. | ||
| # | ||
| # Names should be added to this file only after verifying that | ||
| # the individual or the individual's organization has agreed to | ||
| # the appropriate Contributor License Agreement, found here: | ||
| # | ||
| # https://developers.google.com/open-source/cla/individual | ||
| # https://developers.google.com/open-source/cla/corporate | ||
| # | ||
| # The agreement for individuals can be filled out on the web. | ||
| # | ||
| # When adding J Random Contributor's name to this file, | ||
| # either J's name or J's organization's name should be | ||
| # added to the AUTHORS file, depending on whether the | ||
| # individual or corporate CLA was used. | ||
| # | ||
| # Names should be added to this file as: | ||
| # Name <email address> | ||
| # | ||
| # Please keep the list sorted. | ||
|
|
||
| Albert Pretorius <pretoalb@gmail.com> | ||
| Arne Beer <arne@twobeer.de> | ||
| Billy Robert O'Neal III <billy.oneal@gmail.com> <bion@microsoft.com> | ||
| Chris Kennelly <ckennelly@google.com> <ckennelly@ckennelly.com> | ||
| Christopher Seymour <chris.j.seymour@hotmail.com> | ||
| David Coeurjolly <david.coeurjolly@liris.cnrs.fr> | ||
| Deniz Evrenci <denizevrenci@gmail.com> | ||
| Dominic Hamon <dma@stripysock.com> <dominic@google.com> | ||
| Dominik Czarnota <dominik.b.czarnota@gmail.com> | ||
| Eric Fiselier <eric@efcs.ca> | ||
| Eugene Zhuk <eugene.zhuk@gmail.com> | ||
| Evgeny Safronov <division494@gmail.com> | ||
| Felix Homann <linuxaudio@showlabor.de> | ||
| Ismael Jimenez Martinez <ismael.jimenez.martinez@gmail.com> | ||
| Jern-Kuan Leong <jernkuan@gmail.com> | ||
| JianXiong Zhou <zhoujianxiong2@gmail.com> | ||
| Joao Paulo Magalhaes <joaoppmagalhaes@gmail.com> | ||
| John Millikin <jmillikin@stripe.com> | ||
| Jussi Knuuttila <jussi.knuuttila@gmail.com> | ||
| Kai Wolf <kai.wolf@gmail.com> | ||
| Kishan Kumar <kumar.kishan@outlook.com> | ||
| Kaito Udagawa <umireon@gmail.com> | ||
| Lei Xu <eddyxu@gmail.com> | ||
| Matt Clarkson <mattyclarkson@gmail.com> | ||
| Maxim Vafin <maxvafin@gmail.com> | ||
| Nick Hutchinson <nshutchinson@gmail.com> | ||
| Oleksandr Sochka <sasha.sochka@gmail.com> | ||
| Pascal Leroy <phl@google.com> | ||
| Paul Redmond <paul.redmond@gmail.com> | ||
| Pierre Phaneuf <pphaneuf@google.com> | ||
| Radoslav Yovchev <radoslav.tm@gmail.com> | ||
| Raul Marin <rmrodriguez@cartodb.com> | ||
| Ray Glover <ray.glover@uk.ibm.com> | ||
| Robert Guo <robert.guo@mongodb.com> | ||
| Roman Lebedev <lebedev.ri@gmail.com> | ||
| Shuo Chen <chenshuo@chenshuo.com> | ||
| Tobias Ulvgård <tobias.ulvgard@dirac.se> | ||
| Tom Madams <tom.ej.madams@gmail.com> <tmadams@google.com> | ||
| Yixuan Qiu <yixuanq@gmail.com> | ||
| Yusuke Suzuki <utatane.tea@gmail.com> | ||
| Zbigniew Skowron <zbychs@gmail.com> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,202 @@ | ||
|
|
||
| Apache License | ||
| Version 2.0, January 2004 | ||
| http://www.apache.org/licenses/ | ||
|
|
||
| TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION | ||
|
|
||
| 1. Definitions. | ||
|
|
||
| "License" shall mean the terms and conditions for use, reproduction, | ||
| and distribution as defined by Sections 1 through 9 of this document. | ||
|
|
||
| "Licensor" shall mean the copyright owner or entity authorized by | ||
| the copyright owner that is granting the License. | ||
|
|
||
| "Legal Entity" shall mean the union of the acting entity and all | ||
| other entities that control, are controlled by, or are under common | ||
| control with that entity. For the purposes of this definition, | ||
| "control" means (i) the power, direct or indirect, to cause the | ||
| direction or management of such entity, whether by contract or | ||
| otherwise, or (ii) ownership of fifty percent (50%) or more of the | ||
| outstanding shares, or (iii) beneficial ownership of such entity. | ||
|
|
||
| "You" (or "Your") shall mean an individual or Legal Entity | ||
| exercising permissions granted by this License. | ||
|
|
||
| "Source" form shall mean the preferred form for making modifications, | ||
| including but not limited to software source code, documentation | ||
| source, and configuration files. | ||
|
|
||
| "Object" form shall mean any form resulting from mechanical | ||
| transformation or translation of a Source form, including but | ||
| not limited to compiled object code, generated documentation, | ||
| and conversions to other media types. | ||
|
|
||
| "Work" shall mean the work of authorship, whether in Source or | ||
| Object form, made available under the License, as indicated by a | ||
| copyright notice that is included in or attached to the work | ||
| (an example is provided in the Appendix below). | ||
|
|
||
| "Derivative Works" shall mean any work, whether in Source or Object | ||
| form, that is based on (or derived from) the Work and for which the | ||
| editorial revisions, annotations, elaborations, or other modifications | ||
| represent, as a whole, an original work of authorship. For the purposes | ||
| of this License, Derivative Works shall not include works that remain | ||
| separable from, or merely link (or bind by name) to the interfaces of, | ||
| the Work and Derivative Works thereof. | ||
|
|
||
| "Contribution" shall mean any work of authorship, including | ||
| the original version of the Work and any modifications or additions | ||
| to that Work or Derivative Works thereof, that is intentionally | ||
| submitted to Licensor for inclusion in the Work by the copyright owner | ||
| or by an individual or Legal Entity authorized to submit on behalf of | ||
| the copyright owner. For the purposes of this definition, "submitted" | ||
| means any form of electronic, verbal, or written communication sent | ||
| to the Licensor or its representatives, including but not limited to | ||
| communication on electronic mailing lists, source code control systems, | ||
| and issue tracking systems that are managed by, or on behalf of, the | ||
| Licensor for the purpose of discussing and improving the Work, but | ||
| excluding communication that is conspicuously marked or otherwise | ||
| designated in writing by the copyright owner as "Not a Contribution." | ||
|
|
||
| "Contributor" shall mean Licensor and any individual or Legal Entity | ||
| on behalf of whom a Contribution has been received by Licensor and | ||
| subsequently incorporated within the Work. | ||
|
|
||
| 2. Grant of Copyright License. Subject to the terms and conditions of | ||
| this License, each Contributor hereby grants to You a perpetual, | ||
| worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||
| copyright license to reproduce, prepare Derivative Works of, | ||
| publicly display, publicly perform, sublicense, and distribute the | ||
| Work and such Derivative Works in Source or Object form. | ||
|
|
||
| 3. Grant of Patent License. Subject to the terms and conditions of | ||
| this License, each Contributor hereby grants to You a perpetual, | ||
| worldwide, non-exclusive, no-charge, royalty-free, irrevocable | ||
| (except as stated in this section) patent license to make, have made, | ||
| use, offer to sell, sell, import, and otherwise transfer the Work, | ||
| where such license applies only to those patent claims licensable | ||
| by such Contributor that are necessarily infringed by their | ||
| Contribution(s) alone or by combination of their Contribution(s) | ||
| with the Work to which such Contribution(s) was submitted. If You | ||
| institute patent litigation against any entity (including a | ||
| cross-claim or counterclaim in a lawsuit) alleging that the Work | ||
| or a Contribution incorporated within the Work constitutes direct | ||
| or contributory patent infringement, then any patent licenses | ||
| granted to You under this License for that Work shall terminate | ||
| as of the date such litigation is filed. | ||
|
|
||
| 4. Redistribution. You may reproduce and distribute copies of the | ||
| Work or Derivative Works thereof in any medium, with or without | ||
| modifications, and in Source or Object form, provided that You | ||
| meet the following conditions: | ||
|
|
||
| (a) You must give any other recipients of the Work or | ||
| Derivative Works a copy of this License; and | ||
|
|
||
| (b) You must cause any modified files to carry prominent notices | ||
| stating that You changed the files; and | ||
|
|
||
| (c) You must retain, in the Source form of any Derivative Works | ||
| that You distribute, all copyright, patent, trademark, and | ||
| attribution notices from the Source form of the Work, | ||
| excluding those notices that do not pertain to any part of | ||
| the Derivative Works; and | ||
|
|
||
| (d) If the Work includes a "NOTICE" text file as part of its | ||
| distribution, then any Derivative Works that You distribute must | ||
| include a readable copy of the attribution notices contained | ||
| within such NOTICE file, excluding those notices that do not | ||
| pertain to any part of the Derivative Works, in at least one | ||
| of the following places: within a NOTICE text file distributed | ||
| as part of the Derivative Works; within the Source form or | ||
| documentation, if provided along with the Derivative Works; or, | ||
| within a display generated by the Derivative Works, if and | ||
| wherever such third-party notices normally appear. The contents | ||
| of the NOTICE file are for informational purposes only and | ||
| do not modify the License. You may add Your own attribution | ||
| notices within Derivative Works that You distribute, alongside | ||
| or as an addendum to the NOTICE text from the Work, provided | ||
| that such additional attribution notices cannot be construed | ||
| as modifying the License. | ||
|
|
||
| You may add Your own copyright statement to Your modifications and | ||
| may provide additional or different license terms and conditions | ||
| for use, reproduction, or distribution of Your modifications, or | ||
| for any such Derivative Works as a whole, provided Your use, | ||
| reproduction, and distribution of the Work otherwise complies with | ||
| the conditions stated in this License. | ||
|
|
||
| 5. Submission of Contributions. Unless You explicitly state otherwise, | ||
| any Contribution intentionally submitted for inclusion in the Work | ||
| by You to the Licensor shall be under the terms and conditions of | ||
| this License, without any additional terms or conditions. | ||
| Notwithstanding the above, nothing herein shall supersede or modify | ||
| the terms of any separate license agreement you may have executed | ||
| with Licensor regarding such Contributions. | ||
|
|
||
| 6. Trademarks. This License does not grant permission to use the trade | ||
| names, trademarks, service marks, or product names of the Licensor, | ||
| except as required for reasonable and customary use in describing the | ||
| origin of the Work and reproducing the content of the NOTICE file. | ||
|
|
||
| 7. Disclaimer of Warranty. Unless required by applicable law or | ||
| agreed to in writing, Licensor provides the Work (and each | ||
| Contributor provides its Contributions) on an "AS IS" BASIS, | ||
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | ||
| implied, including, without limitation, any warranties or conditions | ||
| of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A | ||
| PARTICULAR PURPOSE. You are solely responsible for determining the | ||
| appropriateness of using or redistributing the Work and assume any | ||
| risks associated with Your exercise of permissions under this License. | ||
|
|
||
| 8. Limitation of Liability. In no event and under no legal theory, | ||
| whether in tort (including negligence), contract, or otherwise, | ||
| unless required by applicable law (such as deliberate and grossly | ||
| negligent acts) or agreed to in writing, shall any Contributor be | ||
| liable to You for damages, including any direct, indirect, special, | ||
| incidental, or consequential damages of any character arising as a | ||
| result of this License or out of the use or inability to use the | ||
| Work (including but not limited to damages for loss of goodwill, | ||
| work stoppage, computer failure or malfunction, or any and all | ||
| other commercial damages or losses), even if such Contributor | ||
| has been advised of the possibility of such damages. | ||
|
|
||
| 9. Accepting Warranty or Additional Liability. While redistributing | ||
| the Work or Derivative Works thereof, You may choose to offer, | ||
| and charge a fee for, acceptance of support, warranty, indemnity, | ||
| or other liability obligations and/or rights consistent with this | ||
| License. However, in accepting such obligations, You may act only | ||
| on Your own behalf and on Your sole responsibility, not on behalf | ||
| of any other Contributor, and only if You agree to indemnify, | ||
| defend, and hold each Contributor harmless for any liability | ||
| incurred by, or claims asserted against, such Contributor by reason | ||
| of your accepting any such warranty or additional liability. | ||
|
|
||
| END OF TERMS AND CONDITIONS | ||
|
|
||
| APPENDIX: How to apply the Apache License to your work. | ||
|
|
||
| To apply the Apache License to your work, attach the following | ||
| boilerplate notice, with the fields enclosed by brackets "[]" | ||
| replaced with your own identifying information. (Don't include | ||
| the brackets!) The text should be enclosed in the appropriate | ||
| comment syntax for the file format. We also recommend that a | ||
| file or class name and description of purpose be included on the | ||
| same "printed page" as the copyright notice for easier | ||
| identification within third-party archives. | ||
|
|
||
| Copyright [yyyy] [name of copyright owner] | ||
|
|
||
| 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. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| LLVM notes | ||
| ---------- | ||
|
|
||
| This directory contains the Google Benchmark source code. This directory is | ||
| under a different license than LLVM. | ||
|
|
||
| Changes: | ||
|
|
||
| * Bazel BUILD files are removed from the library |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| workspace(name = "com_github_google_benchmark") | ||
|
|
||
| http_archive( | ||
| name = "com_google_googletest", | ||
| urls = ["https://github.com/google/googletest/archive/3f0cf6b62ad1eb50d8736538363d3580dd640c3e.zip"], | ||
| strip_prefix = "googletest-3f0cf6b62ad1eb50d8736538363d3580dd640c3e", | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| version: '{build}' | ||
|
|
||
| image: Visual Studio 2017 | ||
|
|
||
| configuration: | ||
| - Debug | ||
| - Release | ||
|
|
||
| environment: | ||
| matrix: | ||
| - compiler: msvc-15-seh | ||
| generator: "Visual Studio 15 2017" | ||
|
|
||
| - compiler: msvc-15-seh | ||
| generator: "Visual Studio 15 2017 Win64" | ||
|
|
||
| - compiler: msvc-14-seh | ||
| generator: "Visual Studio 14 2015" | ||
|
|
||
| - compiler: msvc-14-seh | ||
| generator: "Visual Studio 14 2015 Win64" | ||
|
|
||
| - compiler: msvc-12-seh | ||
| generator: "Visual Studio 12 2013" | ||
|
|
||
| - compiler: msvc-12-seh | ||
| generator: "Visual Studio 12 2013 Win64" | ||
|
|
||
| - compiler: gcc-5.3.0-posix | ||
| generator: "MinGW Makefiles" | ||
| cxx_path: 'C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin' | ||
| APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015 | ||
|
|
||
| matrix: | ||
| fast_finish: true | ||
|
|
||
| install: | ||
| # git bash conflicts with MinGW makefiles | ||
| - if "%generator%"=="MinGW Makefiles" (set "PATH=%PATH:C:\Program Files\Git\usr\bin;=%") | ||
| - if not "%cxx_path%"=="" (set "PATH=%PATH%;%cxx_path%") | ||
|
|
||
| build_script: | ||
| - md _build -Force | ||
| - cd _build | ||
| - echo %configuration% | ||
| - cmake -G "%generator%" "-DCMAKE_BUILD_TYPE=%configuration%" -DBENCHMARK_DOWNLOAD_DEPENDENCIES=ON .. | ||
| - cmake --build . --config %configuration% | ||
|
|
||
| test_script: | ||
| - ctest -c %configuration% --timeout 300 --output-on-failure | ||
|
|
||
| artifacts: | ||
| - path: '_build/CMakeFiles/*.log' | ||
| name: logs | ||
| - path: '_build/Testing/**/*.xml' | ||
| name: test_results |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| # - Adds a compiler flag if it is supported by the compiler | ||
| # | ||
| # This function checks that the supplied compiler flag is supported and then | ||
| # adds it to the corresponding compiler flags | ||
| # | ||
| # add_cxx_compiler_flag(<FLAG> [<VARIANT>]) | ||
| # | ||
| # - Example | ||
| # | ||
| # include(AddCXXCompilerFlag) | ||
| # add_cxx_compiler_flag(-Wall) | ||
| # add_cxx_compiler_flag(-no-strict-aliasing RELEASE) | ||
| # Requires CMake 2.6+ | ||
|
|
||
| if(__add_cxx_compiler_flag) | ||
| return() | ||
| endif() | ||
| set(__add_cxx_compiler_flag INCLUDED) | ||
|
|
||
| include(CheckCXXCompilerFlag) | ||
|
|
||
| function(mangle_compiler_flag FLAG OUTPUT) | ||
| string(TOUPPER "HAVE_CXX_FLAG_${FLAG}" SANITIZED_FLAG) | ||
| string(REPLACE "+" "X" SANITIZED_FLAG ${SANITIZED_FLAG}) | ||
| string(REGEX REPLACE "[^A-Za-z_0-9]" "_" SANITIZED_FLAG ${SANITIZED_FLAG}) | ||
| string(REGEX REPLACE "_+" "_" SANITIZED_FLAG ${SANITIZED_FLAG}) | ||
| set(${OUTPUT} "${SANITIZED_FLAG}" PARENT_SCOPE) | ||
| endfunction(mangle_compiler_flag) | ||
|
|
||
| function(add_cxx_compiler_flag FLAG) | ||
| mangle_compiler_flag("${FLAG}" MANGLED_FLAG) | ||
| set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") | ||
| set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}") | ||
| check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG}) | ||
| set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}") | ||
| if(${MANGLED_FLAG}) | ||
| set(VARIANT ${ARGV1}) | ||
| if(ARGV1) | ||
| string(TOUPPER "_${VARIANT}" VARIANT) | ||
| endif() | ||
| set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${BENCHMARK_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE) | ||
| endif() | ||
| endfunction() | ||
|
|
||
| function(add_required_cxx_compiler_flag FLAG) | ||
| mangle_compiler_flag("${FLAG}" MANGLED_FLAG) | ||
| set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") | ||
| set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}") | ||
| check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG}) | ||
| set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}") | ||
| if(${MANGLED_FLAG}) | ||
| set(VARIANT ${ARGV1}) | ||
| if(ARGV1) | ||
| string(TOUPPER "_${VARIANT}" VARIANT) | ||
| endif() | ||
| set(CMAKE_CXX_FLAGS${VARIANT} "${CMAKE_CXX_FLAGS${VARIANT}} ${FLAG}" PARENT_SCOPE) | ||
| set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE) | ||
| set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE) | ||
| set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${FLAG}" PARENT_SCOPE) | ||
| set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} ${FLAG}" PARENT_SCOPE) | ||
| else() | ||
| message(FATAL_ERROR "Required flag '${FLAG}' is not supported by the compiler") | ||
| endif() | ||
| endfunction() | ||
|
|
||
| function(check_cxx_warning_flag FLAG) | ||
| mangle_compiler_flag("${FLAG}" MANGLED_FLAG) | ||
| set(OLD_CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS}") | ||
| # Add -Werror to ensure the compiler generates an error if the warning flag | ||
| # doesn't exist. | ||
| set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror ${FLAG}") | ||
| check_cxx_compiler_flag("${FLAG}" ${MANGLED_FLAG}) | ||
| set(CMAKE_REQUIRED_FLAGS "${OLD_CMAKE_REQUIRED_FLAGS}") | ||
| endfunction() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| # - Compile and run code to check for C++ features | ||
| # | ||
| # This functions compiles a source file under the `cmake` folder | ||
| # and adds the corresponding `HAVE_[FILENAME]` flag to the CMake | ||
| # environment | ||
| # | ||
| # cxx_feature_check(<FLAG> [<VARIANT>]) | ||
| # | ||
| # - Example | ||
| # | ||
| # include(CXXFeatureCheck) | ||
| # cxx_feature_check(STD_REGEX) | ||
| # Requires CMake 2.8.12+ | ||
|
|
||
| if(__cxx_feature_check) | ||
| return() | ||
| endif() | ||
| set(__cxx_feature_check INCLUDED) | ||
|
|
||
| function(cxx_feature_check FILE) | ||
| string(TOLOWER ${FILE} FILE) | ||
| string(TOUPPER ${FILE} VAR) | ||
| string(TOUPPER "HAVE_${VAR}" FEATURE) | ||
| if (DEFINED HAVE_${VAR}) | ||
| set(HAVE_${VAR} 1 PARENT_SCOPE) | ||
| add_definitions(-DHAVE_${VAR}) | ||
| return() | ||
| endif() | ||
|
|
||
| if (NOT DEFINED COMPILE_${FEATURE}) | ||
| message("-- Performing Test ${FEATURE}") | ||
| if(CMAKE_CROSSCOMPILING) | ||
| try_compile(COMPILE_${FEATURE} | ||
| ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${FILE}.cpp | ||
| CMAKE_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS} | ||
| LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES}) | ||
| if(COMPILE_${FEATURE}) | ||
| message(WARNING | ||
| "If you see build failures due to cross compilation, try setting HAVE_${VAR} to 0") | ||
| set(RUN_${FEATURE} 0) | ||
| else() | ||
| set(RUN_${FEATURE} 1) | ||
| endif() | ||
| else() | ||
| message("-- Performing Test ${FEATURE}") | ||
| try_run(RUN_${FEATURE} COMPILE_${FEATURE} | ||
| ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${FILE}.cpp | ||
| CMAKE_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS} | ||
| LINK_LIBRARIES ${BENCHMARK_CXX_LIBRARIES}) | ||
| endif() | ||
| endif() | ||
|
|
||
| if(RUN_${FEATURE} EQUAL 0) | ||
| message("-- Performing Test ${FEATURE} -- success") | ||
| set(HAVE_${VAR} 1 PARENT_SCOPE) | ||
| add_definitions(-DHAVE_${VAR}) | ||
| else() | ||
| if(NOT COMPILE_${FEATURE}) | ||
| message("-- Performing Test ${FEATURE} -- failed to compile") | ||
| else() | ||
| message("-- Performing Test ${FEATURE} -- compiled but failed to run") | ||
| endif() | ||
| endif() | ||
| endfunction() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake") |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| # - Returns a version string from Git tags | ||
| # | ||
| # This function inspects the annotated git tags for the project and returns a string | ||
| # into a CMake variable | ||
| # | ||
| # get_git_version(<var>) | ||
| # | ||
| # - Example | ||
| # | ||
| # include(GetGitVersion) | ||
| # get_git_version(GIT_VERSION) | ||
| # | ||
| # Requires CMake 2.8.11+ | ||
| find_package(Git) | ||
|
|
||
| if(__get_git_version) | ||
| return() | ||
| endif() | ||
| set(__get_git_version INCLUDED) | ||
|
|
||
| function(get_git_version var) | ||
| if(GIT_EXECUTABLE) | ||
| execute_process(COMMAND ${GIT_EXECUTABLE} describe --match "v[0-9]*.[0-9]*.[0-9]*" --abbrev=8 | ||
| WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} | ||
| RESULT_VARIABLE status | ||
| OUTPUT_VARIABLE GIT_VERSION | ||
| ERROR_QUIET) | ||
| if(${status}) | ||
| set(GIT_VERSION "v0.0.0") | ||
| else() | ||
| string(STRIP ${GIT_VERSION} GIT_VERSION) | ||
| string(REGEX REPLACE "-[0-9]+-g" "-" GIT_VERSION ${GIT_VERSION}) | ||
| endif() | ||
|
|
||
| # Work out if the repository is dirty | ||
| execute_process(COMMAND ${GIT_EXECUTABLE} update-index -q --refresh | ||
| WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} | ||
| OUTPUT_QUIET | ||
| ERROR_QUIET) | ||
| execute_process(COMMAND ${GIT_EXECUTABLE} diff-index --name-only HEAD -- | ||
| WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} | ||
| OUTPUT_VARIABLE GIT_DIFF_INDEX | ||
| ERROR_QUIET) | ||
| string(COMPARE NOTEQUAL "${GIT_DIFF_INDEX}" "" GIT_DIRTY) | ||
| if (${GIT_DIRTY}) | ||
| set(GIT_VERSION "${GIT_VERSION}-dirty") | ||
| endif() | ||
| else() | ||
| set(GIT_VERSION "v0.0.0") | ||
| endif() | ||
|
|
||
| message("-- git Version: ${GIT_VERSION}") | ||
| set(${var} ${GIT_VERSION} PARENT_SCOPE) | ||
| endfunction() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,113 @@ | ||
|
|
||
| include(split_list) | ||
|
|
||
| macro(build_external_gtest) | ||
| include(ExternalProject) | ||
| set(GTEST_FLAGS "") | ||
| if (BENCHMARK_USE_LIBCXX) | ||
| if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") | ||
| list(APPEND GTEST_FLAGS -stdlib=libc++) | ||
| else() | ||
| message(WARNING "Unsupported compiler (${CMAKE_CXX_COMPILER}) when using libc++") | ||
| endif() | ||
| endif() | ||
| if (BENCHMARK_BUILD_32_BITS) | ||
| list(APPEND GTEST_FLAGS -m32) | ||
| endif() | ||
| if (NOT "${CMAKE_CXX_FLAGS}" STREQUAL "") | ||
| list(APPEND GTEST_FLAGS ${CMAKE_CXX_FLAGS}) | ||
| endif() | ||
| string(TOUPPER "${CMAKE_BUILD_TYPE}" GTEST_BUILD_TYPE) | ||
| if ("${GTEST_BUILD_TYPE}" STREQUAL "COVERAGE") | ||
| set(GTEST_BUILD_TYPE "DEBUG") | ||
| endif() | ||
| # FIXME: Since 10/Feb/2017 the googletest trunk has had a bug where | ||
| # -Werror=unused-function fires during the build on OS X. This is a temporary | ||
| # workaround to keep our travis bots from failing. It should be removed | ||
| # once gtest is fixed. | ||
| if (NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") | ||
| list(APPEND GTEST_FLAGS "-Wno-unused-function") | ||
| endif() | ||
| split_list(GTEST_FLAGS) | ||
| set(EXCLUDE_FROM_ALL_OPT "") | ||
| set(EXCLUDE_FROM_ALL_VALUE "") | ||
| if (${CMAKE_VERSION} VERSION_GREATER "3.0.99") | ||
| set(EXCLUDE_FROM_ALL_OPT "EXCLUDE_FROM_ALL") | ||
| set(EXCLUDE_FROM_ALL_VALUE "ON") | ||
| endif() | ||
| ExternalProject_Add(googletest | ||
| ${EXCLUDE_FROM_ALL_OPT} ${EXCLUDE_FROM_ALL_VALUE} | ||
| GIT_REPOSITORY https://github.com/google/googletest.git | ||
| GIT_TAG master | ||
| PREFIX "${CMAKE_BINARY_DIR}/googletest" | ||
| INSTALL_DIR "${CMAKE_BINARY_DIR}/googletest" | ||
| CMAKE_CACHE_ARGS | ||
| -DCMAKE_BUILD_TYPE:STRING=${GTEST_BUILD_TYPE} | ||
| -DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER} | ||
| -DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER} | ||
| -DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR> | ||
| -DCMAKE_INSTALL_LIBDIR:PATH=<INSTALL_DIR>/lib | ||
| -DCMAKE_CXX_FLAGS:STRING=${GTEST_FLAGS} | ||
| -Dgtest_force_shared_crt:BOOL=ON | ||
| ) | ||
|
|
||
| ExternalProject_Get_Property(googletest install_dir) | ||
| set(GTEST_INCLUDE_DIRS ${install_dir}/include) | ||
| file(MAKE_DIRECTORY ${GTEST_INCLUDE_DIRS}) | ||
|
|
||
| set(LIB_SUFFIX "${CMAKE_STATIC_LIBRARY_SUFFIX}") | ||
| set(LIB_PREFIX "${CMAKE_STATIC_LIBRARY_PREFIX}") | ||
| if("${GTEST_BUILD_TYPE}" STREQUAL "DEBUG") | ||
| set(LIB_SUFFIX "d${CMAKE_STATIC_LIBRARY_SUFFIX}") | ||
| endif() | ||
|
|
||
| # Use gmock_main instead of gtest_main because it initializes gtest as well. | ||
| # Note: The libraries are listed in reverse order of their dependancies. | ||
| foreach(LIB gtest gmock gmock_main) | ||
| add_library(${LIB} UNKNOWN IMPORTED) | ||
| set_target_properties(${LIB} PROPERTIES | ||
| IMPORTED_LOCATION ${install_dir}/lib/${LIB_PREFIX}${LIB}${LIB_SUFFIX} | ||
| INTERFACE_INCLUDE_DIRECTORIES ${GTEST_INCLUDE_DIRS} | ||
| INTERFACE_LINK_LIBRARIES "${GTEST_BOTH_LIBRARIES}" | ||
| ) | ||
| add_dependencies(${LIB} googletest) | ||
| list(APPEND GTEST_BOTH_LIBRARIES ${LIB}) | ||
| endforeach() | ||
| endmacro(build_external_gtest) | ||
|
|
||
| if (BENCHMARK_ENABLE_GTEST_TESTS) | ||
| if (IS_DIRECTORY ${CMAKE_SOURCE_DIR}/googletest) | ||
| set(GTEST_ROOT "${CMAKE_SOURCE_DIR}/googletest") | ||
| set(INSTALL_GTEST OFF CACHE INTERNAL "") | ||
| set(INSTALL_GMOCK OFF CACHE INTERNAL "") | ||
| add_subdirectory(${CMAKE_SOURCE_DIR}/googletest) | ||
| set(GTEST_BOTH_LIBRARIES gtest gmock gmock_main) | ||
| foreach(HEADER test mock) | ||
| # CMake 2.8 and older don't respect INTERFACE_INCLUDE_DIRECTORIES, so we | ||
| # have to add the paths ourselves. | ||
| set(HFILE g${HEADER}/g${HEADER}.h) | ||
| set(HPATH ${GTEST_ROOT}/google${HEADER}/include) | ||
| find_path(HEADER_PATH_${HEADER} ${HFILE} | ||
| NO_DEFAULT_PATHS | ||
| HINTS ${HPATH} | ||
| ) | ||
| if (NOT HEADER_PATH_${HEADER}) | ||
| message(FATAL_ERROR "Failed to find header ${HFILE} in ${HPATH}") | ||
| endif() | ||
| list(APPEND GTEST_INCLUDE_DIRS ${HEADER_PATH_${HEADER}}) | ||
| endforeach() | ||
| elseif(BENCHMARK_DOWNLOAD_DEPENDENCIES) | ||
| build_external_gtest() | ||
| else() | ||
| find_package(GTest REQUIRED) | ||
| find_path(GMOCK_INCLUDE_DIRS gmock/gmock.h | ||
| HINTS ${GTEST_INCLUDE_DIRS}) | ||
| if (NOT GMOCK_INCLUDE_DIRS) | ||
| message(FATAL_ERROR "Failed to find header gmock/gmock.h with hint ${GTEST_INCLUDE_DIRS}") | ||
| endif() | ||
| set(GTEST_INCLUDE_DIRS ${GTEST_INCLUDE_DIRS} ${GMOCK_INCLUDE_DIRS}) | ||
| # FIXME: We don't currently require the gmock library to build the tests, | ||
| # and it's likely we won't find it, so we don't try. As long as we've | ||
| # found the gmock/gmock.h header and gtest_main that should be good enough. | ||
| endif() | ||
| endif() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| include(FeatureSummary) | ||
|
|
||
| find_program(LLVMAR_EXECUTABLE | ||
| NAMES llvm-ar | ||
| DOC "The llvm-ar executable" | ||
| ) | ||
|
|
||
| include(FindPackageHandleStandardArgs) | ||
| find_package_handle_standard_args(LLVMAr | ||
| DEFAULT_MSG | ||
| LLVMAR_EXECUTABLE) | ||
|
|
||
| SET_PACKAGE_PROPERTIES(LLVMAr PROPERTIES | ||
| URL https://llvm.org/docs/CommandGuide/llvm-ar.html | ||
| DESCRIPTION "create, modify, and extract from archives" | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| include(FeatureSummary) | ||
|
|
||
| find_program(LLVMNM_EXECUTABLE | ||
| NAMES llvm-nm | ||
| DOC "The llvm-nm executable" | ||
| ) | ||
|
|
||
| include(FindPackageHandleStandardArgs) | ||
| find_package_handle_standard_args(LLVMNm | ||
| DEFAULT_MSG | ||
| LLVMNM_EXECUTABLE) | ||
|
|
||
| SET_PACKAGE_PROPERTIES(LLVMNm PROPERTIES | ||
| URL https://llvm.org/docs/CommandGuide/llvm-nm.html | ||
| DESCRIPTION "list LLVM bitcode and object file’s symbol table" | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| include(FeatureSummary) | ||
|
|
||
| find_program(LLVMRANLIB_EXECUTABLE | ||
| NAMES llvm-ranlib | ||
| DOC "The llvm-ranlib executable" | ||
| ) | ||
|
|
||
| include(FindPackageHandleStandardArgs) | ||
| find_package_handle_standard_args(LLVMRanLib | ||
| DEFAULT_MSG | ||
| LLVMRANLIB_EXECUTABLE) | ||
|
|
||
| SET_PACKAGE_PROPERTIES(LLVMRanLib PROPERTIES | ||
| DESCRIPTION "generate index for LLVM archive" | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| prefix=@CMAKE_INSTALL_PREFIX@ | ||
| exec_prefix=${prefix} | ||
| libdir=${prefix}/lib | ||
| includedir=${prefix}/include | ||
|
|
||
| Name: @PROJECT_NAME@ | ||
| Description: Google microbenchmark framework | ||
| Version: @VERSION@ | ||
|
|
||
| Libs: -L${libdir} -lbenchmark | ||
| Cflags: -I${includedir} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| #include <gnuregex.h> | ||
| #include <string> | ||
| int main() { | ||
| std::string str = "test0159"; | ||
| regex_t re; | ||
| int ec = regcomp(&re, "^[a-z]+[0-9]+$", REG_EXTENDED | REG_NOSUB); | ||
| if (ec != 0) { | ||
| return ec; | ||
| } | ||
| return regexec(&re, str.c_str(), 0, nullptr, 0) ? -1 : 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| find_package(LLVMAr REQUIRED) | ||
| set(CMAKE_AR "${LLVMAR_EXECUTABLE}" CACHE FILEPATH "" FORCE) | ||
|
|
||
| find_package(LLVMNm REQUIRED) | ||
| set(CMAKE_NM "${LLVMNM_EXECUTABLE}" CACHE FILEPATH "" FORCE) | ||
|
|
||
| find_package(LLVMRanLib REQUIRED) | ||
| set(CMAKE_RANLIB "${LLVMRANLIB_EXECUTABLE}" CACHE FILEPATH "" FORCE) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| #include <regex.h> | ||
| #include <string> | ||
| int main() { | ||
| std::string str = "test0159"; | ||
| regex_t re; | ||
| int ec = regcomp(&re, "^[a-z]+[0-9]+$", REG_EXTENDED | REG_NOSUB); | ||
| if (ec != 0) { | ||
| return ec; | ||
| } | ||
| int ret = regexec(&re, str.c_str(), 0, nullptr, 0) ? -1 : 0; | ||
| regfree(&re); | ||
| return ret; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| macro(split_list listname) | ||
| string(REPLACE ";" " " ${listname} "${${listname}}") | ||
| endmacro() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| #include <regex> | ||
| #include <string> | ||
| int main() { | ||
| const std::string str = "test0159"; | ||
| std::regex re; | ||
| re = std::regex("^[a-z]+[0-9]+$", | ||
| std::regex_constants::extended | std::regex_constants::nosubs); | ||
| return std::regex_search(str, re) ? 0 : -1; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| #include <chrono> | ||
|
|
||
| int main() { | ||
| typedef std::chrono::steady_clock Clock; | ||
| Clock::time_point tp = Clock::now(); | ||
| ((void)tp); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| #define HAVE_THREAD_SAFETY_ATTRIBUTES | ||
| #include "../src/mutex.h" | ||
|
|
||
| int main() {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| # Assembly Tests | ||
|
|
||
| The Benchmark library provides a number of functions whose primary | ||
| purpose in to affect assembly generation, including `DoNotOptimize` | ||
| and `ClobberMemory`. In addition there are other functions, | ||
| such as `KeepRunning`, for which generating good assembly is paramount. | ||
|
|
||
| For these functions it's important to have tests that verify the | ||
| correctness and quality of the implementation. This requires testing | ||
| the code generated by the compiler. | ||
|
|
||
| This document describes how the Benchmark library tests compiler output, | ||
| as well as how to properly write new tests. | ||
|
|
||
|
|
||
| ## Anatomy of a Test | ||
|
|
||
| Writing a test has two steps: | ||
|
|
||
| * Write the code you want to generate assembly for. | ||
| * Add `// CHECK` lines to match against the verified assembly. | ||
|
|
||
| Example: | ||
| ```c++ | ||
|
|
||
| // CHECK-LABEL: test_add: | ||
| extern "C" int test_add() { | ||
| extern int ExternInt; | ||
| return ExternInt + 1; | ||
|
|
||
| // CHECK: movl ExternInt(%rip), %eax | ||
| // CHECK: addl %eax | ||
| // CHECK: ret | ||
| } | ||
|
|
||
| ``` | ||
| #### LLVM Filecheck | ||
| [LLVM's Filecheck](https://llvm.org/docs/CommandGuide/FileCheck.html) | ||
| is used to test the generated assembly against the `// CHECK` lines | ||
| specified in the tests source file. Please see the documentation | ||
| linked above for information on how to write `CHECK` directives. | ||
| #### Tips and Tricks: | ||
| * Tests should match the minimal amount of output required to establish | ||
| correctness. `CHECK` directives don't have to match on the exact next line | ||
| after the previous match, so tests should omit checks for unimportant | ||
| bits of assembly. ([`CHECK-NEXT`](https://llvm.org/docs/CommandGuide/FileCheck.html#the-check-next-directive) | ||
| can be used to ensure a match occurs exactly after the previous match). | ||
| * The tests are compiled with `-O3 -g0`. So we're only testing the | ||
| optimized output. | ||
| * The assembly output is further cleaned up using `tools/strip_asm.py`. | ||
| This removes comments, assembler directives, and unused labels before | ||
| the test is run. | ||
| * The generated and stripped assembly file for a test is output under | ||
| `<build-directory>/test/<test-name>.s` | ||
| * Filecheck supports using [`CHECK` prefixes](https://llvm.org/docs/CommandGuide/FileCheck.html#cmdoption-check-prefixes) | ||
| to specify lines that should only match in certain situations. | ||
| The Benchmark tests use `CHECK-CLANG` and `CHECK-GNU` for lines that | ||
| are only expected to match Clang or GCC's output respectively. Normal | ||
| `CHECK` lines match against all compilers. (Note: `CHECK-NOT` and | ||
| `CHECK-LABEL` are NOT prefixes. They are versions of non-prefixed | ||
| `CHECK` lines) | ||
| * Use `extern "C"` to disable name mangling for specific functions. This | ||
| makes them easier to name in the `CHECK` lines. | ||
| ## Problems Writing Portable Tests | ||
| Writing tests which check the code generated by a compiler are | ||
| inherently non-portable. Different compilers and even different compiler | ||
| versions may generate entirely different code. The Benchmark tests | ||
| must tolerate this. | ||
| LLVM Filecheck provides a number of mechanisms to help write | ||
| "more portable" tests; including [matching using regular expressions](https://llvm.org/docs/CommandGuide/FileCheck.html#filecheck-pattern-matching-syntax), | ||
| allowing the creation of [named variables](https://llvm.org/docs/CommandGuide/FileCheck.html#filecheck-variables) | ||
| for later matching, and [checking non-sequential matches](https://llvm.org/docs/CommandGuide/FileCheck.html#the-check-dag-directive). | ||
| #### Capturing Variables | ||
| For example, say GCC stores a variable in a register but Clang stores | ||
| it in memory. To write a test that tolerates both cases we "capture" | ||
| the destination of the store, and then use the captured expression | ||
| to write the remainder of the test. | ||
| ```c++ | ||
| // CHECK-LABEL: test_div_no_op_into_shr: | ||
| extern "C" void test_div_no_op_into_shr(int value) { | ||
| int divisor = 2; | ||
| benchmark::DoNotOptimize(divisor); // hide the value from the optimizer | ||
| return value / divisor; | ||
| // CHECK: movl $2, [[DEST:.*]] | ||
| // CHECK: idivl [[DEST]] | ||
| // CHECK: ret | ||
| } | ||
| ``` | ||
|
|
||
| #### Using Regular Expressions to Match Differing Output | ||
|
|
||
| Often tests require testing assembly lines which may subtly differ | ||
| between compilers or compiler versions. A common example of this | ||
| is matching stack frame addresses. In this case regular expressions | ||
| can be used to match the differing bits of output. For example: | ||
|
|
||
| ```c++ | ||
| int ExternInt; | ||
| struct Point { int x, y, z; }; | ||
|
|
||
| // CHECK-LABEL: test_store_point: | ||
| extern "C" void test_store_point() { | ||
| Point p{ExternInt, ExternInt, ExternInt}; | ||
| benchmark::DoNotOptimize(p); | ||
|
|
||
| // CHECK: movl ExternInt(%rip), %eax | ||
| // CHECK: movl %eax, -{{[0-9]+}}(%rsp) | ||
| // CHECK: movl %eax, -{{[0-9]+}}(%rsp) | ||
| // CHECK: movl %eax, -{{[0-9]+}}(%rsp) | ||
| // CHECK: ret | ||
| } | ||
| ``` | ||
| ## Current Requirements and Limitations | ||
| The tests require Filecheck to be installed along the `PATH` of the | ||
| build machine. Otherwise the tests will be disabled. | ||
| Additionally, as mentioned in the previous section, codegen tests are | ||
| inherently non-portable. Currently the tests are limited to: | ||
| * x86_64 targets. | ||
| * Compiled with GCC or Clang | ||
| Further work could be done, at least on a limited basis, to extend the | ||
| tests to other architectures and compilers (using `CHECK` prefixes). | ||
| Furthermore, the tests fail for builds which specify additional flags | ||
| that modify code generation, including `--coverage` or `-fsanitize=`. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,320 @@ | ||
| #! /usr/bin/env python | ||
| # encoding: utf-8 | ||
|
|
||
| import argparse | ||
| import errno | ||
| import logging | ||
| import os | ||
| import platform | ||
| import re | ||
| import sys | ||
| import subprocess | ||
| import tempfile | ||
|
|
||
| try: | ||
| import winreg | ||
| except ImportError: | ||
| import _winreg as winreg | ||
| try: | ||
| import urllib.request as request | ||
| except ImportError: | ||
| import urllib as request | ||
| try: | ||
| import urllib.parse as parse | ||
| except ImportError: | ||
| import urlparse as parse | ||
|
|
||
| class EmptyLogger(object): | ||
| ''' | ||
| Provides an implementation that performs no logging | ||
| ''' | ||
| def debug(self, *k, **kw): | ||
| pass | ||
| def info(self, *k, **kw): | ||
| pass | ||
| def warn(self, *k, **kw): | ||
| pass | ||
| def error(self, *k, **kw): | ||
| pass | ||
| def critical(self, *k, **kw): | ||
| pass | ||
| def setLevel(self, *k, **kw): | ||
| pass | ||
|
|
||
| urls = ( | ||
| 'http://downloads.sourceforge.net/project/mingw-w64/Toolchains%20' | ||
| 'targetting%20Win32/Personal%20Builds/mingw-builds/installer/' | ||
| 'repository.txt', | ||
| 'http://downloads.sourceforge.net/project/mingwbuilds/host-windows/' | ||
| 'repository.txt' | ||
| ) | ||
| ''' | ||
| A list of mingw-build repositories | ||
| ''' | ||
|
|
||
| def repository(urls = urls, log = EmptyLogger()): | ||
| ''' | ||
| Downloads and parse mingw-build repository files and parses them | ||
| ''' | ||
| log.info('getting mingw-builds repository') | ||
| versions = {} | ||
| re_sourceforge = re.compile(r'http://sourceforge.net/projects/([^/]+)/files') | ||
| re_sub = r'http://downloads.sourceforge.net/project/\1' | ||
| for url in urls: | ||
| log.debug(' - requesting: %s', url) | ||
| socket = request.urlopen(url) | ||
| repo = socket.read() | ||
| if not isinstance(repo, str): | ||
| repo = repo.decode(); | ||
| socket.close() | ||
| for entry in repo.split('\n')[:-1]: | ||
| value = entry.split('|') | ||
| version = tuple([int(n) for n in value[0].strip().split('.')]) | ||
| version = versions.setdefault(version, {}) | ||
| arch = value[1].strip() | ||
| if arch == 'x32': | ||
| arch = 'i686' | ||
| elif arch == 'x64': | ||
| arch = 'x86_64' | ||
| arch = version.setdefault(arch, {}) | ||
| threading = arch.setdefault(value[2].strip(), {}) | ||
| exceptions = threading.setdefault(value[3].strip(), {}) | ||
| revision = exceptions.setdefault(int(value[4].strip()[3:]), | ||
| re_sourceforge.sub(re_sub, value[5].strip())) | ||
| return versions | ||
|
|
||
| def find_in_path(file, path=None): | ||
| ''' | ||
| Attempts to find an executable in the path | ||
| ''' | ||
| if platform.system() == 'Windows': | ||
| file += '.exe' | ||
| if path is None: | ||
| path = os.environ.get('PATH', '') | ||
| if type(path) is type(''): | ||
| path = path.split(os.pathsep) | ||
| return list(filter(os.path.exists, | ||
| map(lambda dir, file=file: os.path.join(dir, file), path))) | ||
|
|
||
| def find_7zip(log = EmptyLogger()): | ||
| ''' | ||
| Attempts to find 7zip for unpacking the mingw-build archives | ||
| ''' | ||
| log.info('finding 7zip') | ||
| path = find_in_path('7z') | ||
| if not path: | ||
| key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r'SOFTWARE\7-Zip') | ||
| path, _ = winreg.QueryValueEx(key, 'Path') | ||
| path = [os.path.join(path, '7z.exe')] | ||
| log.debug('found \'%s\'', path[0]) | ||
| return path[0] | ||
|
|
||
| find_7zip() | ||
|
|
||
| def unpack(archive, location, log = EmptyLogger()): | ||
| ''' | ||
| Unpacks a mingw-builds archive | ||
| ''' | ||
| sevenzip = find_7zip(log) | ||
| log.info('unpacking %s', os.path.basename(archive)) | ||
| cmd = [sevenzip, 'x', archive, '-o' + location, '-y'] | ||
| log.debug(' - %r', cmd) | ||
| with open(os.devnull, 'w') as devnull: | ||
| subprocess.check_call(cmd, stdout = devnull) | ||
|
|
||
| def download(url, location, log = EmptyLogger()): | ||
| ''' | ||
| Downloads and unpacks a mingw-builds archive | ||
| ''' | ||
| log.info('downloading MinGW') | ||
| log.debug(' - url: %s', url) | ||
| log.debug(' - location: %s', location) | ||
|
|
||
| re_content = re.compile(r'attachment;[ \t]*filename=(")?([^"]*)(")?[\r\n]*') | ||
|
|
||
| stream = request.urlopen(url) | ||
| try: | ||
| content = stream.getheader('Content-Disposition') or '' | ||
| except AttributeError: | ||
| content = stream.headers.getheader('Content-Disposition') or '' | ||
| matches = re_content.match(content) | ||
| if matches: | ||
| filename = matches.group(2) | ||
| else: | ||
| parsed = parse.urlparse(stream.geturl()) | ||
| filename = os.path.basename(parsed.path) | ||
|
|
||
| try: | ||
| os.makedirs(location) | ||
| except OSError as e: | ||
| if e.errno == errno.EEXIST and os.path.isdir(location): | ||
| pass | ||
| else: | ||
| raise | ||
|
|
||
| archive = os.path.join(location, filename) | ||
| with open(archive, 'wb') as out: | ||
| while True: | ||
| buf = stream.read(1024) | ||
| if not buf: | ||
| break | ||
| out.write(buf) | ||
| unpack(archive, location, log = log) | ||
| os.remove(archive) | ||
|
|
||
| possible = os.path.join(location, 'mingw64') | ||
| if not os.path.exists(possible): | ||
| possible = os.path.join(location, 'mingw32') | ||
| if not os.path.exists(possible): | ||
| raise ValueError('Failed to find unpacked MinGW: ' + possible) | ||
| return possible | ||
|
|
||
| def root(location = None, arch = None, version = None, threading = None, | ||
| exceptions = None, revision = None, log = EmptyLogger()): | ||
| ''' | ||
| Returns the root folder of a specific version of the mingw-builds variant | ||
| of gcc. Will download the compiler if needed | ||
| ''' | ||
|
|
||
| # Get the repository if we don't have all the information | ||
| if not (arch and version and threading and exceptions and revision): | ||
| versions = repository(log = log) | ||
|
|
||
| # Determine some defaults | ||
| version = version or max(versions.keys()) | ||
| if not arch: | ||
| arch = platform.machine().lower() | ||
| if arch == 'x86': | ||
| arch = 'i686' | ||
| elif arch == 'amd64': | ||
| arch = 'x86_64' | ||
| if not threading: | ||
| keys = versions[version][arch].keys() | ||
| if 'posix' in keys: | ||
| threading = 'posix' | ||
| elif 'win32' in keys: | ||
| threading = 'win32' | ||
| else: | ||
| threading = keys[0] | ||
| if not exceptions: | ||
| keys = versions[version][arch][threading].keys() | ||
| if 'seh' in keys: | ||
| exceptions = 'seh' | ||
| elif 'sjlj' in keys: | ||
| exceptions = 'sjlj' | ||
| else: | ||
| exceptions = keys[0] | ||
| if revision == None: | ||
| revision = max(versions[version][arch][threading][exceptions].keys()) | ||
| if not location: | ||
| location = os.path.join(tempfile.gettempdir(), 'mingw-builds') | ||
|
|
||
| # Get the download url | ||
| url = versions[version][arch][threading][exceptions][revision] | ||
|
|
||
| # Tell the user whatzzup | ||
| log.info('finding MinGW %s', '.'.join(str(v) for v in version)) | ||
| log.debug(' - arch: %s', arch) | ||
| log.debug(' - threading: %s', threading) | ||
| log.debug(' - exceptions: %s', exceptions) | ||
| log.debug(' - revision: %s', revision) | ||
| log.debug(' - url: %s', url) | ||
|
|
||
| # Store each specific revision differently | ||
| slug = '{version}-{arch}-{threading}-{exceptions}-rev{revision}' | ||
| slug = slug.format( | ||
| version = '.'.join(str(v) for v in version), | ||
| arch = arch, | ||
| threading = threading, | ||
| exceptions = exceptions, | ||
| revision = revision | ||
| ) | ||
| if arch == 'x86_64': | ||
| root_dir = os.path.join(location, slug, 'mingw64') | ||
| elif arch == 'i686': | ||
| root_dir = os.path.join(location, slug, 'mingw32') | ||
| else: | ||
| raise ValueError('Unknown MinGW arch: ' + arch) | ||
|
|
||
| # Download if needed | ||
| if not os.path.exists(root_dir): | ||
| downloaded = download(url, os.path.join(location, slug), log = log) | ||
| if downloaded != root_dir: | ||
| raise ValueError('The location of mingw did not match\n%s\n%s' | ||
| % (downloaded, root_dir)) | ||
|
|
||
| return root_dir | ||
|
|
||
| def str2ver(string): | ||
| ''' | ||
| Converts a version string into a tuple | ||
| ''' | ||
| try: | ||
| version = tuple(int(v) for v in string.split('.')) | ||
| if len(version) is not 3: | ||
| raise ValueError() | ||
| except ValueError: | ||
| raise argparse.ArgumentTypeError( | ||
| 'please provide a three digit version string') | ||
| return version | ||
|
|
||
| def main(): | ||
| ''' | ||
| Invoked when the script is run directly by the python interpreter | ||
| ''' | ||
| parser = argparse.ArgumentParser( | ||
| description = 'Downloads a specific version of MinGW', | ||
| formatter_class = argparse.ArgumentDefaultsHelpFormatter | ||
| ) | ||
| parser.add_argument('--location', | ||
| help = 'the location to download the compiler to', | ||
| default = os.path.join(tempfile.gettempdir(), 'mingw-builds')) | ||
| parser.add_argument('--arch', required = True, choices = ['i686', 'x86_64'], | ||
| help = 'the target MinGW architecture string') | ||
| parser.add_argument('--version', type = str2ver, | ||
| help = 'the version of GCC to download') | ||
| parser.add_argument('--threading', choices = ['posix', 'win32'], | ||
| help = 'the threading type of the compiler') | ||
| parser.add_argument('--exceptions', choices = ['sjlj', 'seh', 'dwarf'], | ||
| help = 'the method to throw exceptions') | ||
| parser.add_argument('--revision', type=int, | ||
| help = 'the revision of the MinGW release') | ||
| group = parser.add_mutually_exclusive_group() | ||
| group.add_argument('-v', '--verbose', action='store_true', | ||
| help='increase the script output verbosity') | ||
| group.add_argument('-q', '--quiet', action='store_true', | ||
| help='only print errors and warning') | ||
| args = parser.parse_args() | ||
|
|
||
| # Create the logger | ||
| logger = logging.getLogger('mingw') | ||
| handler = logging.StreamHandler() | ||
| formatter = logging.Formatter('%(message)s') | ||
| handler.setFormatter(formatter) | ||
| logger.addHandler(handler) | ||
| logger.setLevel(logging.INFO) | ||
| if args.quiet: | ||
| logger.setLevel(logging.WARN) | ||
| if args.verbose: | ||
| logger.setLevel(logging.DEBUG) | ||
|
|
||
| # Get MinGW | ||
| root_dir = root(location = args.location, arch = args.arch, | ||
| version = args.version, threading = args.threading, | ||
| exceptions = args.exceptions, revision = args.revision, | ||
| log = logger) | ||
|
|
||
| sys.stdout.write('%s\n' % os.path.join(root_dir, 'bin')) | ||
|
|
||
| if __name__ == '__main__': | ||
| try: | ||
| main() | ||
| except IOError as e: | ||
| sys.stderr.write('IO error: %s\n' % e) | ||
| sys.exit(1) | ||
| except OSError as e: | ||
| sys.stderr.write('OS error: %s\n' % e) | ||
| sys.exit(1) | ||
| except KeyboardInterrupt as e: | ||
| sys.stderr.write('Killed\n') | ||
| sys.exit(1) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| # How to release | ||
|
|
||
| * Make sure you're on master and synced to HEAD | ||
| * Ensure the project builds and tests run (sanity check only, obviously) | ||
| * `parallel -j0 exec ::: test/*_test` can help ensure everything at least | ||
| passes | ||
| * Prepare release notes | ||
| * `git log $(git describe --abbrev=0 --tags)..HEAD` gives you the list of | ||
| commits between the last annotated tag and HEAD | ||
| * Pick the most interesting. | ||
| * Create a release through github's interface | ||
| * Note this will create a lightweight tag. | ||
| * Update this to an annotated tag: | ||
| * `git pull --tags` | ||
| * `git tag -a -f <tag> <tag>` | ||
| * `git push --force origin` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| # Allow the source files to find headers in src/ | ||
| include_directories(${PROJECT_SOURCE_DIR}/src) | ||
|
|
||
| if (DEFINED BENCHMARK_CXX_LINKER_FLAGS) | ||
| list(APPEND CMAKE_SHARED_LINKER_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS}) | ||
| list(APPEND CMAKE_MODULE_LINKER_FLAGS ${BENCHMARK_CXX_LINKER_FLAGS}) | ||
| endif() | ||
|
|
||
| file(GLOB | ||
| SOURCE_FILES | ||
| *.cc | ||
| ${PROJECT_SOURCE_DIR}/include/benchmark/*.h | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/*.h) | ||
| list(FILTER SOURCE_FILES EXCLUDE REGEX "benchmark_main\\.cc") | ||
|
|
||
| add_library(benchmark ${SOURCE_FILES}) | ||
| set_target_properties(benchmark PROPERTIES | ||
| OUTPUT_NAME "benchmark" | ||
| VERSION ${GENERIC_LIB_VERSION} | ||
| SOVERSION ${GENERIC_LIB_SOVERSION} | ||
| ) | ||
| target_include_directories(benchmark PUBLIC | ||
| $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> | ||
| ) | ||
|
|
||
| # Link threads. | ||
| target_link_libraries(benchmark ${BENCHMARK_CXX_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) | ||
| find_library(LIBRT rt) | ||
| if(LIBRT) | ||
| target_link_libraries(benchmark ${LIBRT}) | ||
| endif() | ||
|
|
||
| # We need extra libraries on Windows | ||
| if(${CMAKE_SYSTEM_NAME} MATCHES "Windows") | ||
| target_link_libraries(benchmark Shlwapi) | ||
| endif() | ||
|
|
||
| # We need extra libraries on Solaris | ||
| if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS") | ||
| target_link_libraries(benchmark kstat) | ||
| endif() | ||
|
|
||
| # Benchmark main library | ||
| add_library(benchmark_main "benchmark_main.cc") | ||
| set_target_properties(benchmark_main PROPERTIES | ||
| OUTPUT_NAME "benchmark_main" | ||
| VERSION ${GENERIC_LIB_VERSION} | ||
| SOVERSION ${GENERIC_LIB_SOVERSION} | ||
| ) | ||
| target_include_directories(benchmark PUBLIC | ||
| $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> | ||
| ) | ||
| target_link_libraries(benchmark_main benchmark) | ||
|
|
||
| set(include_install_dir "include") | ||
| set(lib_install_dir "lib/") | ||
| set(bin_install_dir "bin/") | ||
| set(config_install_dir "lib/cmake/${PROJECT_NAME}") | ||
| set(pkgconfig_install_dir "lib/pkgconfig") | ||
|
|
||
| set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") | ||
|
|
||
| set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") | ||
| set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") | ||
| set(pkg_config "${generated_dir}/${PROJECT_NAME}.pc") | ||
| set(targets_export_name "${PROJECT_NAME}Targets") | ||
|
|
||
| set(namespace "${PROJECT_NAME}::") | ||
|
|
||
| include(CMakePackageConfigHelpers) | ||
| write_basic_package_version_file( | ||
| "${version_config}" VERSION ${GIT_VERSION} COMPATIBILITY SameMajorVersion | ||
| ) | ||
|
|
||
| configure_file("${PROJECT_SOURCE_DIR}/cmake/Config.cmake.in" "${project_config}" @ONLY) | ||
| configure_file("${PROJECT_SOURCE_DIR}/cmake/benchmark.pc.in" "${pkg_config}" @ONLY) | ||
|
|
||
| if (BENCHMARK_ENABLE_INSTALL) | ||
| # Install target (will install the library to specified CMAKE_INSTALL_PREFIX variable) | ||
| install( | ||
| TARGETS benchmark benchmark_main | ||
| EXPORT ${targets_export_name} | ||
| ARCHIVE DESTINATION ${lib_install_dir} | ||
| LIBRARY DESTINATION ${lib_install_dir} | ||
| RUNTIME DESTINATION ${bin_install_dir} | ||
| INCLUDES DESTINATION ${include_install_dir}) | ||
|
|
||
| install( | ||
| DIRECTORY "${PROJECT_SOURCE_DIR}/include/benchmark" | ||
| DESTINATION ${include_install_dir} | ||
| FILES_MATCHING PATTERN "*.*h") | ||
|
|
||
| install( | ||
| FILES "${project_config}" "${version_config}" | ||
| DESTINATION "${config_install_dir}") | ||
|
|
||
| install( | ||
| FILES "${pkg_config}" | ||
| DESTINATION "${pkgconfig_install_dir}") | ||
|
|
||
| install( | ||
| EXPORT "${targets_export_name}" | ||
| NAMESPACE "${namespace}" | ||
| DESTINATION "${config_install_dir}") | ||
| endif() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #ifndef BENCHMARK_ARRAYSIZE_H_ | ||
| #define BENCHMARK_ARRAYSIZE_H_ | ||
|
|
||
| #include "internal_macros.h" | ||
|
|
||
| namespace benchmark { | ||
| namespace internal { | ||
| // The arraysize(arr) macro returns the # of elements in an array arr. | ||
| // The expression is a compile-time constant, and therefore can be | ||
| // used in defining new arrays, for example. If you use arraysize on | ||
| // a pointer by mistake, you will get a compile-time error. | ||
| // | ||
|
|
||
| // This template function declaration is used in defining arraysize. | ||
| // Note that the function doesn't need an implementation, as we only | ||
| // use its type. | ||
| template <typename T, size_t N> | ||
| char (&ArraySizeHelper(T (&array)[N]))[N]; | ||
|
|
||
| // That gcc wants both of these prototypes seems mysterious. VC, for | ||
| // its part, can't decide which to use (another mystery). Matching of | ||
| // template overloads: the final frontier. | ||
| #ifndef COMPILER_MSVC | ||
| template <typename T, size_t N> | ||
| char (&ArraySizeHelper(const T (&array)[N]))[N]; | ||
| #endif | ||
|
|
||
| #define arraysize(array) (sizeof(::benchmark::internal::ArraySizeHelper(array))) | ||
|
|
||
| } // end namespace internal | ||
| } // end namespace benchmark | ||
|
|
||
| #endif // BENCHMARK_ARRAYSIZE_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| #ifndef BENCHMARK_API_INTERNAL_H | ||
| #define BENCHMARK_API_INTERNAL_H | ||
|
|
||
| #include "benchmark/benchmark.h" | ||
|
|
||
| #include <cmath> | ||
| #include <iosfwd> | ||
| #include <limits> | ||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| namespace benchmark { | ||
| namespace internal { | ||
|
|
||
| // Information kept per benchmark we may want to run | ||
| struct Benchmark::Instance { | ||
| std::string name; | ||
| Benchmark* benchmark; | ||
| ReportMode report_mode; | ||
| std::vector<int64_t> arg; | ||
| TimeUnit time_unit; | ||
| int range_multiplier; | ||
| bool use_real_time; | ||
| bool use_manual_time; | ||
| BigO complexity; | ||
| BigOFunc* complexity_lambda; | ||
| UserCounters counters; | ||
| const std::vector<Statistics>* statistics; | ||
| bool last_benchmark_instance; | ||
| int repetitions; | ||
| double min_time; | ||
| size_t iterations; | ||
| int threads; // Number of concurrent threads to us | ||
| }; | ||
|
|
||
| bool FindBenchmarksInternal(const std::string& re, | ||
| std::vector<Benchmark::Instance>* benchmarks, | ||
| std::ostream* Err); | ||
|
|
||
| bool IsZero(double n); | ||
|
|
||
| ConsoleReporter::OutputOptions GetOutputOptions(bool force_no_color = false); | ||
|
|
||
| } // end namespace internal | ||
| } // end namespace benchmark | ||
|
|
||
| #endif // BENCHMARK_API_INTERNAL_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| // Copyright 2018 Google Inc. All rights reserved. | ||
| // | ||
| // 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 "benchmark/benchmark.h" | ||
|
|
||
| BENCHMARK_MAIN(); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #ifndef BENCHMARK_REGISTER_H | ||
| #define BENCHMARK_REGISTER_H | ||
|
|
||
| #include <vector> | ||
|
|
||
| #include "check.h" | ||
|
|
||
| template <typename T> | ||
| void AddRange(std::vector<T>* dst, T lo, T hi, int mult) { | ||
| CHECK_GE(lo, 0); | ||
| CHECK_GE(hi, lo); | ||
| CHECK_GE(mult, 2); | ||
|
|
||
| // Add "lo" | ||
| dst->push_back(lo); | ||
|
|
||
| static const T kmax = std::numeric_limits<T>::max(); | ||
|
|
||
| // Now space out the benchmarks in multiples of "mult" | ||
| for (T i = 1; i < kmax / mult; i *= mult) { | ||
| if (i >= hi) break; | ||
| if (i > lo) { | ||
| dst->push_back(i); | ||
| } | ||
| } | ||
|
|
||
| // Add "hi" (if different from "lo") | ||
| if (hi != lo) { | ||
| dst->push_back(hi); | ||
| } | ||
| } | ||
|
|
||
| #endif // BENCHMARK_REGISTER_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| #ifndef CHECK_H_ | ||
| #define CHECK_H_ | ||
|
|
||
| #include <cstdlib> | ||
| #include <ostream> | ||
| #include <cmath> | ||
|
|
||
| #include "internal_macros.h" | ||
| #include "log.h" | ||
|
|
||
| namespace benchmark { | ||
| namespace internal { | ||
|
|
||
| typedef void(AbortHandlerT)(); | ||
|
|
||
| inline AbortHandlerT*& GetAbortHandler() { | ||
| static AbortHandlerT* handler = &std::abort; | ||
| return handler; | ||
| } | ||
|
|
||
| BENCHMARK_NORETURN inline void CallAbortHandler() { | ||
| GetAbortHandler()(); | ||
| std::abort(); // fallback to enforce noreturn | ||
| } | ||
|
|
||
| // CheckHandler is the class constructed by failing CHECK macros. CheckHandler | ||
| // will log information about the failures and abort when it is destructed. | ||
| class CheckHandler { | ||
| public: | ||
| CheckHandler(const char* check, const char* file, const char* func, int line) | ||
| : log_(GetErrorLogInstance()) { | ||
| log_ << file << ":" << line << ": " << func << ": Check `" << check | ||
| << "' failed. "; | ||
| } | ||
|
|
||
| LogType& GetLog() { return log_; } | ||
|
|
||
| BENCHMARK_NORETURN ~CheckHandler() BENCHMARK_NOEXCEPT_OP(false) { | ||
| log_ << std::endl; | ||
| CallAbortHandler(); | ||
| } | ||
|
|
||
| CheckHandler& operator=(const CheckHandler&) = delete; | ||
| CheckHandler(const CheckHandler&) = delete; | ||
| CheckHandler() = delete; | ||
|
|
||
| private: | ||
| LogType& log_; | ||
| }; | ||
|
|
||
| } // end namespace internal | ||
| } // end namespace benchmark | ||
|
|
||
| // The CHECK macro returns a std::ostream object that can have extra information | ||
| // written to it. | ||
| #ifndef NDEBUG | ||
| #define CHECK(b) \ | ||
| (b ? ::benchmark::internal::GetNullLogInstance() \ | ||
| : ::benchmark::internal::CheckHandler(#b, __FILE__, __func__, __LINE__) \ | ||
| .GetLog()) | ||
| #else | ||
| #define CHECK(b) ::benchmark::internal::GetNullLogInstance() | ||
| #endif | ||
|
|
||
| #define CHECK_EQ(a, b) CHECK((a) == (b)) | ||
| #define CHECK_NE(a, b) CHECK((a) != (b)) | ||
| #define CHECK_GE(a, b) CHECK((a) >= (b)) | ||
| #define CHECK_LE(a, b) CHECK((a) <= (b)) | ||
| #define CHECK_GT(a, b) CHECK((a) > (b)) | ||
| #define CHECK_LT(a, b) CHECK((a) < (b)) | ||
|
|
||
| #define CHECK_FLOAT_EQ(a, b, eps) CHECK(std::fabs((a) - (b)) < (eps)) | ||
| #define CHECK_FLOAT_NE(a, b, eps) CHECK(std::fabs((a) - (b)) >= (eps)) | ||
| #define CHECK_FLOAT_GE(a, b, eps) CHECK((a) - (b) > -(eps)) | ||
| #define CHECK_FLOAT_LE(a, b, eps) CHECK((b) - (a) > -(eps)) | ||
| #define CHECK_FLOAT_GT(a, b, eps) CHECK((a) - (b) > (eps)) | ||
| #define CHECK_FLOAT_LT(a, b, eps) CHECK((b) - (a) > (eps)) | ||
|
|
||
| #endif // CHECK_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,188 @@ | ||
| // Copyright 2015 Google Inc. All rights reserved. | ||
| // | ||
| // 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 "colorprint.h" | ||
|
|
||
| #include <cstdarg> | ||
| #include <cstdio> | ||
| #include <cstdlib> | ||
| #include <cstring> | ||
| #include <memory> | ||
| #include <string> | ||
|
|
||
| #include "check.h" | ||
| #include "internal_macros.h" | ||
|
|
||
| #ifdef BENCHMARK_OS_WINDOWS | ||
| #include <Windows.h> | ||
| #include <io.h> | ||
| #else | ||
| #include <unistd.h> | ||
| #endif // BENCHMARK_OS_WINDOWS | ||
|
|
||
| namespace benchmark { | ||
| namespace { | ||
| #ifdef BENCHMARK_OS_WINDOWS | ||
| typedef WORD PlatformColorCode; | ||
| #else | ||
| typedef const char* PlatformColorCode; | ||
| #endif | ||
|
|
||
| PlatformColorCode GetPlatformColorCode(LogColor color) { | ||
| #ifdef BENCHMARK_OS_WINDOWS | ||
| switch (color) { | ||
| case COLOR_RED: | ||
| return FOREGROUND_RED; | ||
| case COLOR_GREEN: | ||
| return FOREGROUND_GREEN; | ||
| case COLOR_YELLOW: | ||
| return FOREGROUND_RED | FOREGROUND_GREEN; | ||
| case COLOR_BLUE: | ||
| return FOREGROUND_BLUE; | ||
| case COLOR_MAGENTA: | ||
| return FOREGROUND_BLUE | FOREGROUND_RED; | ||
| case COLOR_CYAN: | ||
| return FOREGROUND_BLUE | FOREGROUND_GREEN; | ||
| case COLOR_WHITE: // fall through to default | ||
| default: | ||
| return 0; | ||
| } | ||
| #else | ||
| switch (color) { | ||
| case COLOR_RED: | ||
| return "1"; | ||
| case COLOR_GREEN: | ||
| return "2"; | ||
| case COLOR_YELLOW: | ||
| return "3"; | ||
| case COLOR_BLUE: | ||
| return "4"; | ||
| case COLOR_MAGENTA: | ||
| return "5"; | ||
| case COLOR_CYAN: | ||
| return "6"; | ||
| case COLOR_WHITE: | ||
| return "7"; | ||
| default: | ||
| return nullptr; | ||
| }; | ||
| #endif | ||
| } | ||
|
|
||
| } // end namespace | ||
|
|
||
| std::string FormatString(const char* msg, va_list args) { | ||
| // we might need a second shot at this, so pre-emptivly make a copy | ||
| va_list args_cp; | ||
| va_copy(args_cp, args); | ||
|
|
||
| std::size_t size = 256; | ||
| char local_buff[256]; | ||
| auto ret = vsnprintf(local_buff, size, msg, args_cp); | ||
|
|
||
| va_end(args_cp); | ||
|
|
||
| // currently there is no error handling for failure, so this is hack. | ||
| CHECK(ret >= 0); | ||
|
|
||
| if (ret == 0) // handle empty expansion | ||
| return {}; | ||
| else if (static_cast<size_t>(ret) < size) | ||
| return local_buff; | ||
| else { | ||
| // we did not provide a long enough buffer on our first attempt. | ||
| size = (size_t)ret + 1; // + 1 for the null byte | ||
| std::unique_ptr<char[]> buff(new char[size]); | ||
| ret = vsnprintf(buff.get(), size, msg, args); | ||
| CHECK(ret > 0 && ((size_t)ret) < size); | ||
| return buff.get(); | ||
| } | ||
| } | ||
|
|
||
| std::string FormatString(const char* msg, ...) { | ||
| va_list args; | ||
| va_start(args, msg); | ||
| auto tmp = FormatString(msg, args); | ||
| va_end(args); | ||
| return tmp; | ||
| } | ||
|
|
||
| void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...) { | ||
| va_list args; | ||
| va_start(args, fmt); | ||
| ColorPrintf(out, color, fmt, args); | ||
| va_end(args); | ||
| } | ||
|
|
||
| void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, | ||
| va_list args) { | ||
| #ifdef BENCHMARK_OS_WINDOWS | ||
| ((void)out); // suppress unused warning | ||
|
|
||
| const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); | ||
|
|
||
| // Gets the current text color. | ||
| CONSOLE_SCREEN_BUFFER_INFO buffer_info; | ||
| GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); | ||
| const WORD old_color_attrs = buffer_info.wAttributes; | ||
|
|
||
| // We need to flush the stream buffers into the console before each | ||
| // SetConsoleTextAttribute call lest it affect the text that is already | ||
| // printed but has not yet reached the console. | ||
| fflush(stdout); | ||
| SetConsoleTextAttribute(stdout_handle, | ||
| GetPlatformColorCode(color) | FOREGROUND_INTENSITY); | ||
| vprintf(fmt, args); | ||
|
|
||
| fflush(stdout); | ||
| // Restores the text color. | ||
| SetConsoleTextAttribute(stdout_handle, old_color_attrs); | ||
| #else | ||
| const char* color_code = GetPlatformColorCode(color); | ||
| if (color_code) out << FormatString("\033[0;3%sm", color_code); | ||
| out << FormatString(fmt, args) << "\033[m"; | ||
| #endif | ||
| } | ||
|
|
||
| bool IsColorTerminal() { | ||
| #if BENCHMARK_OS_WINDOWS | ||
| // On Windows the TERM variable is usually not set, but the | ||
| // console there does support colors. | ||
| return 0 != _isatty(_fileno(stdout)); | ||
| #else | ||
| // On non-Windows platforms, we rely on the TERM variable. This list of | ||
| // supported TERM values is copied from Google Test: | ||
| // <https://github.com/google/googletest/blob/master/googletest/src/gtest.cc#L2925>. | ||
| const char* const SUPPORTED_TERM_VALUES[] = { | ||
| "xterm", "xterm-color", "xterm-256color", | ||
| "screen", "screen-256color", "tmux", | ||
| "tmux-256color", "rxvt-unicode", "rxvt-unicode-256color", | ||
| "linux", "cygwin", | ||
| }; | ||
|
|
||
| const char* const term = getenv("TERM"); | ||
|
|
||
| bool term_supports_color = false; | ||
| for (const char* candidate : SUPPORTED_TERM_VALUES) { | ||
| if (term && 0 == strcmp(term, candidate)) { | ||
| term_supports_color = true; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| return 0 != isatty(fileno(stdout)) && term_supports_color; | ||
| #endif // BENCHMARK_OS_WINDOWS | ||
| } | ||
|
|
||
| } // end namespace benchmark |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| #ifndef BENCHMARK_COLORPRINT_H_ | ||
| #define BENCHMARK_COLORPRINT_H_ | ||
|
|
||
| #include <cstdarg> | ||
| #include <iostream> | ||
| #include <string> | ||
|
|
||
| namespace benchmark { | ||
| enum LogColor { | ||
| COLOR_DEFAULT, | ||
| COLOR_RED, | ||
| COLOR_GREEN, | ||
| COLOR_YELLOW, | ||
| COLOR_BLUE, | ||
| COLOR_MAGENTA, | ||
| COLOR_CYAN, | ||
| COLOR_WHITE | ||
| }; | ||
|
|
||
| std::string FormatString(const char* msg, va_list args); | ||
| std::string FormatString(const char* msg, ...); | ||
|
|
||
| void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, | ||
| va_list args); | ||
| void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...); | ||
|
|
||
| // Returns true if stdout appears to be a terminal that supports colored | ||
| // output, false otherwise. | ||
| bool IsColorTerminal(); | ||
|
|
||
| } // end namespace benchmark | ||
|
|
||
| #endif // BENCHMARK_COLORPRINT_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,218 @@ | ||
| // Copyright 2015 Google Inc. All rights reserved. | ||
| // | ||
| // 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 "commandlineflags.h" | ||
|
|
||
| #include <cctype> | ||
| #include <cstdlib> | ||
| #include <cstring> | ||
| #include <iostream> | ||
| #include <limits> | ||
|
|
||
| namespace benchmark { | ||
| // Parses 'str' for a 32-bit signed integer. If successful, writes | ||
| // the result to *value and returns true; otherwise leaves *value | ||
| // unchanged and returns false. | ||
| bool ParseInt32(const std::string& src_text, const char* str, int32_t* value) { | ||
| // Parses the environment variable as a decimal integer. | ||
| char* end = nullptr; | ||
| const long long_value = strtol(str, &end, 10); // NOLINT | ||
|
|
||
| // Has strtol() consumed all characters in the string? | ||
| if (*end != '\0') { | ||
| // No - an invalid character was encountered. | ||
| std::cerr << src_text << " is expected to be a 32-bit integer, " | ||
| << "but actually has value \"" << str << "\".\n"; | ||
| return false; | ||
| } | ||
|
|
||
| // Is the parsed value in the range of an Int32? | ||
| const int32_t result = static_cast<int32_t>(long_value); | ||
| if (long_value == std::numeric_limits<long>::max() || | ||
| long_value == std::numeric_limits<long>::min() || | ||
| // The parsed value overflows as a long. (strtol() returns | ||
| // LONG_MAX or LONG_MIN when the input overflows.) | ||
| result != long_value | ||
| // The parsed value overflows as an Int32. | ||
| ) { | ||
| std::cerr << src_text << " is expected to be a 32-bit integer, " | ||
| << "but actually has value \"" << str << "\", " | ||
| << "which overflows.\n"; | ||
| return false; | ||
| } | ||
|
|
||
| *value = result; | ||
| return true; | ||
| } | ||
|
|
||
| // Parses 'str' for a double. If successful, writes the result to *value and | ||
| // returns true; otherwise leaves *value unchanged and returns false. | ||
| bool ParseDouble(const std::string& src_text, const char* str, double* value) { | ||
| // Parses the environment variable as a decimal integer. | ||
| char* end = nullptr; | ||
| const double double_value = strtod(str, &end); // NOLINT | ||
|
|
||
| // Has strtol() consumed all characters in the string? | ||
| if (*end != '\0') { | ||
| // No - an invalid character was encountered. | ||
| std::cerr << src_text << " is expected to be a double, " | ||
| << "but actually has value \"" << str << "\".\n"; | ||
| return false; | ||
| } | ||
|
|
||
| *value = double_value; | ||
| return true; | ||
| } | ||
|
|
||
| // Returns the name of the environment variable corresponding to the | ||
| // given flag. For example, FlagToEnvVar("foo") will return | ||
| // "BENCHMARK_FOO" in the open-source version. | ||
| static std::string FlagToEnvVar(const char* flag) { | ||
| const std::string flag_str(flag); | ||
|
|
||
| std::string env_var; | ||
| for (size_t i = 0; i != flag_str.length(); ++i) | ||
| env_var += static_cast<char>(::toupper(flag_str.c_str()[i])); | ||
|
|
||
| return "BENCHMARK_" + env_var; | ||
| } | ||
|
|
||
| // Reads and returns the Boolean environment variable corresponding to | ||
| // the given flag; if it's not set, returns default_value. | ||
| // | ||
| // The value is considered true iff it's not "0". | ||
| bool BoolFromEnv(const char* flag, bool default_value) { | ||
| const std::string env_var = FlagToEnvVar(flag); | ||
| const char* const string_value = getenv(env_var.c_str()); | ||
| return string_value == nullptr ? default_value | ||
| : strcmp(string_value, "0") != 0; | ||
| } | ||
|
|
||
| // Reads and returns a 32-bit integer stored in the environment | ||
| // variable corresponding to the given flag; if it isn't set or | ||
| // doesn't represent a valid 32-bit integer, returns default_value. | ||
| int32_t Int32FromEnv(const char* flag, int32_t default_value) { | ||
| const std::string env_var = FlagToEnvVar(flag); | ||
| const char* const string_value = getenv(env_var.c_str()); | ||
| if (string_value == nullptr) { | ||
| // The environment variable is not set. | ||
| return default_value; | ||
| } | ||
|
|
||
| int32_t result = default_value; | ||
| if (!ParseInt32(std::string("Environment variable ") + env_var, string_value, | ||
| &result)) { | ||
| std::cout << "The default value " << default_value << " is used.\n"; | ||
| return default_value; | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| // Reads and returns the string environment variable corresponding to | ||
| // the given flag; if it's not set, returns default_value. | ||
| const char* StringFromEnv(const char* flag, const char* default_value) { | ||
| const std::string env_var = FlagToEnvVar(flag); | ||
| const char* const value = getenv(env_var.c_str()); | ||
| return value == nullptr ? default_value : value; | ||
| } | ||
|
|
||
| // Parses a string as a command line flag. The string should have | ||
| // the format "--flag=value". When def_optional is true, the "=value" | ||
| // part can be omitted. | ||
| // | ||
| // Returns the value of the flag, or nullptr if the parsing failed. | ||
| const char* ParseFlagValue(const char* str, const char* flag, | ||
| bool def_optional) { | ||
| // str and flag must not be nullptr. | ||
| if (str == nullptr || flag == nullptr) return nullptr; | ||
|
|
||
| // The flag must start with "--". | ||
| const std::string flag_str = std::string("--") + std::string(flag); | ||
| const size_t flag_len = flag_str.length(); | ||
| if (strncmp(str, flag_str.c_str(), flag_len) != 0) return nullptr; | ||
|
|
||
| // Skips the flag name. | ||
| const char* flag_end = str + flag_len; | ||
|
|
||
| // When def_optional is true, it's OK to not have a "=value" part. | ||
| if (def_optional && (flag_end[0] == '\0')) return flag_end; | ||
|
|
||
| // If def_optional is true and there are more characters after the | ||
| // flag name, or if def_optional is false, there must be a '=' after | ||
| // the flag name. | ||
| if (flag_end[0] != '=') return nullptr; | ||
|
|
||
| // Returns the string after "=". | ||
| return flag_end + 1; | ||
| } | ||
|
|
||
| bool ParseBoolFlag(const char* str, const char* flag, bool* value) { | ||
| // Gets the value of the flag as a string. | ||
| const char* const value_str = ParseFlagValue(str, flag, true); | ||
|
|
||
| // Aborts if the parsing failed. | ||
| if (value_str == nullptr) return false; | ||
|
|
||
| // Converts the string value to a bool. | ||
| *value = IsTruthyFlagValue(value_str); | ||
| return true; | ||
| } | ||
|
|
||
| bool ParseInt32Flag(const char* str, const char* flag, int32_t* value) { | ||
| // Gets the value of the flag as a string. | ||
| const char* const value_str = ParseFlagValue(str, flag, false); | ||
|
|
||
| // Aborts if the parsing failed. | ||
| if (value_str == nullptr) return false; | ||
|
|
||
| // Sets *value to the value of the flag. | ||
| return ParseInt32(std::string("The value of flag --") + flag, value_str, | ||
| value); | ||
| } | ||
|
|
||
| bool ParseDoubleFlag(const char* str, const char* flag, double* value) { | ||
| // Gets the value of the flag as a string. | ||
| const char* const value_str = ParseFlagValue(str, flag, false); | ||
|
|
||
| // Aborts if the parsing failed. | ||
| if (value_str == nullptr) return false; | ||
|
|
||
| // Sets *value to the value of the flag. | ||
| return ParseDouble(std::string("The value of flag --") + flag, value_str, | ||
| value); | ||
| } | ||
|
|
||
| bool ParseStringFlag(const char* str, const char* flag, std::string* value) { | ||
| // Gets the value of the flag as a string. | ||
| const char* const value_str = ParseFlagValue(str, flag, false); | ||
|
|
||
| // Aborts if the parsing failed. | ||
| if (value_str == nullptr) return false; | ||
|
|
||
| *value = value_str; | ||
| return true; | ||
| } | ||
|
|
||
| bool IsFlag(const char* str, const char* flag) { | ||
| return (ParseFlagValue(str, flag, true) != nullptr); | ||
| } | ||
|
|
||
| bool IsTruthyFlagValue(const std::string& value) { | ||
| if (value.empty()) return true; | ||
| char ch = value[0]; | ||
| return isalnum(ch) && | ||
| !(ch == '0' || ch == 'f' || ch == 'F' || ch == 'n' || ch == 'N'); | ||
| } | ||
| } // end namespace benchmark |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| #ifndef BENCHMARK_COMMANDLINEFLAGS_H_ | ||
| #define BENCHMARK_COMMANDLINEFLAGS_H_ | ||
|
|
||
| #include <cstdint> | ||
| #include <string> | ||
|
|
||
| // Macro for referencing flags. | ||
| #define FLAG(name) FLAGS_##name | ||
|
|
||
| // Macros for declaring flags. | ||
| #define DECLARE_bool(name) extern bool FLAG(name) | ||
| #define DECLARE_int32(name) extern int32_t FLAG(name) | ||
| #define DECLARE_int64(name) extern int64_t FLAG(name) | ||
| #define DECLARE_double(name) extern double FLAG(name) | ||
| #define DECLARE_string(name) extern std::string FLAG(name) | ||
|
|
||
| // Macros for defining flags. | ||
| #define DEFINE_bool(name, default_val, doc) bool FLAG(name) = (default_val) | ||
| #define DEFINE_int32(name, default_val, doc) int32_t FLAG(name) = (default_val) | ||
| #define DEFINE_int64(name, default_val, doc) int64_t FLAG(name) = (default_val) | ||
| #define DEFINE_double(name, default_val, doc) double FLAG(name) = (default_val) | ||
| #define DEFINE_string(name, default_val, doc) \ | ||
| std::string FLAG(name) = (default_val) | ||
|
|
||
| namespace benchmark { | ||
| // Parses 'str' for a 32-bit signed integer. If successful, writes the result | ||
| // to *value and returns true; otherwise leaves *value unchanged and returns | ||
| // false. | ||
| bool ParseInt32(const std::string& src_text, const char* str, int32_t* value); | ||
|
|
||
| // Parses a bool/Int32/string from the environment variable | ||
| // corresponding to the given Google Test flag. | ||
| bool BoolFromEnv(const char* flag, bool default_val); | ||
| int32_t Int32FromEnv(const char* flag, int32_t default_val); | ||
| double DoubleFromEnv(const char* flag, double default_val); | ||
| const char* StringFromEnv(const char* flag, const char* default_val); | ||
|
|
||
| // Parses a string for a bool flag, in the form of either | ||
| // "--flag=value" or "--flag". | ||
| // | ||
| // In the former case, the value is taken as true if it passes IsTruthyValue(). | ||
| // | ||
| // In the latter case, the value is taken as true. | ||
| // | ||
| // On success, stores the value of the flag in *value, and returns | ||
| // true. On failure, returns false without changing *value. | ||
| bool ParseBoolFlag(const char* str, const char* flag, bool* value); | ||
|
|
||
| // Parses a string for an Int32 flag, in the form of | ||
| // "--flag=value". | ||
| // | ||
| // On success, stores the value of the flag in *value, and returns | ||
| // true. On failure, returns false without changing *value. | ||
| bool ParseInt32Flag(const char* str, const char* flag, int32_t* value); | ||
|
|
||
| // Parses a string for a Double flag, in the form of | ||
| // "--flag=value". | ||
| // | ||
| // On success, stores the value of the flag in *value, and returns | ||
| // true. On failure, returns false without changing *value. | ||
| bool ParseDoubleFlag(const char* str, const char* flag, double* value); | ||
|
|
||
| // Parses a string for a string flag, in the form of | ||
| // "--flag=value". | ||
| // | ||
| // On success, stores the value of the flag in *value, and returns | ||
| // true. On failure, returns false without changing *value. | ||
| bool ParseStringFlag(const char* str, const char* flag, std::string* value); | ||
|
|
||
| // Returns true if the string matches the flag. | ||
| bool IsFlag(const char* str, const char* flag); | ||
|
|
||
| // Returns true unless value starts with one of: '0', 'f', 'F', 'n' or 'N', or | ||
| // some non-alphanumeric character. As a special case, also returns true if | ||
| // value is the empty string. | ||
| bool IsTruthyFlagValue(const std::string& value); | ||
| } // end namespace benchmark | ||
|
|
||
| #endif // BENCHMARK_COMMANDLINEFLAGS_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,220 @@ | ||
| // Copyright 2016 Ismael Jimenez Martinez. All rights reserved. | ||
| // | ||
| // 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. | ||
|
|
||
| // Source project : https://github.com/ismaelJimenez/cpp.leastsq | ||
| // Adapted to be used with google benchmark | ||
|
|
||
| #include "benchmark/benchmark.h" | ||
|
|
||
| #include <algorithm> | ||
| #include <cmath> | ||
| #include "check.h" | ||
| #include "complexity.h" | ||
|
|
||
| namespace benchmark { | ||
|
|
||
| // Internal function to calculate the different scalability forms | ||
| BigOFunc* FittingCurve(BigO complexity) { | ||
| switch (complexity) { | ||
| case oN: | ||
| return [](int64_t n) -> double { return static_cast<double>(n); }; | ||
| case oNSquared: | ||
| return [](int64_t n) -> double { return std::pow(n, 2); }; | ||
| case oNCubed: | ||
| return [](int64_t n) -> double { return std::pow(n, 3); }; | ||
| case oLogN: | ||
| return [](int64_t n) { return log2(n); }; | ||
| case oNLogN: | ||
| return [](int64_t n) { return n * log2(n); }; | ||
| case o1: | ||
| default: | ||
| return [](int64_t) { return 1.0; }; | ||
| } | ||
| } | ||
|
|
||
| // Function to return an string for the calculated complexity | ||
| std::string GetBigOString(BigO complexity) { | ||
| switch (complexity) { | ||
| case oN: | ||
| return "N"; | ||
| case oNSquared: | ||
| return "N^2"; | ||
| case oNCubed: | ||
| return "N^3"; | ||
| case oLogN: | ||
| return "lgN"; | ||
| case oNLogN: | ||
| return "NlgN"; | ||
| case o1: | ||
| return "(1)"; | ||
| default: | ||
| return "f(N)"; | ||
| } | ||
| } | ||
|
|
||
| // Find the coefficient for the high-order term in the running time, by | ||
| // minimizing the sum of squares of relative error, for the fitting curve | ||
| // given by the lambda expression. | ||
| // - n : Vector containing the size of the benchmark tests. | ||
| // - time : Vector containing the times for the benchmark tests. | ||
| // - fitting_curve : lambda expression (e.g. [](int64_t n) {return n; };). | ||
|
|
||
| // For a deeper explanation on the algorithm logic, look the README file at | ||
| // http://github.com/ismaelJimenez/Minimal-Cpp-Least-Squared-Fit | ||
|
|
||
| LeastSq MinimalLeastSq(const std::vector<int64_t>& n, | ||
| const std::vector<double>& time, | ||
| BigOFunc* fitting_curve) { | ||
| double sigma_gn = 0.0; | ||
| double sigma_gn_squared = 0.0; | ||
| double sigma_time = 0.0; | ||
| double sigma_time_gn = 0.0; | ||
|
|
||
| // Calculate least square fitting parameter | ||
| for (size_t i = 0; i < n.size(); ++i) { | ||
| double gn_i = fitting_curve(n[i]); | ||
| sigma_gn += gn_i; | ||
| sigma_gn_squared += gn_i * gn_i; | ||
| sigma_time += time[i]; | ||
| sigma_time_gn += time[i] * gn_i; | ||
| } | ||
|
|
||
| LeastSq result; | ||
| result.complexity = oLambda; | ||
|
|
||
| // Calculate complexity. | ||
| result.coef = sigma_time_gn / sigma_gn_squared; | ||
|
|
||
| // Calculate RMS | ||
| double rms = 0.0; | ||
| for (size_t i = 0; i < n.size(); ++i) { | ||
| double fit = result.coef * fitting_curve(n[i]); | ||
| rms += pow((time[i] - fit), 2); | ||
| } | ||
|
|
||
| // Normalized RMS by the mean of the observed values | ||
| double mean = sigma_time / n.size(); | ||
| result.rms = sqrt(rms / n.size()) / mean; | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| // Find the coefficient for the high-order term in the running time, by | ||
| // minimizing the sum of squares of relative error. | ||
| // - n : Vector containing the size of the benchmark tests. | ||
| // - time : Vector containing the times for the benchmark tests. | ||
| // - complexity : If different than oAuto, the fitting curve will stick to | ||
| // this one. If it is oAuto, it will be calculated the best | ||
| // fitting curve. | ||
| LeastSq MinimalLeastSq(const std::vector<int64_t>& n, | ||
| const std::vector<double>& time, const BigO complexity) { | ||
| CHECK_EQ(n.size(), time.size()); | ||
| CHECK_GE(n.size(), 2); // Do not compute fitting curve is less than two | ||
| // benchmark runs are given | ||
| CHECK_NE(complexity, oNone); | ||
|
|
||
| LeastSq best_fit; | ||
|
|
||
| if (complexity == oAuto) { | ||
| std::vector<BigO> fit_curves = {oLogN, oN, oNLogN, oNSquared, oNCubed}; | ||
|
|
||
| // Take o1 as default best fitting curve | ||
| best_fit = MinimalLeastSq(n, time, FittingCurve(o1)); | ||
| best_fit.complexity = o1; | ||
|
|
||
| // Compute all possible fitting curves and stick to the best one | ||
| for (const auto& fit : fit_curves) { | ||
| LeastSq current_fit = MinimalLeastSq(n, time, FittingCurve(fit)); | ||
| if (current_fit.rms < best_fit.rms) { | ||
| best_fit = current_fit; | ||
| best_fit.complexity = fit; | ||
| } | ||
| } | ||
| } else { | ||
| best_fit = MinimalLeastSq(n, time, FittingCurve(complexity)); | ||
| best_fit.complexity = complexity; | ||
| } | ||
|
|
||
| return best_fit; | ||
| } | ||
|
|
||
| std::vector<BenchmarkReporter::Run> ComputeBigO( | ||
| const std::vector<BenchmarkReporter::Run>& reports) { | ||
| typedef BenchmarkReporter::Run Run; | ||
| std::vector<Run> results; | ||
|
|
||
| if (reports.size() < 2) return results; | ||
|
|
||
| // Accumulators. | ||
| std::vector<int64_t> n; | ||
| std::vector<double> real_time; | ||
| std::vector<double> cpu_time; | ||
|
|
||
| // Populate the accumulators. | ||
| for (const Run& run : reports) { | ||
| CHECK_GT(run.complexity_n, 0) << "Did you forget to call SetComplexityN?"; | ||
| n.push_back(run.complexity_n); | ||
| real_time.push_back(run.real_accumulated_time / run.iterations); | ||
| cpu_time.push_back(run.cpu_accumulated_time / run.iterations); | ||
| } | ||
|
|
||
| LeastSq result_cpu; | ||
| LeastSq result_real; | ||
|
|
||
| if (reports[0].complexity == oLambda) { | ||
| result_cpu = MinimalLeastSq(n, cpu_time, reports[0].complexity_lambda); | ||
| result_real = MinimalLeastSq(n, real_time, reports[0].complexity_lambda); | ||
| } else { | ||
| result_cpu = MinimalLeastSq(n, cpu_time, reports[0].complexity); | ||
| result_real = MinimalLeastSq(n, real_time, result_cpu.complexity); | ||
| } | ||
| std::string benchmark_name = | ||
| reports[0].benchmark_name.substr(0, reports[0].benchmark_name.find('/')); | ||
|
|
||
| // Get the data from the accumulator to BenchmarkReporter::Run's. | ||
| Run big_o; | ||
| big_o.benchmark_name = benchmark_name + "_BigO"; | ||
| big_o.iterations = 0; | ||
| big_o.real_accumulated_time = result_real.coef; | ||
| big_o.cpu_accumulated_time = result_cpu.coef; | ||
| big_o.report_big_o = true; | ||
| big_o.complexity = result_cpu.complexity; | ||
|
|
||
| // All the time results are reported after being multiplied by the | ||
| // time unit multiplier. But since RMS is a relative quantity it | ||
| // should not be multiplied at all. So, here, we _divide_ it by the | ||
| // multiplier so that when it is multiplied later the result is the | ||
| // correct one. | ||
| double multiplier = GetTimeUnitMultiplier(reports[0].time_unit); | ||
|
|
||
| // Only add label to mean/stddev if it is same for all runs | ||
| Run rms; | ||
| big_o.report_label = reports[0].report_label; | ||
| rms.benchmark_name = benchmark_name + "_RMS"; | ||
| rms.report_label = big_o.report_label; | ||
| rms.iterations = 0; | ||
| rms.real_accumulated_time = result_real.rms / multiplier; | ||
| rms.cpu_accumulated_time = result_cpu.rms / multiplier; | ||
| rms.report_rms = true; | ||
| rms.complexity = result_cpu.complexity; | ||
| // don't forget to keep the time unit, or we won't be able to | ||
| // recover the correct value. | ||
| rms.time_unit = reports[0].time_unit; | ||
|
|
||
| results.push_back(big_o); | ||
| results.push_back(rms); | ||
| return results; | ||
| } | ||
|
|
||
| } // end namespace benchmark |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| // Copyright 2016 Ismael Jimenez Martinez. All rights reserved. | ||
| // | ||
| // 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. | ||
|
|
||
| // Source project : https://github.com/ismaelJimenez/cpp.leastsq | ||
| // Adapted to be used with google benchmark | ||
|
|
||
| #ifndef COMPLEXITY_H_ | ||
| #define COMPLEXITY_H_ | ||
|
|
||
| #include <string> | ||
| #include <vector> | ||
|
|
||
| #include "benchmark/benchmark.h" | ||
|
|
||
| namespace benchmark { | ||
|
|
||
| // Return a vector containing the bigO and RMS information for the specified | ||
| // list of reports. If 'reports.size() < 2' an empty vector is returned. | ||
| std::vector<BenchmarkReporter::Run> ComputeBigO( | ||
| const std::vector<BenchmarkReporter::Run>& reports); | ||
|
|
||
| // This data structure will contain the result returned by MinimalLeastSq | ||
| // - coef : Estimated coeficient for the high-order term as | ||
| // interpolated from data. | ||
| // - rms : Normalized Root Mean Squared Error. | ||
| // - complexity : Scalability form (e.g. oN, oNLogN). In case a scalability | ||
| // form has been provided to MinimalLeastSq this will return | ||
| // the same value. In case BigO::oAuto has been selected, this | ||
| // parameter will return the best fitting curve detected. | ||
|
|
||
| struct LeastSq { | ||
| LeastSq() : coef(0.0), rms(0.0), complexity(oNone) {} | ||
|
|
||
| double coef; | ||
| double rms; | ||
| BigO complexity; | ||
| }; | ||
|
|
||
| // Function to return an string for the calculated complexity | ||
| std::string GetBigOString(BigO complexity); | ||
|
|
||
| } // end namespace benchmark | ||
|
|
||
| #endif // COMPLEXITY_H_ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| // Copyright 2015 Google Inc. All rights reserved. | ||
| // | ||
| // 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 "benchmark/benchmark.h" | ||
| #include "complexity.h" | ||
| #include "counter.h" | ||
|
|
||
| #include <algorithm> | ||
| #include <cstdint> | ||
| #include <cstdio> | ||
| #include <iostream> | ||
| #include <string> | ||
| #include <tuple> | ||
| #include <vector> | ||
|
|
||
| #include "check.h" | ||
| #include "colorprint.h" | ||
| #include "commandlineflags.h" | ||
| #include "internal_macros.h" | ||
| #include "string_util.h" | ||
| #include "timers.h" | ||
|
|
||
| namespace benchmark { | ||
|
|
||
| bool ConsoleReporter::ReportContext(const Context& context) { | ||
| name_field_width_ = context.name_field_width; | ||
| printed_header_ = false; | ||
| prev_counters_.clear(); | ||
|
|
||
| PrintBasicContext(&GetErrorStream(), context); | ||
|
|
||
| #ifdef BENCHMARK_OS_WINDOWS | ||
| if ((output_options_ & OO_Color) && &std::cout != &GetOutputStream()) { | ||
| GetErrorStream() | ||
| << "Color printing is only supported for stdout on windows." | ||
| " Disabling color printing\n"; | ||
| output_options_ = static_cast< OutputOptions >(output_options_ & ~OO_Color); | ||
| } | ||
| #endif | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| void ConsoleReporter::PrintHeader(const Run& run) { | ||
| std::string str = FormatString("%-*s %13s %13s %10s", static_cast<int>(name_field_width_), | ||
| "Benchmark", "Time", "CPU", "Iterations"); | ||
| if(!run.counters.empty()) { | ||
| if(output_options_ & OO_Tabular) { | ||
| for(auto const& c : run.counters) { | ||
| str += FormatString(" %10s", c.first.c_str()); | ||
| } | ||
| } else { | ||
| str += " UserCounters..."; | ||
| } | ||
| } | ||
| str += "\n"; | ||
| std::string line = std::string(str.length(), '-'); | ||
| GetOutputStream() << line << "\n" << str << line << "\n"; | ||
| } | ||
|
|
||
| void ConsoleReporter::ReportRuns(const std::vector<Run>& reports) { | ||
| for (const auto& run : reports) { | ||
| // print the header: | ||
| // --- if none was printed yet | ||
| bool print_header = !printed_header_; | ||
| // --- or if the format is tabular and this run | ||
| // has different fields from the prev header | ||
| print_header |= (output_options_ & OO_Tabular) && | ||
| (!internal::SameNames(run.counters, prev_counters_)); | ||
| if (print_header) { | ||
| printed_header_ = true; | ||
| prev_counters_ = run.counters; | ||
| PrintHeader(run); | ||
| } | ||
| // As an alternative to printing the headers like this, we could sort | ||
| // the benchmarks by header and then print. But this would require | ||
| // waiting for the full results before printing, or printing twice. | ||
| PrintRunData(run); | ||
| } | ||
| } | ||
|
|
||
| static void IgnoreColorPrint(std::ostream& out, LogColor, const char* fmt, | ||
| ...) { | ||
| va_list args; | ||
| va_start(args, fmt); | ||
| out << FormatString(fmt, args); | ||
| va_end(args); | ||
| } | ||
|
|
||
| void ConsoleReporter::PrintRunData(const Run& result) { | ||
| typedef void(PrinterFn)(std::ostream&, LogColor, const char*, ...); | ||
| auto& Out = GetOutputStream(); | ||
| PrinterFn* printer = (output_options_ & OO_Color) ? | ||
| (PrinterFn*)ColorPrintf : IgnoreColorPrint; | ||
| auto name_color = | ||
| (result.report_big_o || result.report_rms) ? COLOR_BLUE : COLOR_GREEN; | ||
| printer(Out, name_color, "%-*s ", name_field_width_, | ||
| result.benchmark_name.c_str()); | ||
|
|
||
| if (result.error_occurred) { | ||
| printer(Out, COLOR_RED, "ERROR OCCURRED: \'%s\'", | ||
| result.error_message.c_str()); | ||
| printer(Out, COLOR_DEFAULT, "\n"); | ||
| return; | ||
| } | ||
| // Format bytes per second | ||
| std::string rate; | ||
| if (result.bytes_per_second > 0) { | ||
| rate = StrCat(" ", HumanReadableNumber(result.bytes_per_second), "B/s"); | ||
| } | ||
|
|
||
| // Format items per second | ||
| std::string items; | ||
| if (result.items_per_second > 0) { | ||
| items = | ||
| StrCat(" ", HumanReadableNumber(result.items_per_second), " items/s"); | ||
| } | ||
|
|
||
| const double real_time = result.GetAdjustedRealTime(); | ||
| const double cpu_time = result.GetAdjustedCPUTime(); | ||
|
|
||
| if (result.report_big_o) { | ||
| std::string big_o = GetBigOString(result.complexity); | ||
| printer(Out, COLOR_YELLOW, "%10.2f %s %10.2f %s ", real_time, big_o.c_str(), | ||
| cpu_time, big_o.c_str()); | ||
| } else if (result.report_rms) { | ||
| printer(Out, COLOR_YELLOW, "%10.0f %% %10.0f %% ", real_time * 100, | ||
| cpu_time * 100); | ||
| } else { | ||
| const char* timeLabel = GetTimeUnitString(result.time_unit); | ||
| printer(Out, COLOR_YELLOW, "%10.0f %s %10.0f %s ", real_time, timeLabel, | ||
| cpu_time, timeLabel); | ||
| } | ||
|
|
||
| if (!result.report_big_o && !result.report_rms) { | ||
| printer(Out, COLOR_CYAN, "%10lld", result.iterations); | ||
| } | ||
|
|
||
| for (auto& c : result.counters) { | ||
| const std::size_t cNameLen = std::max(std::string::size_type(10), | ||
| c.first.length()); | ||
| auto const& s = HumanReadableNumber(c.second.value, 1000); | ||
| if (output_options_ & OO_Tabular) { | ||
| if (c.second.flags & Counter::kIsRate) { | ||
| printer(Out, COLOR_DEFAULT, " %*s/s", cNameLen - 2, s.c_str()); | ||
| } else { | ||
| printer(Out, COLOR_DEFAULT, " %*s", cNameLen, s.c_str()); | ||
| } | ||
| } else { | ||
| const char* unit = (c.second.flags & Counter::kIsRate) ? "/s" : ""; | ||
| printer(Out, COLOR_DEFAULT, " %s=%s%s", c.first.c_str(), s.c_str(), | ||
| unit); | ||
| } | ||
| } | ||
|
|
||
| if (!rate.empty()) { | ||
| printer(Out, COLOR_DEFAULT, " %*s", 13, rate.c_str()); | ||
| } | ||
|
|
||
| if (!items.empty()) { | ||
| printer(Out, COLOR_DEFAULT, " %*s", 18, items.c_str()); | ||
| } | ||
|
|
||
| if (!result.report_label.empty()) { | ||
| printer(Out, COLOR_DEFAULT, " %s", result.report_label.c_str()); | ||
| } | ||
|
|
||
| printer(Out, COLOR_DEFAULT, "\n"); | ||
| } | ||
|
|
||
| } // end namespace benchmark |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // Copyright 2015 Google Inc. All rights reserved. | ||
| // | ||
| // 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 "counter.h" | ||
|
|
||
| namespace benchmark { | ||
| namespace internal { | ||
|
|
||
| double Finish(Counter const& c, double cpu_time, double num_threads) { | ||
| double v = c.value; | ||
| if (c.flags & Counter::kIsRate) { | ||
| v /= cpu_time; | ||
| } | ||
| if (c.flags & Counter::kAvgThreads) { | ||
| v /= num_threads; | ||
| } | ||
| return v; | ||
| } | ||
|
|
||
| void Finish(UserCounters *l, double cpu_time, double num_threads) { | ||
| for (auto &c : *l) { | ||
| c.second.value = Finish(c.second, cpu_time, num_threads); | ||
| } | ||
| } | ||
|
|
||
| void Increment(UserCounters *l, UserCounters const& r) { | ||
| // add counters present in both or just in *l | ||
| for (auto &c : *l) { | ||
| auto it = r.find(c.first); | ||
| if (it != r.end()) { | ||
| c.second.value = c.second + it->second; | ||
| } | ||
| } | ||
| // add counters present in r, but not in *l | ||
| for (auto const &tc : r) { | ||
| auto it = l->find(tc.first); | ||
| if (it == l->end()) { | ||
| (*l)[tc.first] = tc.second; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| bool SameNames(UserCounters const& l, UserCounters const& r) { | ||
| if (&l == &r) return true; | ||
| if (l.size() != r.size()) { | ||
| return false; | ||
| } | ||
| for (auto const& c : l) { | ||
| if (r.find(c.first) == r.end()) { | ||
| return false; | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| } // end namespace internal | ||
| } // end namespace benchmark |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,26 @@ | ||
| // Copyright 2015 Google Inc. All rights reserved. | ||
| // | ||
| // 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 "benchmark/benchmark.h" | ||
|
|
||
| namespace benchmark { | ||
|
|
||
| // these counter-related functions are hidden to reduce API surface. | ||
| namespace internal { | ||
| void Finish(UserCounters *l, double time, double num_threads); | ||
| void Increment(UserCounters *l, UserCounters const& r); | ||
| bool SameNames(UserCounters const& l, UserCounters const& r); | ||
| } // end namespace internal | ||
|
|
||
| } //end namespace benchmark |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| // Copyright 2015 Google Inc. All rights reserved. | ||
| // | ||
| // 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 "benchmark/benchmark.h" | ||
| #include "complexity.h" | ||
|
|
||
| #include <algorithm> | ||
| #include <cstdint> | ||
| #include <iostream> | ||
| #include <string> | ||
| #include <tuple> | ||
| #include <vector> | ||
|
|
||
| #include "string_util.h" | ||
| #include "timers.h" | ||
| #include "check.h" | ||
|
|
||
| // File format reference: http://edoceo.com/utilitas/csv-file-format. | ||
|
|
||
| namespace benchmark { | ||
|
|
||
| namespace { | ||
| std::vector<std::string> elements = { | ||
| "name", "iterations", "real_time", "cpu_time", | ||
| "time_unit", "bytes_per_second", "items_per_second", "label", | ||
| "error_occurred", "error_message"}; | ||
| } // namespace | ||
|
|
||
| bool CSVReporter::ReportContext(const Context& context) { | ||
| PrintBasicContext(&GetErrorStream(), context); | ||
| return true; | ||
| } | ||
|
|
||
| void CSVReporter::ReportRuns(const std::vector<Run> & reports) { | ||
| std::ostream& Out = GetOutputStream(); | ||
|
|
||
| if (!printed_header_) { | ||
| // save the names of all the user counters | ||
| for (const auto& run : reports) { | ||
| for (const auto& cnt : run.counters) { | ||
| user_counter_names_.insert(cnt.first); | ||
| } | ||
| } | ||
|
|
||
| // print the header | ||
| for (auto B = elements.begin(); B != elements.end();) { | ||
| Out << *B++; | ||
| if (B != elements.end()) Out << ","; | ||
| } | ||
| for (auto B = user_counter_names_.begin(); B != user_counter_names_.end();) { | ||
| Out << ",\"" << *B++ << "\""; | ||
| } | ||
| Out << "\n"; | ||
|
|
||
| printed_header_ = true; | ||
| } else { | ||
| // check that all the current counters are saved in the name set | ||
| for (const auto& run : reports) { | ||
| for (const auto& cnt : run.counters) { | ||
| CHECK(user_counter_names_.find(cnt.first) != user_counter_names_.end()) | ||
| << "All counters must be present in each run. " | ||
| << "Counter named \"" << cnt.first | ||
| << "\" was not in a run after being added to the header"; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // print results for each run | ||
| for (const auto& run : reports) { | ||
| PrintRunData(run); | ||
| } | ||
|
|
||
| } | ||
|
|
||
| void CSVReporter::PrintRunData(const Run & run) { | ||
| std::ostream& Out = GetOutputStream(); | ||
|
|
||
| // Field with embedded double-quote characters must be doubled and the field | ||
| // delimited with double-quotes. | ||
| std::string name = run.benchmark_name; | ||
| ReplaceAll(&name, "\"", "\"\""); | ||
| Out << '"' << name << "\","; | ||
| if (run.error_occurred) { | ||
| Out << std::string(elements.size() - 3, ','); | ||
| Out << "true,"; | ||
| std::string msg = run.error_message; | ||
| ReplaceAll(&msg, "\"", "\"\""); | ||
| Out << '"' << msg << "\"\n"; | ||
| return; | ||
| } | ||
|
|
||
| // Do not print iteration on bigO and RMS report | ||
| if (!run.report_big_o && !run.report_rms) { | ||
| Out << run.iterations; | ||
| } | ||
| Out << ","; | ||
|
|
||
| Out << run.GetAdjustedRealTime() << ","; | ||
| Out << run.GetAdjustedCPUTime() << ","; | ||
|
|
||
| // Do not print timeLabel on bigO and RMS report | ||
| if (run.report_big_o) { | ||
| Out << GetBigOString(run.complexity); | ||
| } else if (!run.report_rms) { | ||
| Out << GetTimeUnitString(run.time_unit); | ||
| } | ||
| Out << ","; | ||
|
|
||
| if (run.bytes_per_second > 0.0) { | ||
| Out << run.bytes_per_second; | ||
| } | ||
| Out << ","; | ||
| if (run.items_per_second > 0.0) { | ||
| Out << run.items_per_second; | ||
| } | ||
| Out << ","; | ||
| if (!run.report_label.empty()) { | ||
| // Field with embedded double-quote characters must be doubled and the field | ||
| // delimited with double-quotes. | ||
| std::string label = run.report_label; | ||
| ReplaceAll(&label, "\"", "\"\""); | ||
| Out << "\"" << label << "\""; | ||
| } | ||
| Out << ",,"; // for error_occurred and error_message | ||
|
|
||
| // Print user counters | ||
| for (const auto &ucn : user_counter_names_) { | ||
| auto it = run.counters.find(ucn); | ||
| if(it == run.counters.end()) { | ||
| Out << ","; | ||
| } else { | ||
| Out << "," << it->second; | ||
| } | ||
| } | ||
| Out << '\n'; | ||
| } | ||
|
|
||
| } // end namespace benchmark |