Skip to content

Commit

Permalink
Correct CMake script. Improve C++ language feature selection (#243)
Browse files Browse the repository at this point in the history
* cmake: correct minimum version

the previous script would fail with CMake < 3.13 anyway.

* cmake: canonical way to set C++ options

* ci: use modern CMake syntax

* doc: modern CMake syntax

* most compilers have C++17 fallthrough

* correct logic for [[noreturn]]

---------

Co-authored-by: scivision <scivision@users.noreply.github.com>
  • Loading branch information
scivision and scivision committed Jun 25, 2024
1 parent d88c5e1 commit e5b892b
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 82 deletions.
26 changes: 19 additions & 7 deletions .github/workflows/linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,24 @@ jobs:
compiler: [g++, clang++]
flags: [-std=c++11, -std=c++17]
optimize: [-O2]

env:
CXX: ${{ matrix.compiler }}
CXXFLAGS: ${{ matrix.flags }} ${{ matrix.optimize }}
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
CTEST_PARALLEL_LEVEL: 0
CMAKE_BUILD_PARALLEL_LEVEL: 4

steps:
- name: Checkout
uses: actions/checkout@v2.0.0
- name: Build and test
env:
CXX: ${{ matrix.compiler }}
CXXFLAGS: ${{ matrix.flags }} ${{ matrix.optimize }}
run: |
mkdir build && cd build && cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Release .. && cmake --build . && make test
uses: actions/checkout@v4

- name: CMake configure
run: cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Release -B build

- name: CMake build
run: cmake --build build

- name: CMake test
run: ctest --test-dir build
26 changes: 19 additions & 7 deletions .github/workflows/macos.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,24 @@ jobs:
compiler: [g++, clang++]
flags: [-std=c++11, -std=c++17]
optimize: [-O2]

env:
CXX: ${{ matrix.compiler }}
CXXFLAGS: ${{ matrix.flags }} ${{ matrix.optimize }}
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
CTEST_PARALLEL_LEVEL: 0
CMAKE_BUILD_PARALLEL_LEVEL: 4

steps:
- name: Checkout
uses: actions/checkout@v2.0.0
- name: Build and test
env:
CXX: ${{ matrix.compiler }}
CXXFLAGS: ${{ matrix.flags }} ${{ matrix.optimize }}
run: |
mkdir build && cd build && cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Release .. && cmake --build . && make test
uses: actions/checkout@v4

- name: CMake configure
run: cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Release -B build

- name: CMake build
run: cmake --build build

- name: CMake test
run: ctest --test-dir build
26 changes: 18 additions & 8 deletions .github/workflows/windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,23 @@ jobs:
os: [windows-latest]
flags: ["/std:c++11", "/std:c++latest"]
optimize: [/O2]

env:
CXXFLAGS: ${{ matrix.flags }} ${{ matrix.optimize }}
CTEST_OUTPUT_ON_FAILURE: 1
CTEST_NO_TESTS_ACTION: error
CTEST_PARALLEL_LEVEL: 0
CMAKE_BUILD_PARALLEL_LEVEL: 4

steps:
- name: Checkout
uses: actions/checkout@v2.0.0
- name: Build and test
env:
CXX: ${{ matrix.compiler }}
CXXFLAGS: ${{ matrix.flags }} ${{ matrix.optimize }}
CTEST_OUTPUT_ON_FAILURE: 1
run: |
cmake -Bbuild -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build --target ALL_BUILD && cmake --build build --target RUN_TESTS
uses: actions/checkout@v4

- name: CMake configure
run: cmake -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -B build

- name: CMake build
run: cmake --build build --config Release

- name: CMake test
run: ctest --test-dir build -C Release
42 changes: 19 additions & 23 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,12 @@

#]===================================================================]

cmake_minimum_required(VERSION 3.8)
cmake_minimum_required(VERSION 3.13)

list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

include(DetectVersion)

cmake_policy(SET CMP0048 NEW) ## set VERSION as documented by the project() command.
cmake_policy(SET CMP0076 NEW) ## accept new policy

if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11) ## compile with C++11 support
endif()
Expand All @@ -48,14 +45,14 @@ option(PHMAP_INSTALL "Enable installation" ${PHMAP_MASTER_PROJECT})


set(PHMAP_DIR parallel_hashmap)
set(PHMAP_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap_base.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap_bits.h
set(PHMAP_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap_base.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap_bits.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap_config.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap_dump.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap_fwd_decl.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap_utils.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/meminfo.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap_dump.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap_fwd_decl.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/phmap_utils.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/meminfo.h
${CMAKE_CURRENT_SOURCE_DIR}/${PHMAP_DIR}/btree.h)

include(helpers)
Expand All @@ -72,14 +69,14 @@ target_include_directories(
if(PHMAP_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)

install(
DIRECTORY ${PROJECT_SOURCE_DIR}/${PHMAP_DIR}/
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PHMAP_DIR})

install(TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}-targets)

export(EXPORT ${PROJECT_NAME}-targets
FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake")
endif()
Expand All @@ -89,7 +86,7 @@ option(PHMAP_BUILD_TESTS "Whether or not to build the tests" ${PHMAP_MASTE
option(PHMAP_BUILD_EXAMPLES "Whether or not to build the examples" ${PHMAP_MASTER_PROJECT})

if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:/bigobj>")
endif()

if (PHMAP_BUILD_TESTS OR PHMAP_BUILD_EXAMPLES)
Expand All @@ -100,7 +97,7 @@ if (PHMAP_BUILD_TESTS)

if (NOT PHMAP_GTEST_LIBS)
include(cmake/DownloadGTest.cmake)

check_target(gtest)
check_target(gtest_main)
check_target(gmock)
Expand Down Expand Up @@ -169,12 +166,12 @@ endif()

if (PHMAP_BUILD_EXAMPLES)
if(NOT MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wextra -Wcast-align -Wcast-qual -Wdisabled-optimization -Winit-self -Wlogical-op -Wmissing-include-dirs -Woverloaded-virtual -Wredundant-decls -Wshadow -Wstrict-null-sentinel -Wswitch-default -Wno-unused")
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-pedantic;-Wall;-Wextra;-Wcast-align;-Wcast-qual;-Wdisabled-optimization;-Winit-self;-Wlogical-op;-Wmissing-include-dirs;-Woverloaded-virtual;-Wredundant-decls;-Wshadow;-Wstrict-null-sentinel;-Wswitch-default;-Wno-unused>")
if (NOT CMAKE_COMPILER_IS_GNUCC OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unknown-warning-option -Wno-gnu-zero-variadic-macro-arguments")
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:-Wno-unknown-warning-option;-Wno-gnu-zero-variadic-macro-arguments>")
endif()
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4 /Zc:__cplusplus")
add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:/W4;/Zc:__cplusplus>")
endif()

set(THREADS_PREFER_PTHREAD_FLAG ON)
Expand Down Expand Up @@ -203,8 +200,8 @@ if (PHMAP_BUILD_EXAMPLES)
add_executable(ex_p_bench examples/p_bench.cc phmap.natvis)

#set(Boost_INCLUDE_DIR /home/greg/dev/boost_1_82_0) # if boost installed in non-standard location
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_LIBS OFF)
set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)

# llil4map.cc - see https://www.perlmonks.com/?node_id=11149643
Expand All @@ -223,8 +220,7 @@ if (PHMAP_BUILD_EXAMPLES)
target_compile_options(ex_llil4map PRIVATE "${OpenMP_CXX_FLAGS}")
file(COPY examples/llil_utils DESTINATION "${CMAKE_CURRENT_BINARY_DIR}")
endif()

target_link_libraries(ex_knucleotide Threads::Threads)
target_link_libraries(ex_bench Threads::Threads)
endif()

60 changes: 34 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@

<img src="https://github.com/greg7mdp/parallel-hashmap/blob/master/html/img/phash.png?raw=true" width="120" align="middle">
<img src="https://github.com/greg7mdp/parallel-hashmap/blob/master/html/img/phash.png?raw=true" width="120" align="middle">

# The Parallel Hashmap
# The Parallel Hashmap

[![License: Apache-2.0](https://img.shields.io/badge/License-Apache-yellow.svg)](https://opensource.org/licenses/Apache-2.0) [![Linux](https://github.com/greg7mdp/parallel-hashmap/actions/workflows/linux.yml/badge.svg)](https://github.com/greg7mdp/parallel-hashmap/actions/workflows/linux.yml) [![MacOS](https://github.com/greg7mdp/parallel-hashmap/actions/workflows/macos.yml/badge.svg)](https://github.com/greg7mdp/parallel-hashmap/actions/workflows/macos.yml) [![Windows](https://github.com/greg7mdp/parallel-hashmap/actions/workflows/windows.yml/badge.svg)](https://github.com/greg7mdp/parallel-hashmap/actions/workflows/windows.yml)
[![License: Apache-2.0](https://img.shields.io/badge/License-Apache-yellow.svg)](https://opensource.org/licenses/Apache-2.0) [![Linux](https://github.com/greg7mdp/parallel-hashmap/actions/workflows/linux.yml/badge.svg)](https://github.com/greg7mdp/parallel-hashmap/actions/workflows/linux.yml) [![MacOS](https://github.com/greg7mdp/parallel-hashmap/actions/workflows/macos.yml/badge.svg)](https://github.com/greg7mdp/parallel-hashmap/actions/workflows/macos.yml) [![Windows](https://github.com/greg7mdp/parallel-hashmap/actions/workflows/windows.yml/badge.svg)](https://github.com/greg7mdp/parallel-hashmap/actions/workflows/windows.yml)

## Overview

This repository aims to provide a set of excellent **hash map** implementations, as well as a **btree** alternative to std::map and std::set, with the following characteristics:
This repository aims to provide a set of excellent **hash map** implementations, as well as a **btree** alternative to std::map and std::set, with the following characteristics:

- **Header only**: nothing to build, just copy the `parallel_hashmap` directory to your project and you are good to go.

Expand Down Expand Up @@ -48,7 +48,15 @@ Copy the parallel_hashmap directory to your project. Update your include path. T

If you are using Visual Studio, you probably want to add `phmap.natvis` to your projects. This will allow for a clear display of the hash table contents in the debugger.

> A cmake configuration files (CMakeLists.txt) is provided for building the tests and examples. Command for building and running the tests is: `mkdir build && cd build && cmake -DPHMAP_BUILD_TESTS=ON -DPHMAP_BUILD_EXAMPLES=ON .. && cmake --build . && make test`
> A cmake configuration files (CMakeLists.txt) is provided for building the tests and examples. Command for building and running the tests is:
```sh
cmake -DPHMAP_BUILD_TESTS=ON -DPHMAP_BUILD_EXAMPLES=ON -B build

cmake --build build

ctest --test-dir build
```

## Example

Expand All @@ -58,27 +66,27 @@ If you are using Visual Studio, you probably want to add `phmap.natvis` to your
#include <parallel_hashmap/phmap.h>

using phmap::flat_hash_map;

int main()
{
// Create an unordered_map of three strings (that map to strings)
flat_hash_map<std::string, std::string> email =
flat_hash_map<std::string, std::string> email =
{
{ "tom", "tom@gmail.com"},
{ "jeff", "jk@gmail.com"},
{ "jim", "jimg@microsoft.com"}
};
// Iterate and print keys and values
for (const auto& n : email)

// Iterate and print keys and values
for (const auto& n : email)
std::cout << n.first << "'s email is: " << n.second << "\n";

// Add a new entry
email["bill"] = "bg@whatever.com";

// and print it
std::cout << "bill's email is: " << email["bill"] << "\n";

return 0;
}
```
Expand Down Expand Up @@ -115,8 +123,8 @@ The full types with template parameters can be found in the [parallel_hashmap/ph

- The `parallel` hash maps are preferred when you have a few hash maps that will store a very large number of values. The `non-parallel` hash maps are preferred if you have a large number of hash maps, each storing a relatively small number of values.

- The benefits of the `parallel` hash maps are:
a. reduced peak memory usage (when resizing), and
- The benefits of the `parallel` hash maps are:
a. reduced peak memory usage (when resizing), and
b. multithreading support (and inherent internal parallelism)

**Key decision points for btree containers:**
Expand All @@ -139,7 +147,7 @@ When an ordering is not needed, a hash container is typically a better choice th

- The Abseil hash tables internally randomize a hash seed, so that the table iteration order is non-deterministic. This can be useful to prevent *Denial Of Service* attacks when a hash table is used for a customer facing web service, but it can make debugging more difficult. The *phmap* hashmaps by default do **not** implement this randomization, but it can be enabled by adding `#define PHMAP_NON_DETERMINISTIC 1` before including the header `phmap.h` (as is done in raw_hash_set_test.cc).

- Unlike the Abseil hash maps, we do an internal mixing of the hash value provided. This prevents serious degradation of the hash table performance when the hash function provided by the user has poor entropy distribution. The cost in performance is very minimal, and this helps provide reliable performance even with *imperfect* hash functions.
- Unlike the Abseil hash maps, we do an internal mixing of the hash value provided. This prevents serious degradation of the hash table performance when the hash function provided by the user has poor entropy distribution. The cost in performance is very minimal, and this helps provide reliable performance even with *imperfect* hash functions.


## Memory usage
Expand Down Expand Up @@ -189,10 +197,10 @@ In order to use a flat_hash_set or flat_hash_map, a hash function should be prov

- Provide a hash functor via the HashFcn template parameter

- As with boost, you may add a `hash_value()` friend function in your class.
- As with boost, you may add a `hash_value()` friend function in your class.

For example:

```c++
#include <parallel_hashmap/phmap_utils.h> // minimal header providing phmap::HashState()
#include <string>
Expand All @@ -201,8 +209,8 @@ using std::string;
struct Person
{
bool operator==(const Person &o) const
{
return _first == o._first && _last == o._last && _age == o._age;
{
return _first == o._first && _last == o._last && _age == o._age;
}

friend size_t hash_value(const Person &p)
Expand Down Expand Up @@ -230,8 +238,8 @@ using std::string;
struct Person
{
bool operator==(const Person &o) const
{
return _first == o._first && _last == o._last && _age == o._age;
{
return _first == o._first && _last == o._last && _age == o._age;
}

string _first;
Expand All @@ -253,7 +261,7 @@ namespace std
}
```

The `std::hash` specialization for `Person` combines the hash values for both first and last name and age, using the convenient phmap::HashState() function, and returns the combined hash value.
The `std::hash` specialization for `Person` combines the hash values for both first and last name and age, using the convenient phmap::HashState() function, and returns the combined hash value.

### file "main.cpp"

Expand All @@ -268,7 +276,7 @@ int main()
// As we have defined a specialization of std::hash() for Person,
// we can now create sparse_hash_set or sparse_hash_map of Persons
// ----------------------------------------------------------------
phmap::flat_hash_set<Person> persons =
phmap::flat_hash_set<Person> persons =
{ { "John", "Mitchell", 35 },
{ "Jane", "Smith", 32 },
{ "Jane", "Smith", 30 },
Expand All @@ -287,7 +295,7 @@ Parallel Hashmap containers follow the thread safety rules of the Standard C++ l

- A single phmap hash table is thread safe for reading from multiple threads. For example, given a hash table A, it is safe to read A from thread 1 and from thread 2 simultaneously.

- If a single hash table is being written to by one thread, then all reads and writes to that hash table on the same or other threads must be protected. For example, given a hash table A, if thread 1 is writing to A, then thread 2 must be prevented from reading from or writing to A.
- If a single hash table is being written to by one thread, then all reads and writes to that hash table on the same or other threads must be protected. For example, given a hash table A, if thread 1 is writing to A, then thread 2 must be prevented from reading from or writing to A.

- It is safe to read and write to one instance of a type even if another thread is reading or writing to a different instance of the same type. For example, given hash tables A and B of the same type, it is safe if A is being written in thread 1 and B is being read in thread 2.

Expand All @@ -304,4 +312,4 @@ While C++ is the native language of the Parallel Hashmap, we welcome bindings ma

## Acknowledgements

Many thanks to the Abseil developers for implementing the swiss table and btree data structures (see [abseil-cpp](https://github.com/abseil/abseil-cpp)) upon which this work is based, and to Google for releasing it as open-source.
Many thanks to the Abseil developers for implementing the swiss table and btree data structures (see [abseil-cpp](https://github.com/abseil/abseil-cpp)) upon which this work is based, and to Google for releasing it as open-source.
Loading

0 comments on commit e5b892b

Please sign in to comment.