Compare commits

..

1 Commits

Author SHA1 Message Date
Marshall Clow 68db81064b Release 1.53.0
[SVN r82734]
2013-02-04 18:11:49 +00:00
268 changed files with 21879 additions and 52904 deletions
-149
View File
@@ -1,149 +0,0 @@
# Copyright 2016, 2017 Peter Dimov
# Copyright 2017 - 2019 James E. King III
# Copyright 2019 - 2021 Alexander Grund
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
version: 1.0.{build}-{branch}
shallow_clone: true
branches:
only:
- master
- develop
- /bugfix\/.*/
- /feature\/.*/
- /fix\/.*/
- /pr\/.*/
matrix:
fast_finish: false
# Adding MAYFAIL to any matrix job allows it to fail but the build stays green:
allow_failures:
- MAYFAIL: true
environment:
global:
B2_CI_VERSION: 1
GIT_FETCH_JOBS: 4
B2_ADDRESS_MODEL: 32,64
B2_VARIANT: debug,release
matrix:
- FLAVOR: Visual Studio 2008, 2010, 2012
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
B2_TOOLSET: msvc-9.0,msvc-10.0,msvc-11.0
B2_ADDRESS_MODEL: 32 # No 64bit support
- FLAVOR: Visual Studio 2013, 2015
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
B2_TOOLSET: msvc-12.0,msvc-14.0
- FLAVOR: Visual Studio 2017
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
B2_CXXSTD: 14,17
B2_TOOLSET: msvc-14.1
- FLAVOR: Visual Studio 2017
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
B2_CXXSTD: latest
B2_TOOLSET: msvc-14.1
- FLAVOR: cygwin (32-bit)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
ADDPATH: C:\cygwin\bin;
B2_ADDRESS_MODEL: 32
B2_CXXSTD: 03,11,14,1z
B2_TOOLSET: gcc
- FLAVOR: cygwin (64-bit)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 03,11,14,1z
B2_TOOLSET: gcc
- FLAVOR: cygwin (64-bit, latest)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 03
B2_TOOLSET: gcc
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
- FLAVOR: cygwin (64-bit, latest)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 11
B2_TOOLSET: gcc
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
- FLAVOR: cygwin (64-bit, latest)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 14
B2_TOOLSET: gcc
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
- FLAVOR: cygwin (64-bit, latest)
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022
ADDPATH: C:\cygwin64\bin;
B2_ADDRESS_MODEL: 64
B2_CXXSTD: 1z
B2_TOOLSET: gcc
B2_FLAGS: "include=libs/unordered/test/unordered include=libs/unordered/test/exception"
- FLAVOR: mingw-w64, 32 bit
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
B2_CXXSTD: 03,11,14
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 32
- FLAVOR: mingw-w64, 32 bit
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\i686-8.1.0-posix-dwarf-rt_v6-rev0\mingw32\bin;
B2_CXXSTD: 17,2a
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 32
- FLAVOR: mingw-w64, 64 bit
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
B2_CXXSTD: 03,11,14
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 64
- FLAVOR: mingw-w64, 64 bit
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
ADDPATH: C:\mingw-w64\x86_64-8.1.0-posix-seh-rt_v6-rev0\mingw64\bin;
B2_CXXSTD: 17,2a
B2_TOOLSET: gcc
B2_ADDRESS_MODEL: 64
#- FLAVOR: CodeCov (VS 2019)
# APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
# B2_CXXFLAGS: -permissive-
# B2_CXXSTD: 14
# B2_TOOLSET: msvc-14.2
# COVERAGE: true
install:
- git clone --depth 1 https://github.com/boostorg/boost-ci.git C:\boost-ci-cloned
# Copy ci folder if not testing Boost.CI
- if NOT "%APPVEYOR_PROJECT_NAME%" == "boost-ci" xcopy /s /e /q /i /y C:\boost-ci-cloned\ci .\ci
- rmdir /s /q C:\boost-ci-cloned
- ci\appveyor\install.bat
build: off
test_script: ci\build.bat
for:
# CodeCov coverage build
- matrix:
only: [COVERAGE: true]
test_script: [ps: ci\codecov.ps1]
-23
View File
@@ -1,23 +0,0 @@
# Copyright 2019 - 2021 Alexander Grund
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
#
# Sample codecov configuration file. Edit as required
codecov:
max_report_age: off
require_ci_to_pass: yes
notify:
# Increase this if you have multiple coverage collection jobs
after_n_builds: 1
wait_for_ci: yes
# Change how pull request comments look
comment:
layout: "reach,diff,flags,files,footer"
# Ignore specific files or folders. Glob patterns are supported.
# See https://docs.codecov.com/docs/ignoring-paths
ignore:
- extra/**/*
# - test/**/*
-388
View File
@@ -1,388 +0,0 @@
# Copyright 2022 Peter Dimov
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
local library = "unordered";
local triggers =
{
branch: [ "master", "develop", "feature/*", "bugfix/*" ]
};
local ubsan = { UBSAN: '1', UBSAN_OPTIONS: 'print_stacktrace=1' };
local asan = { ASAN: '1' };
local linux_pipeline(name, image, environment, packages = "", sources = [], arch = "amd64") =
{
name: name,
kind: "pipeline",
type: "docker",
trigger: triggers,
platform:
{
os: "linux",
arch: arch
},
steps:
[
{
name: "everything",
image: image,
environment: environment,
commands:
[
'set -e',
'uname -a',
'wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -',
] +
(if sources != [] then [ ('apt-add-repository "' + source + '"') for source in sources ] else []) +
(if packages != "" then [ 'apt-get update', 'apt-get -y install ' + packages ] else []) +
[
'export LIBRARY=' + library,
'./.drone/drone.sh',
]
}
]
};
local macos_pipeline(name, environment, xcode_version = "12.2", osx_version = "catalina", arch = "amd64") =
{
name: name,
kind: "pipeline",
type: "exec",
trigger: triggers,
platform: {
"os": "darwin",
"arch": arch
},
node: {
"os": osx_version
},
steps: [
{
name: "everything",
environment: environment + { "DEVELOPER_DIR": "/Applications/Xcode-" + xcode_version + ".app/Contents/Developer" },
commands:
[
'uname -a',
'export LIBRARY=' + library,
'./.drone/drone.sh',
]
}
]
};
local windows_pipeline(name, image, environment, arch = "amd64") =
{
name: name,
kind: "pipeline",
type: "docker",
trigger: triggers,
platform:
{
os: "windows",
arch: arch
},
"steps":
[
{
name: "everything",
image: image,
environment: environment,
commands:
[
'echo $env:DRONE_STAGE_MACHINE',
'cmd /C .drone\\\\drone.bat ' + library,
]
}
]
};
[
linux_pipeline(
"Linux 14.04 GCC 4.4 32/64",
"cppalliance/droneubuntu1404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-4.4', CXXSTD: '98,0x', ADDRMD: '32,64' },
"g++-4.4-multilib",
[ "ppa:ubuntu-toolchain-r/test" ],
),
linux_pipeline(
"Linux 14.04 GCC 4.6 32/64",
"cppalliance/droneubuntu1404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-4.6', CXXSTD: '98,0x', ADDRMD: '32,64' },
"g++-4.6-multilib",
[ "ppa:ubuntu-toolchain-r/test" ],
),
linux_pipeline(
"Linux 14.04 GCC 4.7 32/64",
"cppalliance/droneubuntu1404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-4.7', CXXSTD: '98,0x', ADDRMD: '32,64' },
"g++-4.7-multilib",
[ "ppa:ubuntu-toolchain-r/test" ],
),
linux_pipeline(
"Linux 14.04 GCC 4.8* 32/64",
"cppalliance/droneubuntu1404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 14.04 GCC 4.9 32/64",
"cppalliance/droneubuntu1404:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-4.9', CXXSTD: '03,11', ADDRMD: '32,64' },
"g++-4.9-multilib",
[ "ppa:ubuntu-toolchain-r/test" ],
),
linux_pipeline(
"Linux 16.04 GCC 5* 32/64",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 18.04 GCC 6 32/64",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-6', CXXSTD: '03,11,14', ADDRMD: '32,64' },
"g++-6-multilib",
),
linux_pipeline(
"Linux 18.04 GCC 7* 32/64",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 18.04 GCC 8 32/64",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-8', CXXSTD: '03,11,14,17', ADDRMD: '32,64' },
"g++-8-multilib",
),
linux_pipeline(
"Linux 20.04 GCC 9* 32/64",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 20.04 GCC 9* ARM64",
"cppalliance/droneubuntu2004:multiarch",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a' },
arch="arm64",
),
linux_pipeline(
"Linux 20.04 GCC 9* S390x",
"cppalliance/droneubuntu2004:multiarch",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a' },
arch="s390x",
),
linux_pipeline(
"Linux 20.04 GCC 10 32/64",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-10', CXXSTD: '03,11,14,17,20', ADDRMD: '32,64' },
"g++-10-multilib",
),
linux_pipeline(
"Linux 22.04 GCC 11* 32/64",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'gcc', COMPILER: 'g++', CXXSTD: '03,11,14,17,2a', ADDRMD: '32,64' },
),
linux_pipeline(
"Linux 22.04 GCC 12 32 ASAN (03,11,14)",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14', ADDRMD: '32' } + asan,
"g++-12-multilib",
),
linux_pipeline(
"Linux 22.04 GCC 12 32 ASAN (17,20,2b)",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '17,20,2b', ADDRMD: '32' } + asan,
"g++-12-multilib",
),
linux_pipeline(
"Linux 22.04 GCC 12 64 ASAN (03,11,14)",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14', ADDRMD: '64' } + asan,
"g++-12-multilib",
),
linux_pipeline(
"Linux 22.04 GCC 12 64 ASAN (17,20,2b)",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '17,20,2b', ADDRMD: '64' } + asan,
"g++-12-multilib",
),
linux_pipeline(
"Linux 16.04 Clang 3.5",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.5', CXXSTD: '03,11' },
"clang-3.5",
),
linux_pipeline(
"Linux 16.04 Clang 3.6",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.6', CXXSTD: '03,11,14' },
"clang-3.6",
),
linux_pipeline(
"Linux 16.04 Clang 3.7",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.7', CXXSTD: '03,11,14' },
"clang-3.7",
),
linux_pipeline(
"Linux 16.04 Clang 3.8",
"cppalliance/droneubuntu1604:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.8', CXXSTD: '03,11,14' },
"clang-3.8",
),
linux_pipeline(
"Linux 18.04 Clang 3.9",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-3.9', CXXSTD: '03,11,14' },
"clang-3.9",
),
linux_pipeline(
"Linux 18.04 Clang 4.0",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-4.0', CXXSTD: '03,11,14' },
"clang-4.0",
),
linux_pipeline(
"Linux 18.04 Clang 5.0",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-5.0', CXXSTD: '03,11,14,1z' },
"clang-5.0",
),
linux_pipeline(
"Linux 18.04 Clang 6.0",
"cppalliance/droneubuntu1804:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-6.0', CXXSTD: '03,11,14,17' },
"clang-6.0",
),
linux_pipeline(
"Linux 20.04 Clang 7",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-7', CXXSTD: '03,11,14,17' },
"clang-7",
),
linux_pipeline(
"Linux 20.04 Clang 8",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-8', CXXSTD: '03,11,14,17' },
"clang-8",
),
linux_pipeline(
"Linux 20.04 Clang 9",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-9', CXXSTD: '03,11,14,17,2a' },
"clang-9",
),
linux_pipeline(
"Linux 20.04 Clang 10",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-10', CXXSTD: '03,11,14,17,2a' },
"clang-10",
),
linux_pipeline(
"Linux 20.04 Clang 11",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-11', CXXSTD: '03,11,14,17,2a' },
"clang-11",
),
linux_pipeline(
"Linux 20.04 Clang 12",
"cppalliance/droneubuntu2004:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-12', CXXSTD: '03,11,14,17,2a' },
"clang-12",
),
linux_pipeline(
"Linux 22.04 Clang 13",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-13', CXXSTD: '03,11,14,17,20' },
"clang-13",
),
linux_pipeline(
"Linux 22.04 Clang 14 UBSAN",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20' } + ubsan,
"clang-14",
),
linux_pipeline(
"Linux 22.04 Clang 14 ASAN",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20' } + asan,
"clang-14",
),
linux_pipeline(
"Linux 22.04 Clang 15",
"cppalliance/droneubuntu2204:1",
{ TOOLSET: 'clang', COMPILER: 'clang++-15', CXXSTD: '03,11,14,17,20,2b' },
"clang-15",
["deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-15 main"],
),
macos_pipeline(
"MacOS 10.15 Xcode 12.2 UBSAN",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,1z' } + ubsan,
),
macos_pipeline(
"MacOS 12.4 Xcode 13.4.1 ASAN",
{ TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,1z' } + asan,
xcode_version = "13.4.1", osx_version = "monterey", arch = "arm64",
),
windows_pipeline(
"Windows VS2015 msvc-14.0",
"cppalliance/dronevs2015",
{ TOOLSET: 'msvc-14.0', CXXSTD: '14,latest' },
),
windows_pipeline(
"Windows VS2017 msvc-14.1",
"cppalliance/dronevs2017",
{ TOOLSET: 'msvc-14.1', CXXSTD: '14,17,latest' },
),
windows_pipeline(
"Windows VS2019 msvc-14.2",
"cppalliance/dronevs2019",
{ TOOLSET: 'msvc-14.2', CXXSTD: '14,17,20,latest' },
),
windows_pipeline(
"Windows VS2022 msvc-14.3",
"cppalliance/dronevs2022:1",
{ TOOLSET: 'msvc-14.3', CXXSTD: '14,17,20,latest' },
),
]
-23
View File
@@ -1,23 +0,0 @@
@REM Copyright 2022 Peter Dimov
@REM Distributed under the Boost Software License, Version 1.0.
@REM https://www.boost.org/LICENSE_1_0.txt
@ECHO ON
set LIBRARY=%1
set DRONE_BUILD_DIR=%CD%
set BOOST_BRANCH=develop
if "%DRONE_BRANCH%" == "master" set BOOST_BRANCH=master
cd ..
git clone -b %BOOST_BRANCH% --depth 1 https://github.com/boostorg/boost.git boost-root
cd boost-root
git submodule update --init tools/boostdep
xcopy /s /e /q %DRONE_BUILD_DIR% libs\%LIBRARY%\
python tools/boostdep/depinst/depinst.py %LIBRARY%
cmd /c bootstrap
b2 -d0 headers
if not "%CXXSTD%" == "" set CXXSTD=cxxstd=%CXXSTD%
if not "%ADDRMD%" == "" set ADDRMD=address-model=%ADDRMD%
b2 -j3 libs/%LIBRARY%/test toolset=%TOOLSET% %CXXSTD% %ADDRMD% variant=debug,release embed-manifest-via=linker
-25
View File
@@ -1,25 +0,0 @@
#!/bin/bash
# Copyright 2022 Peter Dimov
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
set -ex
export PATH=~/.local/bin:/usr/local/bin:$PATH
DRONE_BUILD_DIR=$(pwd)
BOOST_BRANCH=develop
if [ "$DRONE_BRANCH" = "master" ]; then BOOST_BRANCH=master; fi
cd ..
git clone -b $BOOST_BRANCH --depth 1 https://github.com/boostorg/boost.git boost-root
cd boost-root
git submodule update --init tools/boostdep
cp -r $DRONE_BUILD_DIR/* libs/$LIBRARY
python tools/boostdep/depinst/depinst.py $LIBRARY
./bootstrap.sh
./b2 -d0 headers
echo "using $TOOLSET : : $COMPILER ;" > ~/user-config.jam
./b2 -j3 libs/$LIBRARY/test toolset=$TOOLSET cxxstd=$CXXSTD variant=debug,release ${ADDRMD:+address-model=$ADDRMD} ${UBSAN:+undefined-sanitizer=norecover debug-symbols=on} ${ASAN:+address-sanitizer=norecover debug-symbols=on} ${LINKFLAGS:+linkflags=$LINKFLAGS}
-7
View File
@@ -1,7 +0,0 @@
[*]
end_of_line = lf
insert_final_newline = true
indent_style = space
[*.?pp]
indent_size = 2
-395
View File
@@ -1,395 +0,0 @@
# Copyright 2020-2021 Peter Dimov
# Copyright 2021 Andrey Semashev
# Copyright 2021 Alexander Grund
# Copyright 2022 James E. King III
#
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at http://boost.org/LICENSE_1_0.txt)
---
name: CI
on:
pull_request:
push:
branches:
- master
- develop
- bugfix/**
- feature/**
- fix/**
- pr/**
concurrency:
group: ${{format('{0}:{1}', github.repository, github.ref)}}
cancel-in-progress: true
env:
GIT_FETCH_JOBS: 8
NET_RETRY_COUNT: 5
B2_CI_VERSION: 1
B2_VARIANT: debug,release
LCOV_BRANCH_COVERAGE: 0
CODECOV_NAME: Github Actions
jobs:
posix:
defaults:
run:
shell: bash
strategy:
fail-fast: false
matrix:
include:
# Linux, gcc
- { compiler: gcc-7, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-7' }
- { compiler: gcc-8, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-8' }
- { compiler: gcc-9, cxxstd: '03,11,14,17', os: ubuntu-20.04, install: 'g++-9' }
- { compiler: gcc-10, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, install: 'g++-10' }
- { compiler: gcc-11, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, install: 'g++-11' }
- { name: "gcc-12 w/ sanitizers (03,11,14)", sanitize: yes,
compiler: gcc-12, cxxstd: '03,11,14', os: ubuntu-22.04, ccache_key: "san1" }
- { name: "gcc-12 w/ sanitizers (17,20,2b)", sanitize: yes,
compiler: gcc-12, cxxstd: '17,20,2b', os: ubuntu-22.04, ccache_key: "san2" }
- { name: Collect coverage, coverage: yes,
compiler: gcc-8, cxxstd: '03,11', os: ubuntu-20.04, install: 'g++-8-multilib', address-model: '32,64', ccache_key: "cov" }
# Linux, clang, libc++
- { compiler: clang-7, cxxstd: '03,11,14,17', os: ubuntu-20.04, stdlib: libc++, install: 'clang-7 libc++-7-dev libc++abi-7-dev' }
- { compiler: clang-10, cxxstd: '03,11,14,17,20', os: ubuntu-20.04, stdlib: libc++, install: 'clang-10 libc++-10-dev libc++abi-10-dev' }
- { compiler: clang-11, cxxstd: '03,11,14,17,20', os: ubuntu-22.04, stdlib: libc++, install: 'clang-11 libc++-11-dev libc++abi-11-dev' }
# clang-12 doesn't work on ubuntu-22.04, the linker can't find -lunwind for some reason
- { name: "clang-12 w/ sanitizers (03,11,14)", sanitize: yes,
compiler: clang-12, cxxstd: '03,11,14', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san1" }
- { name: "clang-12 w/ sanitizers (17,20,2b)", sanitize: yes,
compiler: clang-12, cxxstd: '17,20,2b', os: ubuntu-20.04, stdlib: libc++, install: 'clang-12 libc++-12-dev libc++abi-12-dev', ccache_key: "san2" }
- { compiler: clang-13, cxxstd: '03,11,14,17,20,2b', os: ubuntu-22.04, stdlib: libc++, install: 'clang-13 libc++-13-dev libc++abi-13-dev' }
- { compiler: clang-14, cxxstd: '03,11,14,17,20,2b', os: ubuntu-22.04, stdlib: libc++, install: 'clang-14 libc++-14-dev libc++abi-14-dev' }
# not using libc++ because of https://github.com/llvm/llvm-project/issues/52771
- { name: "clang-14 w/ sanitizers (03,11,14)", sanitize: yes,
compiler: clang-14, cxxstd: '03,11,14', os: ubuntu-22.04, ccache_key: "san1" }
- { name: "clang-14 w/ sanitizers (17,20,2b)", sanitize: yes,
compiler: clang-14, cxxstd: '17,20,2b', os: ubuntu-22.04, ccache_key: "san2" }
# OSX, clang
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-11, }
- { compiler: clang, cxxstd: '03,11,14,17,2a', os: macos-12, sanitize: yes }
timeout-minutes: 120
runs-on: ${{matrix.os}}
container: ${{matrix.container}}
env: {B2_USE_CCACHE: 1}
steps:
- name: Setup environment
run: |
if [ -f "/etc/debian_version" ]; then
echo "DEBIAN_FRONTEND=noninteractive" >> $GITHUB_ENV
export DEBIAN_FRONTEND=noninteractive
fi
if [ -n "${{matrix.container}}" ] && [ -f "/etc/debian_version" ]; then
apt-get -o Acquire::Retries=$NET_RETRY_COUNT update
apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y sudo software-properties-common
# Need (newer) git, and the older Ubuntu container may require requesting the key manually using port 80
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys E1DD270288B4E6030699E45FA1715D88E1DF1F24
for i in {1..${NET_RETRY_COUNT:-3}}; do sudo -E add-apt-repository -y ppa:git-core/ppa && break || sleep 10; done
apt-get -o Acquire::Retries=$NET_RETRY_COUNT update
apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y g++ python libpython-dev git
fi
# For jobs not compatible with ccache, use "ccache: no" in the matrix
if [[ "${{ matrix.ccache }}" == "no" ]]; then
echo "B2_USE_CCACHE=0" >> $GITHUB_ENV
fi
git config --global pack.threads 0
- uses: actions/checkout@v3
with:
# For coverage builds fetch the whole history, else only 1 commit using a 'fake ternary'
fetch-depth: ${{ matrix.coverage && '0' || '1' }}
- name: Cache ccache
uses: actions/cache@v3
if: env.B2_USE_CCACHE
with:
path: ~/.ccache
key: ${{matrix.os}}-${{matrix.container}}-${{matrix.compiler}}-${{matrix.ccache_key}}-${{github.sha}}
restore-keys: ${{matrix.os}}-${{matrix.container}}-${{matrix.compiler}}-${{matrix.ccache_key}}-
- name: Fetch Boost.CI
uses: actions/checkout@v3
with:
repository: boostorg/boost-ci
ref: master
path: boost-ci-cloned
- name: Get CI scripts folder
run: |
# Copy ci folder if not testing Boost.CI
[[ "$GITHUB_REPOSITORY" =~ "boost-ci" ]] || cp -r boost-ci-cloned/ci .
rm -rf boost-ci-cloned
- name: Install packages
if: startsWith(matrix.os, 'ubuntu')
run: |
SOURCE_KEYS=(${{join(matrix.source_keys, ' ')}})
SOURCES=(${{join(matrix.sources, ' ')}})
# Add this by default
SOURCES+=(ppa:ubuntu-toolchain-r/test)
for key in "${SOURCE_KEYS[@]}"; do
for i in {1..$NET_RETRY_COUNT}; do
wget -O - "$key" | sudo apt-key add - && break || sleep 10
done
done
for source in "${SOURCES[@]}"; do
for i in {1..$NET_RETRY_COUNT}; do
sudo add-apt-repository $source && break || sleep 10
done
done
sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT update
if [[ -z "${{matrix.install}}" ]]; then
pkgs="${{matrix.compiler}}"
pkgs="${pkgs/gcc-/g++-}"
else
pkgs="${{matrix.install}}"
fi
sudo apt-get -o Acquire::Retries=$NET_RETRY_COUNT install -y $pkgs
- name: Setup GCC Toolchain
if: matrix.gcc_toolchain
run: |
GCC_TOOLCHAIN_ROOT="$HOME/gcc-toolchain"
echo "GCC_TOOLCHAIN_ROOT=$GCC_TOOLCHAIN_ROOT" >> $GITHUB_ENV
MULTIARCH_TRIPLET="$(dpkg-architecture -qDEB_HOST_MULTIARCH)"
mkdir -p "$GCC_TOOLCHAIN_ROOT"
ln -s /usr/include "$GCC_TOOLCHAIN_ROOT/include"
ln -s /usr/bin "$GCC_TOOLCHAIN_ROOT/bin"
mkdir -p "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET"
ln -s "/usr/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}" "$GCC_TOOLCHAIN_ROOT/lib/gcc/$MULTIARCH_TRIPLET/${{matrix.gcc_toolchain}}"
- name: Setup multiarch
if: matrix.multiarch
run: |
sudo apt-get install --no-install-recommends -y binfmt-support qemu-user-static
sudo docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
git clone https://github.com/jeking3/bdde.git
echo "$(pwd)/bdde/bin/linux" >> ${GITHUB_PATH}
echo "BDDE_DISTRO=${{ matrix.distro }}" >> ${GITHUB_ENV}
echo "BDDE_EDITION=${{ matrix.edition }}" >> ${GITHUB_ENV}
echo "BDDE_ARCH=${{ matrix.arch }}" >> ${GITHUB_ENV}
echo "B2_WRAPPER=bdde" >> ${GITHUB_ENV}
- name: Setup Boost
env:
B2_ADDRESS_MODEL: ${{matrix.address-model}}
B2_COMPILER: ${{matrix.compiler}}
B2_CXXSTD: ${{matrix.cxxstd}}
B2_SANITIZE: ${{matrix.sanitize}}
B2_STDLIB: ${{matrix.stdlib}}
# More entries can be added in the same way, see the B2_ARGS assignment in ci/enforce.sh for the possible keys.
# B2_DEFINES: ${{matrix.defines}}
# Variables set here (to non-empty) will override the top-level environment variables, e.g.
# B2_VARIANT: ${{matrix.variant}}
run: source ci/github/install.sh
- name: Setup coverage collection
if: matrix.coverage
run: ci/github/codecov.sh "setup"
- name: Run tests
if: '!matrix.coverity'
run: ci/build.sh
- name: Upload coverage
if: matrix.coverage
run: ci/codecov.sh "upload"
- name: Run coverity
if: matrix.coverity && github.event_name == 'push' && (github.ref_name == 'develop' || github.ref_name == 'master')
run: ci/github/coverity.sh
env:
COVERITY_SCAN_NOTIFICATION_EMAIL: ${{ secrets.COVERITY_SCAN_NOTIFICATION_EMAIL }}
COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
windows:
defaults:
run:
shell: cmd
strategy:
fail-fast: false
matrix:
include:
- { toolset: msvc-14.0, cxxstd: '14,latest', addrmd: '32,64', os: windows-2019, variant: 'debug,release' }
- { toolset: msvc-14.2, cxxstd: '14,17,20,latest', addrmd: '32,64', os: windows-2019, variant: 'debug,release' }
- { toolset: msvc-14.3, cxxstd: '14,17,20,latest', addrmd: '32,64', os: windows-2022, variant: 'debug,release' }
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '64', os: windows-2022, variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '/RTCc' }
- { toolset: msvc-14.3, cxxstd: '14', addrmd: '32', os: windows-2022, variant: 'debug', defines: '_ALLOW_RTCc_IN_STL', cxxflags: '"/RTCc /arch:IA32"' }
- { toolset: clang-win, cxxstd: '14,17,latest', addrmd: '32,64', os: windows-2022, variant: 'debug,release' }
- { toolset: gcc, cxxstd: '03,11,14,17,2a', addrmd: '64', os: windows-2019, variant: 'debug,release' }
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v3
- name: Fetch Boost.CI
uses: actions/checkout@v3
with:
repository: boostorg/boost-ci
ref: master
path: boost-ci-cloned
- name: Get CI scripts folder
run: |
REM Copy ci folder if not testing Boost.CI
if "%GITHUB_REPOSITORY%" == "%GITHUB_REPOSITORY:boost-ci=%" xcopy /s /e /q /i /y boost-ci-cloned\ci .\ci
rmdir /s /q boost-ci-cloned
- name: Setup Boost
run: ci\github\install.bat
- name: Run tests
if: '!matrix.coverage'
run: ci\build.bat
env:
B2_TOOLSET: ${{matrix.toolset}}
B2_CXXSTD: ${{matrix.cxxstd}}
B2_ADDRESS_MODEL: ${{matrix.addrmd}}
B2_DEFINES: ${{matrix.defines}}
B2_VARIANT: ${{matrix.variant}}
B2_CXXFLAGS: ${{matrix.cxxflags}}
- name: Collect coverage
shell: powershell
if: matrix.coverage
run: ci\opencppcoverage.ps1
env:
B2_TOOLSET: ${{matrix.toolset}}
B2_CXXSTD: ${{matrix.cxxstd}}
B2_ADDRESS_MODEL: ${{matrix.addrmd}}
- name: Upload coverage
if: matrix.coverage
uses: codecov/codecov-action@v2
with:
files: __out/cobertura.xml
MSYS2:
defaults:
run:
shell: msys2 {0}
strategy:
fail-fast: false
matrix:
include:
- { sys: MINGW32, compiler: gcc, cxxstd: '03,11,17,20' }
- { sys: MINGW64, compiler: gcc, cxxstd: '03,11,17,20' }
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
- name: Setup MSYS2 environment
uses: msys2/setup-msys2@v2
with:
msystem: ${{matrix.sys}}
update: true
install: git python
pacboy: gcc:p cmake:p ninja:p
- name: Fetch Boost.CI
uses: actions/checkout@v3
with:
repository: boostorg/boost-ci
ref: master
path: boost-ci-cloned
- name: Get CI scripts folder
run: |
# Copy ci folder if not testing Boost.CI
[[ "$GITHUB_REPOSITORY" =~ "boost-ci" ]] || cp -r boost-ci-cloned/ci .
rm -rf boost-ci-cloned
- name: Setup Boost
env:
B2_COMPILER: ${{matrix.compiler}}
B2_CXXSTD: ${{matrix.cxxstd}}
B2_SANITIZE: ${{matrix.sanitize}}
B2_STDLIB: ${{matrix.stdlib}}
run: ci/github/install.sh
- name: Run tests
run: ci/build.sh
# Run also the CMake tests to avoid having to setup another matrix for CMake on MSYS
- name: Run CMake tests
run: |
cd "$BOOST_ROOT"
mkdir __build_cmake_test__ && cd __build_cmake_test__
cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=ON -DBUILD_TESTING=ON -DBoost_VERBOSE=ON ..
cmake --build . --target tests --config Debug -j$B2_JOBS
ctest --output-on-failure --build-config Debug
CMake:
defaults:
run:
shell: bash
strategy:
fail-fast: false
matrix:
include:
- { os: ubuntu-20.04, build_shared: ON, build_type: Debug, generator: 'Unix Makefiles' }
- { os: windows-2019, build_shared: ON, build_type: Debug, generator: 'Visual Studio 16 2019' }
timeout-minutes: 120
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v3
- name: Fetch Boost.CI
uses: actions/checkout@v3
with:
repository: boostorg/boost-ci
ref: master
path: boost-ci-cloned
- name: Get CI scripts folder
run: |
# Copy ci folder if not testing Boost.CI
[[ "$GITHUB_REPOSITORY" =~ "boost-ci" ]] || cp -r boost-ci-cloned/ci .
rm -rf boost-ci-cloned
- name: Setup Boost
env: {B2_DONT_BOOTSTRAP: 1}
run: source ci/github/install.sh
- name: Run CMake tests
run: |
cd "$BOOST_ROOT"
mkdir __build_cmake_test__ && cd __build_cmake_test__
cmake -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DBUILD_TESTING=ON -DBoost_VERBOSE=ON ..
cmake --build . --target tests --config ${{matrix.build_type}} -j$B2_JOBS
ctest --output-on-failure --build-config ${{matrix.build_type}}
- name: Run CMake subdir tests
run: |
cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_test" # New unified folder
[ -d "$cmake_test_folder" ] || cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_subdir_test"
cd "$cmake_test_folder"
mkdir __build_cmake_subdir_test__ && cd __build_cmake_subdir_test__
cmake -G "${{matrix.generator}}" -DBOOST_CI_INSTALL_TEST=OFF -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.build_shared}} ..
cmake --build . --config ${{matrix.build_type}} -j$B2_JOBS
ctest --output-on-failure --build-config ${{matrix.build_type}}
- name: Install Library
run: |
cd "$BOOST_ROOT"
mkdir __build_cmake_install_test__ && cd __build_cmake_install_test__
cmake -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBOOST_INCLUDE_LIBRARIES=$SELF -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DCMAKE_INSTALL_PREFIX=~/.local -DBoost_VERBOSE=ON -DBoost_DEBUG=ON ..
cmake --build . --target install --config ${{matrix.build_type}} -j$B2_JOBS
- name: Run CMake install tests
run: |
cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_test" # New unified folder
[ -d "$cmake_test_folder" ] || cmake_test_folder="$BOOST_ROOT/libs/$SELF/test/cmake_install_test"
cd "$cmake_test_folder"
mkdir __build_cmake_install_test__ && cd __build_cmake_install_test__
cmake -G "${{matrix.generator}}" -DBOOST_CI_INSTALL_TEST=ON -DCMAKE_BUILD_TYPE=${{matrix.build_type}} -DBUILD_SHARED_LIBS=${{matrix.build_shared}} -DCMAKE_PREFIX_PATH=~/.local ..
cmake --build . --config ${{matrix.build_type}} -j$B2_JOBS
ctest --output-on-failure --build-config ${{matrix.build_type}}
-2
View File
@@ -1,2 +0,0 @@
/doc/html/
/doc/pdf/
-87
View File
@@ -1,87 +0,0 @@
# Copyright (C) 2016 Daniel James.
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
# Use Trusty to get a reasonably recent version of Boost.
sudo: required
dist: trusty
language: c++
addons:
apt:
packages:
- libxml2-utils
- g++-multilib
matrix:
include:
- compiler: gcc
env: |
label="gcc C++03/11";
user_config="using gcc : : g++-4.8 --coverage -fsanitize=address ;"
enable_coverage=1
CXXSTD=03,11
- compiler: gcc
env: |
label="gcc 32 bit C++11";
user_config="using gcc : : g++-4.8 -m32 -fsanitize=address ;"
CXXSTD=11
- compiler: clang
env: |
label="clang C++11/17";
user_config="using clang : : clang++ -fsanitize=address ;"
CXXSTD=11,17
# sanitized=address not available for 32-bit clang on travis.
- compiler: clang
env: |
label="clang 32 bit";
user_config="using clang : : clang++ -m32 ;"
CXXSTD=03
before_install:
- if [ -n $enable_coverage ]; then pip install --user cpp-coveralls; fi
before_script:
- export BOOST_VERSION=1.67.0
- export BOOST_FILENAME=boost_1_67_0
- export BOOST_ROOT=${HOME}/boost
- cd ${TRAVIS_BUILD_DIR}
- touch Jamroot.jam
- cd $HOME
- echo $user_config > ~/user-config.jam
- cat ~/user-config.jam
- |
# Pick snapshot to use
if [ "$TRAVIS_EVENT_TYPE" == "cron" ]
then
if [ "$TRAVIS_BRANCH" == "master" ]
then
snapshot=master
else
snapshot=develop
fi
else
#snapshot=stable
snapshot=master
fi
# Download and extract snapshot
echo "Downloading ${download_url}"
mkdir $HOME/download
cd $HOME/download
python ${TRAVIS_BUILD_DIR}/ci/download-boost-snapshot.py $snapshot
mv * ${BOOST_ROOT}
- rm -r ${BOOST_ROOT}/boost/unordered
- cd ${BOOST_ROOT}/tools/build
- mkdir ${HOME}/opt
- bash bootstrap.sh
- ./b2 install --prefix=$HOME/opt
after_success:
if [ -n $enable_coverage ]; then coveralls -r ${TRAVIS_BUILD_DIR} -b ${TRAVIS_BUILD_DIR}/test --gcov-options '\-lp' --include include/boost/unordered/ ; fi
script:
- cd ${TRAVIS_BUILD_DIR}/test
- ${HOME}/opt/bin/b2 -j 3 cxxstd=$CXXSTD -q include=${BOOST_ROOT} include=${TRAVIS_BUILD_DIR}/include
- xmllint --noout ${TRAVIS_BUILD_DIR}/doc/ref.xml
-34
View File
@@ -1,34 +0,0 @@
# Generated by `boostdep --cmake unordered`
# Copyright 2020, 2021 Peter Dimov
# Distributed under the Boost Software License, Version 1.0.
# https://www.boost.org/LICENSE_1_0.txt
cmake_minimum_required(VERSION 3.5...3.20)
project(boost_unordered VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
add_library(boost_unordered INTERFACE)
add_library(Boost::unordered ALIAS boost_unordered)
target_include_directories(boost_unordered INTERFACE include)
target_link_libraries(boost_unordered
INTERFACE
Boost::assert
Boost::config
Boost::container_hash
Boost::core
Boost::move
Boost::mp11
Boost::predef
Boost::preprocessor
Boost::throw_exception
Boost::tuple
Boost::type_traits
)
if(BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/test/CMakeLists.txt")
add_subdirectory(test)
endif()
-23
View File
@@ -1,23 +0,0 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
-49
View File
@@ -1,49 +0,0 @@
# Boost.Unordered
Part of collection of the [Boost C++ Libraries](http://github.com/boostorg).
For accessing data based on key lookup, the C++ standard library offers `std::set`, `std::map`, `std::multiset` and `std::multimap`.
These are generally implemented using balanced binary trees so that lookup time has logarithmic complexity.
That is generally okay, but in many cases a hash table can perform better, as accessing data has constant complexity, on average.
The worst case complexity is linear, but that occurs rarely and with some care, can be avoided.
Also, the existing containers require a 'less than' comparison object to order their elements.
For some data types this is impossible to implement or isnt practical.
In contrast, a hash table only needs an equality function and a hash function for the key.
With this in mind, unordered associative containers were added to the C++ standard.
This is an implementation of the containers described in C++11, with some deviations from the standard in order to work with non-C++11 compilers and libraries.
### License
Distributed under the [Boost Software License, Version 1.0](http://www.boost.org/LICENSE_1_0.txt).
### Properties
* C++03
* Header-Only
### Build Status
Branch | GH Actions | Appveyor | codecov.io | Deps | Docs | Tests |
:-------------: | ---------- | -------- | ---------- | ---- | ---- | ----- |
[`master`](https://github.com/boostorg/unordered/tree/master) | [![CI](https://github.com/boostorg/unordered/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/boostorg/unordered/actions/workflows/ci.yml) | [![Build status](https://ci.appveyor.com/api/projects/status/github/boostorg/unordered?branch=master&svg=true)](https://ci.appveyor.com/project/danieljames/unordered-qtwe6/branch/master) | [![codecov](https://codecov.io/gh/boostorg/unordered/branch/master/graph/badge.svg)](https://codecov.io/gh/boostorg/unordered/branch/master) | [![Deps](https://img.shields.io/badge/deps-master-brightgreen.svg)](https://pdimov.github.io/boostdep-report/master/unordered.html) | [![Documentation](https://img.shields.io/badge/docs-master-brightgreen.svg)](https://www.boost.org/doc/libs/master/libs/unordered/doc/html/unordered.html) | [![Enter the Matrix](https://img.shields.io/badge/matrix-master-brightgreen.svg)](http://www.boost.org/development/tests/master/developer/unordered.html)
[`develop`](https://github.com/boostorg/unordered/tree/develop) | [![CI](https://github.com/boostorg/unordered/actions/workflows/ci.yml/badge.svg?branch=develop)](https://github.com/boostorg/unordered/actions/workflows/ci.yml) | [![Build status](https://ci.appveyor.com/api/projects/status/github/boostorg/unordered?branch=develop&svg=true)](https://ci.appveyor.com/project/danieljames/unordered-qtwe6/branch/develop) | [![codecov](https://codecov.io/gh/boostorg/unordered/branch/develop/graph/badge.svg)](https://codecov.io/gh/boostorg/unordered/branch/develop) | [![Deps](https://img.shields.io/badge/deps-develop-brightgreen.svg)](https://pdimov.github.io/boostdep-report/develop/unordered.html) | [![Documentation](https://img.shields.io/badge/docs-develop-brightgreen.svg)](https://www.boost.org/doc/libs/develop/libs/unordered/doc/html/unordered.html) | [![Enter the Matrix](https://img.shields.io/badge/matrix-develop-brightgreen.svg)](http://www.boost.org/development/tests/develop/developer/unordered.html)
### Directories
| Name | Purpose |
| ----------- | ------------------------------ |
| `doc` | documentation |
| `example` | examples |
| `include` | headers |
| `test` | unit tests |
### More information
* [Ask questions](http://stackoverflow.com/questions/ask?tags=c%2B%2B,boost,boost-unordered)
* [Report bugs](https://github.com/boostorg/unordered/issues): Be sure to mention Boost version, platform and compiler you're using. A small compilable code sample to reproduce the problem is always good as well.
* Submit your patches as pull requests against **develop** branch. Note that by submitting patches you agree to license your modifications under the [Boost Software License, Version 1.0](http://www.boost.org/LICENSE_1_0.txt).
* Discussions about the library are held on the [Boost developers mailing list](http://www.boost.org/community/groups.html#main). Be sure to read the [discussion policy](http://www.boost.org/community/policy.html) before posting and add the `[unordered]` tag at the beginning of the subject line.
-38
View File
@@ -1,38 +0,0 @@
# Copyright 2017 Daniel James.
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
# Using clang format 4.0
# http://llvm.org/releases/4.0.0/tools/clang/docs/ClangFormatStyleOptions.html
# Becuase you have to start somewhere.
BasedOnStyle: LLVM
# Basic settings
ColumnLimit: 80
NamespaceIndentation: All
ContinuationIndentWidth: 2
IndentWidth: 2
UseTab: Never
Language: Cpp
Standard: Cpp03
# Code layout
AlignAfterOpenBracket: DontAlign
AlignTrailingComments: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterNamespace: false
AfterClass: true
AfterStruct: true
AfterUnion: true
AfterEnum: true
AfterFunction: true
AfterControlStatement: false
BeforeCatch: false
BeforeElse: false
PointerAlignment: Left
# Boost specific stuff
ForEachMacros: [ BOOST_FOREACH, UNORDERED_AUTO_TEST ]
-3
View File
@@ -1,3 +0,0 @@
enwik8
enwik9
*.exe
-421
View File
@@ -1,421 +0,0 @@
// Copyright 2021 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/core/detail/splitmix64.hpp>
#include <boost/config.hpp>
#ifdef HAVE_ABSEIL
# include "absl/container/node_hash_map.h"
# include "absl/container/flat_hash_map.h"
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
# include "ankerl/unordered_dense.h"
#endif
#include <unordered_map>
#include <vector>
#include <memory>
#include <cstdint>
#include <iostream>
#include <iomanip>
#include <chrono>
using namespace std::chrono_literals;
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint32_t s, std::size_t size )
{
auto t2 = std::chrono::steady_clock::now();
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
t1 = t2;
}
constexpr unsigned N = 2'000'000;
constexpr int K = 10;
static std::vector<std::string> indices1, indices2;
static std::string make_index( unsigned x )
{
char buffer[ 64 ];
std::snprintf( buffer, sizeof(buffer), "pfx_%u_sfx", x );
return buffer;
}
static std::string make_random_index( unsigned x )
{
char buffer[ 64 ];
std::snprintf( buffer, sizeof(buffer), "pfx_%0*d_%u_sfx", x % 8 + 1, 0, x );
return buffer;
}
static void init_indices()
{
indices1.reserve( N*2+1 );
indices1.push_back( make_index( 0 ) );
for( unsigned i = 1; i <= N*2; ++i )
{
indices1.push_back( make_index( i ) );
}
indices2.reserve( N*2+1 );
indices2.push_back( make_index( 0 ) );
{
boost::detail::splitmix64 rng;
for( unsigned i = 1; i <= N*2; ++i )
{
indices2.push_back( make_random_index( static_cast<std::uint32_t>( rng() ) ) );
}
}
}
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
{
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices1[ i ], i } );
}
print_time( t1, "Consecutive insert", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices2[ i ], i } );
}
print_time( t1, "Random insert", 0, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::uint32_t s;
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices1[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Consecutive lookup", s, map.size() );
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices2[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Random lookup", s, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
{
auto it = map.begin();
while( it != map.end() )
{
if( it->second & 1 )
{
if constexpr( std::is_void_v< decltype( map.erase( it ) ) > )
{
map.erase( it++ );
}
else
{
it = map.erase( it );
}
}
else
{
++it;
}
}
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
{
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices1[ i ] );
}
print_time( t1, "Consecutive erase", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices2[ i ] );
}
print_time( t1, "Random erase", 0, map.size() );
std::cout << std::endl;
}
// counting allocator
static std::size_t s_alloc_bytes = 0;
static std::size_t s_alloc_count = 0;
template<class T> struct allocator
{
using value_type = T;
allocator() = default;
template<class U> allocator( allocator<U> const & ) noexcept
{
}
template<class U> bool operator==( allocator<U> const & ) const noexcept
{
return true;
}
template<class U> bool operator!=( allocator<U> const& ) const noexcept
{
return false;
}
T* allocate( std::size_t n ) const
{
s_alloc_bytes += n * sizeof(T);
s_alloc_count++;
return std::allocator<T>().allocate( n );
}
void deallocate( T* p, std::size_t n ) const noexcept
{
s_alloc_bytes -= n * sizeof(T);
s_alloc_count--;
std::allocator<T>().deallocate( p, n );
}
};
//
struct record
{
std::string label_;
long long time_;
std::size_t bytes_;
std::size_t count_;
};
static std::vector<record> times;
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
{
std::cout << label << ":\n\n";
s_alloc_bytes = 0;
s_alloc_count = 0;
Map<std::string, std::uint32_t> map;
auto t0 = std::chrono::steady_clock::now();
auto t1 = t0;
test_insert( map, t1 );
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
test_lookup( map, t1 );
test_iteration( map, t1 );
test_lookup( map, t1 );
test_erase( map, t1 );
auto tN = std::chrono::steady_clock::now();
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
rec.time_ = ( tN - t0 ) / 1ms;
times.push_back( rec );
}
// aliases using the counting allocator
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
template<class K, class V> using std_unordered_map =
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map =
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
template<class K, class V> using absl_flat_hash_map =
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
template<class K, class V> using ankerl_unordered_dense_map =
ankerl::unordered_dense::map<K, V, ankerl::unordered_dense::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
#endif
// fnv1a_hash
template<int Bits> struct fnv1a_hash_impl;
template<> struct fnv1a_hash_impl<32>
{
std::size_t operator()( std::string const& s ) const
{
std::size_t h = 0x811C9DC5u;
char const * first = s.data();
char const * last = first + s.size();
for( ; first != last; ++first )
{
h ^= static_cast<unsigned char>( *first );
h *= 0x01000193ul;
}
return h;
}
};
template<> struct fnv1a_hash_impl<64>
{
std::size_t operator()( std::string const& s ) const
{
std::size_t h = 0xCBF29CE484222325ull;
char const * first = s.data();
char const * last = first + s.size();
for( ; first != last; ++first )
{
h ^= static_cast<unsigned char>( *first );
h *= 0x00000100000001B3ull;
}
return h;
}
};
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits >
{
using is_avalanching = void;
};
template<class K, class V> using std_unordered_map_fnv1a =
std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map_fnv1a =
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map_fnv1a =
boost::unordered_flat_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map_fnv1a =
absl::node_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
template<class K, class V> using absl_flat_hash_map_fnv1a =
absl::flat_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
template<class K, class V> using ankerl_unordered_dense_map_fnv1a =
ankerl::unordered_dense::map<K, V, fnv1a_hash, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
#endif
//
int main()
{
init_indices();
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
test<ankerl_unordered_dense_map>( "ankerl::unordered_dense::map" );
#endif
#ifdef HAVE_ABSEIL
test<absl_node_hash_map>( "absl::node_hash_map" );
test<absl_flat_hash_map>( "absl::flat_hash_map" );
#endif
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
test<boost_unordered_flat_map_fnv1a>( "boost::unordered_flat_map, FNV-1a" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
test<ankerl_unordered_dense_map_fnv1a>( "ankerl::unordered_dense::map, FNV-1a" );
#endif
#ifdef HAVE_ABSEIL
test<absl_node_hash_map_fnv1a>( "absl::node_hash_map, FNV-1a" );
test<absl_flat_hash_map_fnv1a>( "absl::flat_hash_map, FNV-1a" );
#endif
std::cout << "---\n\n";
for( auto const& x: times )
{
std::cout << std::setw( 38 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
}
}
#ifdef HAVE_ABSEIL
# include "absl/container/internal/raw_hash_set.cc"
# include "absl/hash/internal/hash.cc"
# include "absl/hash/internal/low_level_hash.cc"
# include "absl/hash/internal/city.cc"
#endif
-422
View File
@@ -1,422 +0,0 @@
// Copyright 2021 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/core/detail/splitmix64.hpp>
#include <boost/config.hpp>
#ifdef HAVE_ABSEIL
# include "absl/container/node_hash_map.h"
# include "absl/container/flat_hash_map.h"
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
# include "ankerl/unordered_dense.h"
#endif
#include <unordered_map>
#include <string_view>
#include <vector>
#include <memory>
#include <cstdint>
#include <iostream>
#include <iomanip>
#include <chrono>
using namespace std::chrono_literals;
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint32_t s, std::size_t size )
{
auto t2 = std::chrono::steady_clock::now();
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
t1 = t2;
}
constexpr unsigned N = 2'000'000;
constexpr int K = 10;
static std::vector<std::string> indices1, indices2;
static std::string make_index( unsigned x )
{
char buffer[ 64 ];
std::snprintf( buffer, sizeof(buffer), "pfx_%u_sfx", x );
return buffer;
}
static std::string make_random_index( unsigned x )
{
char buffer[ 64 ];
std::snprintf( buffer, sizeof(buffer), "pfx_%0*d_%u_sfx", x % 8 + 1, 0, x );
return buffer;
}
static void init_indices()
{
indices1.reserve( N*2+1 );
indices1.push_back( make_index( 0 ) );
for( unsigned i = 1; i <= N*2; ++i )
{
indices1.push_back( make_index( i ) );
}
indices2.reserve( N*2+1 );
indices2.push_back( make_index( 0 ) );
{
boost::detail::splitmix64 rng;
for( unsigned i = 1; i <= N*2; ++i )
{
indices2.push_back( make_random_index( static_cast<std::uint32_t>( rng() ) ) );
}
}
}
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
{
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices1[ i ], i } );
}
print_time( t1, "Consecutive insert", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices2[ i ], i } );
}
print_time( t1, "Random insert", 0, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::uint32_t s;
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices1[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Consecutive lookup", s, map.size() );
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices2[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Random lookup", s, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
{
auto it = map.begin();
while( it != map.end() )
{
if( it->second & 1 )
{
if constexpr( std::is_void_v< decltype( map.erase( it ) ) > )
{
map.erase( it++ );
}
else
{
it = map.erase( it );
}
}
else
{
++it;
}
}
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
{
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices1[ i ] );
}
print_time( t1, "Consecutive erase", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices2[ i ] );
}
print_time( t1, "Random erase", 0, map.size() );
std::cout << std::endl;
}
// counting allocator
static std::size_t s_alloc_bytes = 0;
static std::size_t s_alloc_count = 0;
template<class T> struct allocator
{
using value_type = T;
allocator() = default;
template<class U> allocator( allocator<U> const & ) noexcept
{
}
template<class U> bool operator==( allocator<U> const & ) const noexcept
{
return true;
}
template<class U> bool operator!=( allocator<U> const& ) const noexcept
{
return false;
}
T* allocate( std::size_t n ) const
{
s_alloc_bytes += n * sizeof(T);
s_alloc_count++;
return std::allocator<T>().allocate( n );
}
void deallocate( T* p, std::size_t n ) const noexcept
{
s_alloc_bytes -= n * sizeof(T);
s_alloc_count--;
std::allocator<T>().deallocate( p, n );
}
};
//
struct record
{
std::string label_;
long long time_;
std::size_t bytes_;
std::size_t count_;
};
static std::vector<record> times;
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
{
std::cout << label << ":\n\n";
s_alloc_bytes = 0;
s_alloc_count = 0;
Map<std::string_view, std::uint32_t> map;
auto t0 = std::chrono::steady_clock::now();
auto t1 = t0;
test_insert( map, t1 );
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
test_lookup( map, t1 );
test_iteration( map, t1 );
test_lookup( map, t1 );
test_erase( map, t1 );
auto tN = std::chrono::steady_clock::now();
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
rec.time_ = ( tN - t0 ) / 1ms;
times.push_back( rec );
}
// aliases using the counting allocator
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
template<class K, class V> using std_unordered_map =
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map =
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
template<class K, class V> using absl_flat_hash_map =
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
template<class K, class V> using ankerl_unordered_dense_map =
ankerl::unordered_dense::map<K, V, ankerl::unordered_dense::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
#endif
// fnv1a_hash
template<int Bits> struct fnv1a_hash_impl;
template<> struct fnv1a_hash_impl<32>
{
std::size_t operator()( std::string_view const& s ) const
{
std::size_t h = 0x811C9DC5u;
char const * first = s.data();
char const * last = first + s.size();
for( ; first != last; ++first )
{
h ^= static_cast<unsigned char>( *first );
h *= 0x01000193ul;
}
return h;
}
};
template<> struct fnv1a_hash_impl<64>
{
std::size_t operator()( std::string_view const& s ) const
{
std::size_t h = 0xCBF29CE484222325ull;
char const * first = s.data();
char const * last = first + s.size();
for( ; first != last; ++first )
{
h ^= static_cast<unsigned char>( *first );
h *= 0x00000100000001B3ull;
}
return h;
}
};
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits >
{
using is_avalanching = void;
};
template<class K, class V> using std_unordered_map_fnv1a =
std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map_fnv1a =
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map_fnv1a =
boost::unordered_flat_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map_fnv1a =
absl::node_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
template<class K, class V> using absl_flat_hash_map_fnv1a =
absl::flat_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
template<class K, class V> using ankerl_unordered_dense_map_fnv1a =
ankerl::unordered_dense::map<K, V, fnv1a_hash, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
#endif
//
int main()
{
init_indices();
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
test<ankerl_unordered_dense_map>( "ankerl::unordered_dense::map" );
#endif
#ifdef HAVE_ABSEIL
test<absl_node_hash_map>( "absl::node_hash_map" );
test<absl_flat_hash_map>( "absl::flat_hash_map" );
#endif
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
test<boost_unordered_flat_map_fnv1a>( "boost::unordered_flat_map, FNV-1a" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
test<ankerl_unordered_dense_map_fnv1a>( "ankerl::unordered_dense::map, FNV-1a" );
#endif
#ifdef HAVE_ABSEIL
test<absl_node_hash_map_fnv1a>( "absl::node_hash_map, FNV-1a" );
test<absl_flat_hash_map_fnv1a>( "absl::flat_hash_map, FNV-1a" );
#endif
std::cout << "---\n\n";
for( auto const& x: times )
{
std::cout << std::setw( 38 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
}
}
#ifdef HAVE_ABSEIL
# include "absl/container/internal/raw_hash_set.cc"
# include "absl/hash/internal/hash.cc"
# include "absl/hash/internal/low_level_hash.cc"
# include "absl/hash/internal/city.cc"
#endif
-346
View File
@@ -1,346 +0,0 @@
// Copyright 2021 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/endian/conversion.hpp>
#include <boost/core/detail/splitmix64.hpp>
#include <boost/config.hpp>
#ifdef HAVE_ABSEIL
# include "absl/container/node_hash_map.h"
# include "absl/container/flat_hash_map.h"
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
# include "ankerl/unordered_dense.h"
#endif
#include <unordered_map>
#include <vector>
#include <memory>
#include <cstdint>
#include <iostream>
#include <iomanip>
#include <chrono>
using namespace std::chrono_literals;
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint32_t s, std::size_t size )
{
auto t2 = std::chrono::steady_clock::now();
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
t1 = t2;
}
constexpr unsigned N = 2'000'000;
constexpr int K = 10;
static std::vector< std::uint32_t > indices1, indices2, indices3;
static void init_indices()
{
indices1.push_back( 0 );
for( unsigned i = 1; i <= N*2; ++i )
{
indices1.push_back( i );
}
indices2.push_back( 0 );
{
boost::detail::splitmix64 rng;
for( unsigned i = 1; i <= N*2; ++i )
{
indices2.push_back( static_cast<std::uint32_t>( rng() ) );
}
}
indices3.push_back( 0 );
for( unsigned i = 1; i <= N*2; ++i )
{
indices3.push_back( boost::endian::endian_reverse( static_cast<std::uint32_t>( i ) ) );
}
}
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
{
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices1[ i ], i } );
}
print_time( t1, "Consecutive insert", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices2[ i ], i } );
}
print_time( t1, "Random insert", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices3[ i ], i } );
}
print_time( t1, "Consecutive reversed insert", 0, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::uint32_t s;
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices1[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Consecutive lookup", s, map.size() );
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices2[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Random lookup", s, map.size() );
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices3[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Consecutive reversed lookup", s, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
{
auto it = map.begin();
while( it != map.end() )
{
if( it->second & 1 )
{
if constexpr( std::is_void_v< decltype( map.erase( it ) ) > )
{
map.erase( it++ );
}
else
{
it = map.erase( it );
}
}
else
{
++it;
}
}
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
{
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices1[ i ] );
}
print_time( t1, "Consecutive erase", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices2[ i ] );
}
print_time( t1, "Random erase", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices3[ i ] );
}
print_time( t1, "Consecutive reversed erase", 0, map.size() );
std::cout << std::endl;
}
// counting allocator
static std::size_t s_alloc_bytes = 0;
static std::size_t s_alloc_count = 0;
template<class T> struct allocator
{
using value_type = T;
allocator() = default;
template<class U> allocator( allocator<U> const & ) noexcept
{
}
template<class U> bool operator==( allocator<U> const & ) const noexcept
{
return true;
}
template<class U> bool operator!=( allocator<U> const& ) const noexcept
{
return false;
}
T* allocate( std::size_t n ) const
{
s_alloc_bytes += n * sizeof(T);
s_alloc_count++;
return std::allocator<T>().allocate( n );
}
void deallocate( T* p, std::size_t n ) const noexcept
{
s_alloc_bytes -= n * sizeof(T);
s_alloc_count--;
std::allocator<T>().deallocate( p, n );
}
};
//
struct record
{
std::string label_;
long long time_;
std::size_t bytes_;
std::size_t count_;
};
static std::vector<record> times;
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
{
std::cout << label << ":\n\n";
s_alloc_bytes = 0;
s_alloc_count = 0;
Map<std::uint32_t, std::uint32_t> map;
auto t0 = std::chrono::steady_clock::now();
auto t1 = t0;
test_insert( map, t1 );
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
test_lookup( map, t1 );
test_iteration( map, t1 );
test_lookup( map, t1 );
test_erase( map, t1 );
auto tN = std::chrono::steady_clock::now();
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
rec.time_ = ( tN - t0 ) / 1ms;
times.push_back( rec );
}
// aliases using the counting allocator
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
template<class K, class V> using std_unordered_map =
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map =
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
template<class K, class V> using absl_flat_hash_map =
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
template<class K, class V> using ankerl_unordered_dense_map =
ankerl::unordered_dense::map<K, V, ankerl::unordered_dense::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
#endif
int main()
{
init_indices();
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
test<ankerl_unordered_dense_map>( "ankerl::unordered_dense::map" );
#endif
#ifdef HAVE_ABSEIL
test<absl_node_hash_map>( "absl::node_hash_map" );
test<absl_flat_hash_map>( "absl::flat_hash_map" );
#endif
std::cout << "---\n\n";
for( auto const& x: times )
{
std::cout << std::setw( 30 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
}
}
#ifdef HAVE_ABSEIL
# include "absl/container/internal/raw_hash_set.cc"
# include "absl/hash/internal/hash.cc"
# include "absl/hash/internal/low_level_hash.cc"
# include "absl/hash/internal/city.cc"
#endif
-356
View File
@@ -1,356 +0,0 @@
// Copyright 2021 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/endian/conversion.hpp>
#include <boost/core/detail/splitmix64.hpp>
#include <boost/config.hpp>
#ifdef HAVE_ABSEIL
# include "absl/container/node_hash_map.h"
# include "absl/container/flat_hash_map.h"
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
# include "ankerl/unordered_dense.h"
#endif
#include <unordered_map>
#include <vector>
#include <memory>
#include <cstdint>
#include <iostream>
#include <iomanip>
#include <chrono>
using namespace std::chrono_literals;
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint64_t s, std::size_t size )
{
auto t2 = std::chrono::steady_clock::now();
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
t1 = t2;
}
constexpr unsigned N = 2'000'000;
constexpr int K = 10;
static std::vector< std::uint64_t > indices1, indices2, indices3;
static void init_indices()
{
indices1.push_back( 0 );
for( unsigned i = 1; i <= N*2; ++i )
{
indices1.push_back( i );
}
indices2.push_back( 0 );
{
boost::detail::splitmix64 rng;
for( unsigned i = 1; i <= N*2; ++i )
{
indices2.push_back( rng() );
}
}
indices3.push_back( 0 );
for( unsigned i = 1; i <= N*2; ++i )
{
indices3.push_back( boost::endian::endian_reverse( static_cast<std::uint64_t>( i ) ) );
}
}
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
{
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices1[ i ], i } );
}
print_time( t1, "Consecutive insert", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices2[ i ], i } );
}
print_time( t1, "Random insert", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices3[ i ], i } );
}
print_time( t1, "Consecutive reversed insert", 0, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::uint64_t s;
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices1[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Consecutive lookup", s, map.size() );
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices2[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Random lookup", s, map.size() );
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices3[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Consecutive reversed lookup", s, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
{
auto it = map.begin();
while( it != map.end() )
{
if( it->second & 1 )
{
if constexpr( std::is_void_v< decltype( map.erase( it ) ) > )
{
map.erase( it++ );
}
else
{
it = map.erase( it );
}
}
else
{
++it;
}
}
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
{
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices1[ i ] );
}
print_time( t1, "Consecutive erase", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices2[ i ] );
}
print_time( t1, "Random erase", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices3[ i ] );
}
print_time( t1, "Consecutive reversed erase", 0, map.size() );
std::cout << std::endl;
}
// counting allocator
static std::size_t s_alloc_bytes = 0;
static std::size_t s_alloc_count = 0;
template<class T> struct allocator
{
using value_type = T;
allocator() = default;
template<class U> allocator( allocator<U> const & ) noexcept
{
}
template<class U> bool operator==( allocator<U> const & ) const noexcept
{
return true;
}
template<class U> bool operator!=( allocator<U> const& ) const noexcept
{
return false;
}
T* allocate( std::size_t n ) const
{
s_alloc_bytes += n * sizeof(T);
s_alloc_count++;
return std::allocator<T>().allocate( n );
}
void deallocate( T* p, std::size_t n ) const noexcept
{
s_alloc_bytes -= n * sizeof(T);
s_alloc_count--;
std::allocator<T>().deallocate( p, n );
}
};
//
struct record
{
std::string label_;
long long time_;
std::size_t bytes_;
std::size_t count_;
};
static std::vector<record> times;
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
{
std::cout << label << ":\n\n";
s_alloc_bytes = 0;
s_alloc_count = 0;
Map<std::uint64_t, std::uint64_t> map;
auto t0 = std::chrono::steady_clock::now();
auto t1 = t0;
test_insert( map, t1 );
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
test_lookup( map, t1 );
test_iteration( map, t1 );
test_lookup( map, t1 );
test_erase( map, t1 );
auto tN = std::chrono::steady_clock::now();
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
rec.time_ = ( tN - t0 ) / 1ms;
times.push_back( rec );
}
// aliases using the counting allocator
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
template<class K, class V> using std_unordered_map =
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map =
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
template<class K, class V> using absl_flat_hash_map =
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
template<class K, class V> using ankerl_unordered_dense_map =
ankerl::unordered_dense::map<K, V, ankerl::unordered_dense::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
#endif
int main()
{
init_indices();
#if defined(BOOST_LIBSTDCXX_VERSION) && __SIZE_WIDTH__ == 32
// Pathological behavior:
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104945
#else
test<std_unordered_map>( "std::unordered_map" );
#endif
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
test<ankerl_unordered_dense_map>( "ankerl::unordered_dense::map" );
#endif
#ifdef HAVE_ABSEIL
test<absl_node_hash_map>( "absl::node_hash_map" );
test<absl_flat_hash_map>( "absl::flat_hash_map" );
#endif
std::cout << "---\n\n";
for( auto const& x: times )
{
std::cout << std::setw( 30 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
}
}
#ifdef HAVE_ABSEIL
# include "absl/container/internal/raw_hash_set.cc"
# include "absl/hash/internal/hash.cc"
# include "absl/hash/internal/low_level_hash.cc"
# include "absl/hash/internal/city.cc"
#endif
-397
View File
@@ -1,397 +0,0 @@
// Copyright 2021, 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/endian/conversion.hpp>
#include <boost/core/detail/splitmix64.hpp>
#include <boost/container_hash/hash.hpp>
#include <boost/config.hpp>
#ifdef HAVE_ABSEIL
# include "absl/container/node_hash_map.h"
# include "absl/container/flat_hash_map.h"
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
# include "ankerl/unordered_dense.h"
#endif
#include <unordered_map>
#include <vector>
#include <memory>
#include <cstdint>
#include <iostream>
#include <iomanip>
#include <chrono>
#include <cstring>
using namespace std::chrono_literals;
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::uint64_t s, std::size_t size )
{
auto t2 = std::chrono::steady_clock::now();
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
t1 = t2;
}
constexpr unsigned N = 2'000'000;
constexpr int K = 10;
struct uuid
{
unsigned char data[ 16 ];
uuid(): data()
{
}
uuid( std::uint64_t low, std::uint64_t high ) noexcept
{
boost::endian::store_little_u64( data + 0, low );
boost::endian::store_little_u64( data + 8, high );
}
inline friend std::size_t hash_value( uuid const& u ) noexcept
{
std::uint64_t low = boost::endian::load_little_u64( u.data + 0 );
std::uint64_t high = boost::endian::load_little_u64( u.data + 8 );
std::size_t r = 0;
boost::hash_combine( r, low );
boost::hash_combine( r, high );
return r;
}
inline friend bool operator==( uuid const& u1, uuid const& u2 ) noexcept
{
return std::memcmp( u1.data, u2.data, 16 ) == 0;
}
};
namespace std
{
template<> struct hash< ::uuid >
{
std::size_t operator()( uuid const& u ) const noexcept
{
return hash_value( u );
}
};
} // namespace std
static std::vector< uuid > indices1, indices2, indices3;
static void init_indices()
{
indices1.push_back( {} );
for( unsigned i = 1; i <= N*2; ++i )
{
indices1.push_back( { i, 0 } );
}
indices2.push_back( {} );
{
boost::detail::splitmix64 rng;
for( unsigned i = 1; i <= N*2; ++i )
{
indices2.push_back( { rng(), rng() } );
}
}
indices3.push_back( {} );
for( unsigned i = 1; i <= N*2; ++i )
{
uuid k( i, 0 );
std::reverse( k.data + 0, k.data + 16 );
indices3.push_back( k );
}
}
template<class Map> BOOST_NOINLINE void test_insert( Map& map, std::chrono::steady_clock::time_point & t1 )
{
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices1[ i ], i } );
}
print_time( t1, "Consecutive insert", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices2[ i ], i } );
}
print_time( t1, "Random insert", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.insert( { indices3[ i ], i } );
}
print_time( t1, "Consecutive reversed insert", 0, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_lookup( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::uint64_t s;
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices1[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Consecutive lookup", s, map.size() );
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices2[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Random lookup", s, map.size() );
s = 0;
for( int j = 0; j < K; ++j )
{
for( unsigned i = 1; i <= N * 2; ++i )
{
auto it = map.find( indices3[ i ] );
if( it != map.end() ) s += it->second;
}
}
print_time( t1, "Consecutive reversed lookup", s, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
{
auto it = map.begin();
while( it != map.end() )
{
if( it->second & 1 )
{
if constexpr( std::is_void_v< decltype( map.erase( it ) ) > )
{
map.erase( it++ );
}
else
{
it = map.erase( it );
}
}
else
{
++it;
}
}
print_time( t1, "Iterate and erase odd elements", 0, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_erase( Map& map, std::chrono::steady_clock::time_point & t1 )
{
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices1[ i ] );
}
print_time( t1, "Consecutive erase", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices2[ i ] );
}
print_time( t1, "Random erase", 0, map.size() );
for( unsigned i = 1; i <= N; ++i )
{
map.erase( indices3[ i ] );
}
print_time( t1, "Consecutive reversed erase", 0, map.size() );
std::cout << std::endl;
}
// counting allocator
static std::size_t s_alloc_bytes = 0;
static std::size_t s_alloc_count = 0;
template<class T> struct allocator
{
using value_type = T;
allocator() = default;
template<class U> allocator( allocator<U> const & ) noexcept
{
}
template<class U> bool operator==( allocator<U> const & ) const noexcept
{
return true;
}
template<class U> bool operator!=( allocator<U> const& ) const noexcept
{
return false;
}
T* allocate( std::size_t n ) const
{
s_alloc_bytes += n * sizeof(T);
s_alloc_count++;
return std::allocator<T>().allocate( n );
}
void deallocate( T* p, std::size_t n ) const noexcept
{
s_alloc_bytes -= n * sizeof(T);
s_alloc_count--;
std::allocator<T>().deallocate( p, n );
}
};
//
struct record
{
std::string label_;
long long time_;
std::size_t bytes_;
std::size_t count_;
};
static std::vector<record> times;
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
{
std::cout << label << ":\n\n";
s_alloc_bytes = 0;
s_alloc_count = 0;
Map<uuid, std::uint64_t> map;
auto t0 = std::chrono::steady_clock::now();
auto t1 = t0;
test_insert( map, t1 );
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
test_lookup( map, t1 );
test_iteration( map, t1 );
test_lookup( map, t1 );
test_erase( map, t1 );
auto tN = std::chrono::steady_clock::now();
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
rec.time_ = ( tN - t0 ) / 1ms;
times.push_back( rec );
}
// aliases using the counting allocator
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
template<class K, class V> using std_unordered_map =
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map =
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
template<class K, class V> using absl_flat_hash_map =
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
template<class K, class V> using ankerl_unordered_dense_map =
ankerl::unordered_dense::map<K, V, ankerl::unordered_dense::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
#endif
int main()
{
init_indices();
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
test<ankerl_unordered_dense_map>( "ankerl::unordered_dense::map" );
#endif
#ifdef HAVE_ABSEIL
test<absl_node_hash_map>( "absl::node_hash_map" );
test<absl_flat_hash_map>( "absl::flat_hash_map" );
#endif
std::cout << "---\n\n";
for( auto const& x: times )
{
std::cout << std::setw( 30 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
}
}
#ifdef HAVE_ABSEIL
# include "absl/container/internal/raw_hash_set.cc"
# include "absl/hash/internal/hash.cc"
# include "absl/hash/internal/low_level_hash.cc"
# include "absl/hash/internal/city.cc"
#endif
-376
View File
@@ -1,376 +0,0 @@
// Copyright 2021, 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/regex.hpp>
#ifdef HAVE_ABSEIL
# include "absl/container/node_hash_map.h"
# include "absl/container/flat_hash_map.h"
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
# include "ankerl/unordered_dense.h"
#endif
#include <unordered_map>
#include <vector>
#include <memory>
#include <cstdint>
#include <iostream>
#include <iomanip>
#include <chrono>
#include <fstream>
#include <string_view>
#include <string>
using namespace std::chrono_literals;
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::size_t s, std::size_t size )
{
auto t2 = std::chrono::steady_clock::now();
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
t1 = t2;
}
static std::vector<std::string> words;
static void init_words()
{
#if SIZE_MAX > UINT32_MAX
char const* fn = "enwik9"; // http://mattmahoney.net/dc/textdata
#else
char const* fn = "enwik8"; // ditto
#endif
auto t1 = std::chrono::steady_clock::now();
std::ifstream is( fn );
std::string in( std::istreambuf_iterator<char>( is ), std::istreambuf_iterator<char>{} );
boost::regex re( "[a-zA-Z]+");
boost::sregex_token_iterator it( in.begin(), in.end(), re, 0 ), end;
words.assign( it, end );
auto t2 = std::chrono::steady_clock::now();
std::cout << fn << ": " << words.size() << " words, " << ( t2 - t1 ) / 1ms << " ms\n\n";
}
template<class Map> BOOST_NOINLINE void test_word_count( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::size_t s = 0;
for( auto const& word: words )
{
++map[ word ];
++s;
}
print_time( t1, "Word count", s, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_contains( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::size_t s = 0;
for( auto const& word: words )
{
std::string_view w2( word );
w2.remove_prefix( 1 );
s += map.contains( w2 );
}
print_time( t1, "Contains", s, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_count( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::size_t s = 0;
for( auto const& word: words )
{
std::string_view w2( word );
w2.remove_prefix( 1 );
s += map.count( w2 );
}
print_time( t1, "Count", s, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::size_t max = 0;
std::string_view word;
for( auto const& x: map )
{
if( x.second > max )
{
word = x.first;
max = x.second;
}
}
print_time( t1, "Iterate and find max element", max, map.size() );
std::cout << std::endl;
}
// counting allocator
static std::size_t s_alloc_bytes = 0;
static std::size_t s_alloc_count = 0;
template<class T> struct allocator
{
using value_type = T;
allocator() = default;
template<class U> allocator( allocator<U> const & ) noexcept
{
}
template<class U> bool operator==( allocator<U> const & ) const noexcept
{
return true;
}
template<class U> bool operator!=( allocator<U> const& ) const noexcept
{
return false;
}
T* allocate( std::size_t n ) const
{
s_alloc_bytes += n * sizeof(T);
s_alloc_count++;
return std::allocator<T>().allocate( n );
}
void deallocate( T* p, std::size_t n ) const noexcept
{
s_alloc_bytes -= n * sizeof(T);
s_alloc_count--;
std::allocator<T>().deallocate( p, n );
}
};
//
struct record
{
std::string label_;
long long time_;
std::size_t bytes_;
std::size_t count_;
};
static std::vector<record> times;
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
{
std::cout << label << ":\n\n";
s_alloc_bytes = 0;
s_alloc_count = 0;
Map<std::string_view, std::size_t> map;
auto t0 = std::chrono::steady_clock::now();
auto t1 = t0;
test_word_count( map, t1 );
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
test_contains( map, t1 );
test_count( map, t1 );
test_iteration( map, t1 );
auto tN = std::chrono::steady_clock::now();
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
rec.time_ = ( tN - t0 ) / 1ms;
times.push_back( rec );
}
// aliases using the counting allocator
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
template<class K, class V> using std_unordered_map =
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map =
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
template<class K, class V> using absl_flat_hash_map =
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
template<class K, class V> using ankerl_unordered_dense_map =
ankerl::unordered_dense::map<K, V, ankerl::unordered_dense::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
#endif
// fnv1a_hash
template<int Bits> struct fnv1a_hash_impl;
template<> struct fnv1a_hash_impl<32>
{
std::size_t operator()( std::string_view const& s ) const
{
std::size_t h = 0x811C9DC5u;
char const * first = s.data();
char const * last = first + s.size();
for( ; first != last; ++first )
{
h ^= static_cast<unsigned char>( *first );
h *= 0x01000193ul;
}
return h;
}
};
template<> struct fnv1a_hash_impl<64>
{
std::size_t operator()( std::string_view const& s ) const
{
std::size_t h = 0xCBF29CE484222325ull;
char const * first = s.data();
char const * last = first + s.size();
for( ; first != last; ++first )
{
h ^= static_cast<unsigned char>( *first );
h *= 0x00000100000001B3ull;
}
return h;
}
};
struct fnv1a_hash: fnv1a_hash_impl< std::numeric_limits<std::size_t>::digits >
{
using is_avalanching = void;
};
template<class K, class V> using std_unordered_map_fnv1a =
std::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map_fnv1a =
boost::unordered_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map_fnv1a =
boost::unordered_flat_map<K, V, fnv1a_hash, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map_fnv1a =
absl::node_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
template<class K, class V> using absl_flat_hash_map_fnv1a =
absl::flat_hash_map<K, V, fnv1a_hash, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
template<class K, class V> using ankerl_unordered_dense_map_fnv1a =
ankerl::unordered_dense::map<K, V, fnv1a_hash, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
#endif
//
int main()
{
init_words();
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
test<ankerl_unordered_dense_map>( "ankerl::unordered_dense::map" );
#endif
#ifdef HAVE_ABSEIL
test<absl_node_hash_map>( "absl::node_hash_map" );
test<absl_flat_hash_map>( "absl::flat_hash_map" );
#endif
test<std_unordered_map_fnv1a>( "std::unordered_map, FNV-1a" );
test<boost_unordered_map_fnv1a>( "boost::unordered_map, FNV-1a" );
test<boost_unordered_flat_map_fnv1a>( "boost::unordered_flat_map, FNV-1a" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
test<ankerl_unordered_dense_map_fnv1a>( "ankerl::unordered_dense::map, FNV-1a" );
#endif
#ifdef HAVE_ABSEIL
test<absl_node_hash_map_fnv1a>( "absl::node_hash_map, FNV-1a" );
test<absl_flat_hash_map_fnv1a>( "absl::flat_hash_map, FNV-1a" );
#endif
std::cout << "---\n\n";
for( auto const& x: times )
{
std::cout << std::setw( 38 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
}
}
#ifdef HAVE_ABSEIL
# include "absl/container/internal/raw_hash_set.cc"
# include "absl/hash/internal/hash.cc"
# include "absl/hash/internal/low_level_hash.cc"
# include "absl/hash/internal/city.cc"
#endif
-238
View File
@@ -1,238 +0,0 @@
// Copyright 2021, 2022 Peter Dimov.
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING
#define _SILENCE_CXX20_CISO646_REMOVED_WARNING
#include <boost/unordered_map.hpp>
#include <boost/unordered/unordered_flat_map.hpp>
#include <boost/regex.hpp>
#ifdef HAVE_ABSEIL
# include "absl/container/node_hash_map.h"
# include "absl/container/flat_hash_map.h"
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
# include "ankerl/unordered_dense.h"
#endif
#include <unordered_map>
#include <vector>
#include <memory>
#include <cstdint>
#include <iostream>
#include <iomanip>
#include <chrono>
#include <fstream>
using namespace std::chrono_literals;
static void print_time( std::chrono::steady_clock::time_point & t1, char const* label, std::size_t s, std::size_t size )
{
auto t2 = std::chrono::steady_clock::now();
std::cout << label << ": " << ( t2 - t1 ) / 1ms << " ms (s=" << s << ", size=" << size << ")\n";
t1 = t2;
}
static std::vector<std::string> words;
static void init_words()
{
#if SIZE_MAX > UINT32_MAX
char const* fn = "enwik9"; // http://mattmahoney.net/dc/textdata
#else
char const* fn = "enwik8"; // ditto
#endif
auto t1 = std::chrono::steady_clock::now();
std::ifstream is( fn );
std::string in( std::istreambuf_iterator<char>( is ), std::istreambuf_iterator<char>{} );
boost::regex re( "[a-zA-Z]+");
boost::sregex_token_iterator it( in.begin(), in.end(), re, 0 ), end;
words.assign( it, end );
auto t2 = std::chrono::steady_clock::now();
std::cout << fn << ": " << words.size() << " words, " << ( t2 - t1 ) / 1ms << " ms\n\n";
}
template<class Map> BOOST_NOINLINE void test_word_size( Map& map, std::chrono::steady_clock::time_point & t1 )
{
for( auto const& word: words )
{
++map[ word.size() ];
}
print_time( t1, "Word size count", 0, map.size() );
std::cout << std::endl;
}
template<class Map> BOOST_NOINLINE void test_iteration( Map& map, std::chrono::steady_clock::time_point & t1 )
{
std::size_t s = 0;
for( auto const& x: map )
{
s += x.second;
}
print_time( t1, "Iterate and sum counts", s, map.size() );
std::cout << std::endl;
}
// counting allocator
static std::size_t s_alloc_bytes = 0;
static std::size_t s_alloc_count = 0;
template<class T> struct allocator
{
using value_type = T;
allocator() = default;
template<class U> allocator( allocator<U> const & ) noexcept
{
}
template<class U> bool operator==( allocator<U> const & ) const noexcept
{
return true;
}
template<class U> bool operator!=( allocator<U> const& ) const noexcept
{
return false;
}
T* allocate( std::size_t n ) const
{
s_alloc_bytes += n * sizeof(T);
s_alloc_count++;
return std::allocator<T>().allocate( n );
}
void deallocate( T* p, std::size_t n ) const noexcept
{
s_alloc_bytes -= n * sizeof(T);
s_alloc_count--;
std::allocator<T>().deallocate( p, n );
}
};
//
struct record
{
std::string label_;
long long time_;
std::size_t bytes_;
std::size_t count_;
};
static std::vector<record> times;
template<template<class...> class Map> BOOST_NOINLINE void test( char const* label )
{
std::cout << label << ":\n\n";
s_alloc_bytes = 0;
s_alloc_count = 0;
Map<std::size_t, std::size_t> map;
auto t0 = std::chrono::steady_clock::now();
auto t1 = t0;
test_word_size( map, t1 );
std::cout << "Memory: " << s_alloc_bytes << " bytes in " << s_alloc_count << " allocations\n\n";
record rec = { label, 0, s_alloc_bytes, s_alloc_count };
test_iteration( map, t1 );
auto tN = std::chrono::steady_clock::now();
std::cout << "Total: " << ( tN - t0 ) / 1ms << " ms\n\n";
rec.time_ = ( tN - t0 ) / 1ms;
times.push_back( rec );
}
// aliases using the counting allocator
template<class K, class V> using allocator_for = ::allocator< std::pair<K const, V> >;
template<class K, class V> using std_unordered_map =
std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_map =
boost::unordered_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
template<class K, class V> using boost_unordered_flat_map =
boost::unordered_flat_map<K, V, boost::hash<K>, std::equal_to<K>, allocator_for<K, V>>;
#ifdef HAVE_ABSEIL
template<class K, class V> using absl_node_hash_map =
absl::node_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
template<class K, class V> using absl_flat_hash_map =
absl::flat_hash_map<K, V, absl::container_internal::hash_default_hash<K>, absl::container_internal::hash_default_eq<K>, allocator_for<K, V>>;
#endif
#ifdef HAVE_ANKERL_UNORDERED_DENSE
template<class K, class V> using ankerl_unordered_dense_map =
ankerl::unordered_dense::map<K, V, ankerl::unordered_dense::hash<K>, std::equal_to<K>, ::allocator< std::pair<K, V> >>;
#endif
int main()
{
init_words();
test<std_unordered_map>( "std::unordered_map" );
test<boost_unordered_map>( "boost::unordered_map" );
test<boost_unordered_flat_map>( "boost::unordered_flat_map" );
#ifdef HAVE_ANKERL_UNORDERED_DENSE
test<ankerl_unordered_dense_map>( "ankerl::unordered_dense::map" );
#endif
#ifdef HAVE_ABSEIL
test<absl_node_hash_map>( "absl::node_hash_map" );
test<absl_flat_hash_map>( "absl::flat_hash_map" );
#endif
std::cout << "---\n\n";
for( auto const& x: times )
{
std::cout << std::setw( 30 ) << ( x.label_ + ": " ) << std::setw( 5 ) << x.time_ << " ms, " << std::setw( 9 ) << x.bytes_ << " bytes in " << x.count_ << " allocations\n";
}
}
#ifdef HAVE_ABSEIL
# include "absl/container/internal/raw_hash_set.cc"
# include "absl/hash/internal/hash.cc"
# include "absl/hash/internal/low_level_hash.cc"
# include "absl/hash/internal/city.cc"
#endif
+50 -12
View File
@@ -1,21 +1,59 @@
# Copyright 2005 Daniel James.
# Distributed under the Boost Software License, Version 1.0. (See accompanying
# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
import asciidoctor ;
using boostbook ;
using quickbook ;
html unordered.html : unordered.adoc ;
path-constant images_location : ../ ;
path-constant admonishment_location : ../../../../doc/src/images ;
install html_ : unordered.html : <location>html ;
xml unordered : unordered.qbk ;
boostbook standalone : unordered :
<xsl:param>chunk.first.sections=1
<xsl:param>chunk.section.depth=2
<xsl:param>generate.section.toc.level=2
<xsl:param>toc.section.depth=1
<xsl:param>toc.max.depth=1
pdf unordered.pdf : unordered.adoc ;
explicit unordered.pdf ;
<xsl:param>boost.compact.typedef=0
<xsl:param>boost.compact.function=0
<xsl:param>boost.compact.enum=0
install pdf_ : unordered.pdf : <location>pdf ;
explicit pdf_ ;
# HTML Options:
<format>html:<xsl:param>boost.root=../../../..
<format>html:<xsl:param>img.src.path=../../../../doc/html/
<format>xhtml:<xsl:param>boost.root=../../../..
<format>xhtml:<xsl:param>img.src.path=../../../../doc/html/
# PDF Options:
# TOC Generation: this is needed for FOP-0.9 and later:
<xsl:param>fop1.extensions=0
<format>pdf:<xsl:param>xep.extensions=1
# TOC generation: this is needed for FOP 0.2, but must not be set to zero for FOP-0.9!
<format>pdf:<xsl:param>fop.extensions=0
# No indent on body text:
<format>pdf:<xsl:param>body.start.indent=0pt
# Margin size:
<format>pdf:<xsl:param>page.margin.inner=0.5in
# Margin size:
<format>pdf:<xsl:param>page.margin.outer=0.5in
# Paper type = A4
<format>pdf:<xsl:param>paper.type=A4
# Yes, we want graphics for admonishments:
<xsl:param>admon.graphics=1
# Set this one for PDF generation *only*:
# default png graphics are awful in PDF form,
# better use SVG's instead:
<format>pdf:<xsl:param>admon.graphics.extension=".svg"
<format>pdf:<xsl:param>use.role.for.mediaobject=1
<format>pdf:<xsl:param>preferred.mediaobject.role=print
<format>pdf:<xsl:param>img.src.path=$(images_location)/
#<format>pdf:<xsl:param>admon.graphics.path=$(admonishment_location)
<format>pdf:<xsl:param>draft.mode="no"
<format>pdf:<xsl:param>boost.url.prefix=http://www.boost.org/doc/libs/release/libs/unordered/doc/html
;
###############################################################################
alias boostdoc ;
explicit boostdoc ;
alias boostrelease : html_ ;
explicit boostrelease ;
+26
View File
@@ -0,0 +1,26 @@
<!--
Copyright Daniel James 2008-2009
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-->
<section id="unordered.bibliography">
<title>Bibliography</title>
<bibliography>
<biblioentry>
<biblioset relation="journal">
<title>C/C++ Users Journal</title>
<date>February, 2006</date>
</biblioset>
<biblioset relation="article">
<authorgroup>
<author>
<firstname>Pete</firstname>
<surname>Becker</surname>
</author>
</authorgroup>
<title><ulink url="http://www.ddj.com/cpp/184402066">STL and TR1: Part III - Unordered containers</ulink></title>
</biblioset>
<para>An introducation to the standard unordered containers.</para>
</biblioentry>
</bibliography>
</section>
+171
View File
@@ -0,0 +1,171 @@
[/ Copyright 2006-2008 Daniel James.
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ]
[section:buckets The Data Structure]
The containers are made up of a number of 'buckets', each of which can contain
any number of elements. For example, the following diagram shows an [classref
boost::unordered_set unordered_set] with 7 buckets containing 5 elements, `A`,
`B`, `C`, `D` and `E` (this is just for illustration, containers will typically
have more buckets).
[diagram buckets]
In order to decide which bucket to place an element in, the container applies
the hash function, `Hash`, to the element's key (for `unordered_set` and
`unordered_multiset` the key is the whole element, but is referred to as the key
so that the same terminology can be used for sets and maps). This returns a
value of type `std::size_t`. `std::size_t` has a much greater range of values
then the number of buckets, so that container applies another transformation to
that value to choose a bucket to place the element in.
Retrieving the elements for a given key is simple. The same process is applied
to the key to find the correct bucket. Then the key is compared with the
elements in the bucket to find any elements that match (using the equality
predicate `Pred`). If the hash function has worked well the elements will be
evenly distributed amongst the buckets so only a small number of elements will
need to be examined.
There is [link unordered.hash_equality more information on hash functions and
equality predicates in the next section].
You can see in the diagram that `A` & `D` have been placed in the same bucket.
When looking for elements in this bucket up to 2 comparisons are made, making
the search slower. This is known as a collision. To keep things fast we try to
keep collisions to a minimum.
'''
<table frame="all"><title>Methods for Accessing Buckets</title>
<tgroup cols="2">
<thead><row>
<entry><para>Method</para></entry>
<entry><para>Description</para></entry>
</row></thead>
<tbody>
<row>
<entry>'''`size_type bucket_count() const`'''</entry>
<entry>'''The number of buckets.'''</entry>
</row>
<row>
<entry>'''`size_type max_bucket_count() const`'''</entry>
<entry>'''An upper bound on the number of buckets.'''</entry>
</row>
<row>
<entry>'''`size_type bucket_size(size_type n) const`'''</entry>
<entry>'''The number of elements in bucket `n`.'''</entry>
</row>
<row>
<entry>'''`size_type bucket(key_type const& k) const`'''</entry>
<entry>'''Returns the index of the bucket which would contain k'''</entry>
</row>
<row>
<entry>'''`local_iterator begin(size_type n);`'''</entry>
<entry morerows='5'>'''Return begin and end iterators for bucket `n`.'''</entry>
</row>
<row>
<entry>'''`local_iterator end(size_type n);`'''</entry>
</row>
<row>
<entry>'''`const_local_iterator begin(size_type n) const;`'''</entry>
</row>
<row>
<entry>'''`const_local_iterator end(size_type n) const;`'''</entry>
</row>
<row>
<entry>'''`const_local_iterator cbegin(size_type n) const;`'''</entry>
</row>
<row>
<entry>'''`const_local_iterator cend(size_type n) const;`'''</entry>
</row>
</tbody>
</tgroup>
</table>
'''
[h2 Controlling the number of buckets]
As more elements are added to an unordered associative container, the number
of elements in the buckets will increase causing performance to degrade.
To combat this the containers increase the bucket count as elements are inserted.
You can also tell the container to change the bucket count (if required) by
calling `rehash`.
The standard leaves a lot of freedom to the implementer to decide how the
number of buckets are chosen, but it does make some requirements based on the
container's 'load factor', the average number of elements per bucket.
Containers also have a 'maximum load factor' which they should try to keep the
load factor below.
You can't control the bucket count directly but there are two ways to
influence it:
* Specify the minimum number of buckets when constructing a container or
when calling `rehash`.
* Suggest a maximum load factor by calling `max_load_factor`.
`max_load_factor` doesn't let you set the maximum load factor yourself, it just
lets you give a /hint/. And even then, the draft standard doesn't actually
require the container to pay much attention to this value. The only time the
load factor is /required/ to be less than the maximum is following a call to
`rehash`. But most implementations will try to keep the number of elements
below the max load factor, and set the maximum load factor to be the same as
or close to the hint - unless your hint is unreasonably small or large.
[table:bucket_size Methods for Controlling Bucket Size
[[Method] [Description]]
[
[`X(size_type n)`]
[Construct an empty container with at least `n` buckets (`X` is the container type).]
]
[
[`X(InputIterator i, InputIterator j, size_type n)`]
[Construct an empty container with at least `n` buckets and insert elements
from the range \[`i`, `j`) (`X` is the container type).]
]
[
[`float load_factor() const`]
[The average number of elements per bucket.]
]
[
[`float max_load_factor() const`]
[Returns the current maximum load factor.]
]
[
[`float max_load_factor(float z)`]
[Changes the container's maximum load factor, using `z` as a hint.]
]
[
[`void rehash(size_type n)`]
[Changes the number of buckets so that there at least n buckets, and
so that the load factor is less than the maximum load factor.]
]
]
[h2 Iterator Invalidation]
It is not specified how member functions other than `rehash` affect
the bucket count, although `insert` is only allowed to invalidate iterators
when the insertion causes the load factor to be greater than or equal to the
maximum load factor. For most implementations this means that insert will only
change the number of buckets when this happens. While iterators can be
invalidated by calls to `insert` and `rehash`, pointers and references to the
container's elements are never invalidated.
In a similar manner to using `reserve` for `vector`s, it can be a good idea
to call `rehash` before inserting a large number of elements. This will get
the expensive rehashing out of the way and let you store iterators, safe in
the knowledge that they won't be invalidated. If you are inserting `n`
elements into container `x`, you could first call:
x.rehash((x.size() + n) / x.max_load_factor() + 1);
[blurb Note: `rehash`'s argument is the minimum number of buckets, not the
number of elements, which is why the new size is divided by the maximum load factor. The
`+ 1` guarantees there is no invalidation; without it, reallocation could occur
if the number of bucket exactly divides the target size, since the container is
allowed to rehash when the load factor is equal to the maximum load factor.]
[endsect]
+231
View File
@@ -0,0 +1,231 @@
[/ Copyright 2008 Daniel James.
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ]
[template ticket[number]'''<ulink
url="https://svn.boost.org/trac/boost/ticket/'''[number]'''">'''#[number]'''</ulink>''']
[section:changes Change Log]
[h2 Review Version]
Initial review version, for the review conducted from 7th December 2007 to
16th December 2007.
[h2 1.35.0 Add-on - 31st March 2008]
Unofficial release uploaded to vault, to be used with Boost 1.35.0. Incorporated
many of the suggestions from the review.
* Improved portability thanks to Boost regression testing.
* Fix lots of typos, and clearer text in the documentation.
* Fix floating point to `std::size_t` conversion when calculating sizes from
the max load factor, and use `double` in the calculation for greater accuracy.
* Fix some errors in the examples.
[h2 Boost 1.36.0]
First official release.
* Rearrange the internals.
* Move semantics - full support when rvalue references are available, emulated
using a cut down version of the Adobe move library when they are not.
* Emplace support when rvalue references and variadic template are available.
* More efficient node allocation when rvalue references and variadic template
are available.
* Added equality operators.
[h2 Boost 1.37.0]
* Rename overload of `emplace` with hint, to `emplace_hint` as specified in
[@http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2691.pdf n2691].
* Provide forwarding headers at `<boost/unordered/unordered_map_fwd.hpp>` and
`<boost/unordered/unordered_set_fwd.hpp>`.
* Move all the implementation inside `boost/unordered`, to assist
modularization and hopefully make it easier to track changes in subversion.
[h2 Boost 1.38.0]
* Use [@boost:/libs/utility/swap.html `boost::swap`].
* [@https://svn.boost.org/trac/boost/ticket/2237 Ticket 2237]:
Document that the equality and inequality operators are undefined for two
objects if their equality predicates aren't equivalent. Thanks to Daniel
Krügler.
* [@https://svn.boost.org/trac/boost/ticket/1710 Ticket 1710]:
Use a larger prime number list. Thanks to Thorsten Ottosen and Hervé
Brönnimann.
* Use
[@boost:/libs/type_traits/doc/html/boost_typetraits/category/alignment.html
aligned storage] to store the types. This changes the way the allocator is
used to construct nodes. It used to construct the node with two calls to
the allocator's `construct` method - once for the pointers and once for the
value. It now constructs the node with a single call to construct and
then constructs the value using in place construction.
* Add support for C++0x initializer lists where they're available (currently
only g++ 4.4 in C++0x mode).
[h2 Boost 1.39.0]
* [@https://svn.boost.org/trac/boost/ticket/2756 Ticket 2756]: Avoid a warning
on Visual C++ 2009.
* Some other minor internal changes to the implementation, tests and
documentation.
* Avoid an unnecessary copy in `operator[]`.
* [@https://svn.boost.org/trac/boost/ticket/2975 Ticket 2975]: Fix length of
prime number list.
[h2 Boost 1.40.0]
* [@https://svn.boost.org/trac/boost/ticket/2975 Ticket 2975]:
Store the prime list as a preprocessor sequence - so that it will always get
the length right if it changes again in the future.
* [@https://svn.boost.org/trac/boost/ticket/1978 Ticket 1978]:
Implement `emplace` for all compilers.
* [@https://svn.boost.org/trac/boost/ticket/2908 Ticket 2908],
[@https://svn.boost.org/trac/boost/ticket/3096 Ticket 3096]:
Some workarounds for old versions of borland, including adding explicit
destructors to all containers.
* [@https://svn.boost.org/trac/boost/ticket/3082 Ticket 3082]:
Disable incorrect Visual C++ warnings.
* Better configuration for C++0x features when the headers aren't available.
* Create less buckets by default.
[h2 Boost 1.41.0 - Major update]
* The original version made heavy use of macros to sidestep some of the older
compilers' poor template support. But since I no longer support those
compilers and the macro use was starting to become a maintenance burden it
has been rewritten to use templates instead of macros for the implementation
classes.
* The container objcet is now smaller thanks to using `boost::compressed_pair`
for EBO and a slightly different function buffer - now using a bool instead
of a member pointer.
* Buckets are allocated lazily which means that constructing an empty container
will not allocate any memory.
[h2 Boost 1.42.0]
* Support instantiating the containers with incomplete value types.
* Reduced the number of warnings (mostly in tests).
* Improved codegear compatibility.
* [@http://svn.boost.org/trac/boost/ticket/3693 Ticket 3693]:
Add `erase_return_void` as a temporary workaround for the current
`erase` which can be inefficient because it has to find the next
element to return an iterator.
* Add templated find overload for compatible keys.
* [@http://svn.boost.org/trac/boost/ticket/3773 Ticket 3773]:
Add missing `std` qualifier to `ptrdiff_t`.
* Some code formatting changes to fit almost all lines into 80 characters.
[h2 Boost 1.43.0]
* [@http://svn.boost.org/trac/boost/ticket/3966 Ticket 3966]:
`erase_return_void` is now `quick_erase`, which is the
[@http://home.roadrunner.com/~hinnant/issue_review/lwg-active.html#579
current forerunner for resolving the slow erase by iterator], although
there's a strong possibility that this may change in the future. The old
method name remains for backwards compatibility but is considered deprecated
and will be removed in a future release.
* Use Boost.Exception.
* Stop using deprecated `BOOST_HAS_*` macros.
[h2 Boost 1.45.0]
* Fix a bug when inserting into an `unordered_map` or `unordered_set` using
iterators which returns `value_type` by copy.
[h2 Boost 1.48.0 - Major update]
This is major change which has been converted to use Boost.Move's move
emulation, and be more compliant with the C++11 standard. See the
[link unordered.compliance compliance section] for details.
The container now meets C++11's complexity requirements, but to do so
uses a little more memory. This means that `quick_erase` and
`erase_return_void` are no longer required, they'll be removed in a
future version.
C++11 support has resulted in some breaking changes:
* Equality comparison has been changed to the C++11 specification.
In a container with equivalent keys, elements in a group with equal
keys used to have to be in the same order to be considered equal,
now they can be a permutation of each other. To use the old
behavior define the macro `BOOST_UNORDERED_DEPRECATED_EQUALITY`.
* The behaviour of swap is different when the two containers to be
swapped has unequal allocators. It used to allocate new nodes using
the appropriate allocators, it now swaps the allocators if
the allocator has a member structure `propagate_on_container_swap`,
such that `propagate_on_container_swap::value` is true.
* Allocator's `construct` and `destroy` functions are called with raw
pointers, rather than the allocator's `pointer` type.
* `emplace` used to emulate the variadic pair constructors that
appeared in early C++0x drafts. Since they were removed it no
longer does so. It does emulate the new `piecewise_construct`
pair constructors - only you need to use
`boost::piecewise_construct`. To use the old emulation of
the variadic consturctors define
`BOOST_UNORDERED_DEPRECATED_PAIR_CONSTRUCT`.
[h2 Boost 1.49.0]
* Fix warning due to accidental odd assignment.
* Slightly better error messages.
[h2 Boost 1.50.0]
* Fix equality for `unordered_multiset` and `unordered_multimap`.
* [@https://svn.boost.org/trac/boost/ticket/6857 Ticket 6857]:
Implement `reserve`.
* [@https://svn.boost.org/trac/boost/ticket/6771 Ticket 6771]:
Avoid gcc's `-Wfloat-equal` warning.
* [@https://svn.boost.org/trac/boost/ticket/6784 Ticket 6784]:
Fix some Sun specific code.
* [@https://svn.boost.org/trac/boost/ticket/6190 Ticket 6190]:
Avoid gcc's `-Wshadow` warning.
* [@https://svn.boost.org/trac/boost/ticket/6905 Ticket 6905]:
Make namespaces in macros compatible with `bcp` custom namespaces.
Fixed by Luke Elliott.
* Remove some of the smaller prime number of buckets, as they may make
collisions quite probable (e.g. multiples of 5 are very common because
we used base 10).
* On old versions of Visual C++, use the container library's implementation
of `allocator_traits`, as it's more likely to work.
* On machines with 64 bit std::size_t, use power of 2 buckets, with Thomas
Wang's hash function to pick which one to use. As modulus is very slow
for 64 bit values.
* Some internal changes.
[h2 Boost 1.51.0]
* Fix construction/destruction issue when using a C++11 compiler with a
C++03 allocator ([ticket 7100]).
* Remove a `try..catch` to support compiling without exceptions.
* Adjust SFINAE use to try to supprt g++ 3.4 ([ticket 7175]).
* Updated to use the new config macros.
[h2 Boost 1.52.0]
* Faster assign, which assigns to existing nodes where possible, rather than
creating entirely new nodes and copy constructing.
* Fixed bug in `erase_range` ([ticket 7471]).
* Reverted some of the internal changes to how nodes are created, especially
for C++11 compilers. 'construct' and 'destroy' should work a little better
for C++11 allocators.
* Simplified the implementation a bit. Hopefully more robust.
[h2 Boost 1.53.0]
* Remove support for the old pre-standard variadic pair constructors, and
equality implementation. Both have been deprecated since Boost 1.48.
* Remove use of deprecated config macros.
* More internal implementation changes, including a much simpler
implementation of `erase`.
[endsect]
+161
View File
@@ -0,0 +1,161 @@
[/ Copyright 2006-2011 Daniel James.
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ]
[section:comparison Comparison with Associative Containers]
[table:interface_differences Interface differences.
[[Associative Containers] [Unordered Associative Containers]]
[
[Parameterized by an ordering relation `Compare`]
[Parameterized by a function object `Hash` and an equivalence relation
`Pred`]
]
[
[Keys can be compared using `key_compare` which is accessed by member function `key_comp()`,
values can be compared using `value_compare` which is accessed by member function `value_comp()`.]
[Keys can be hashed using `hasher` which is accessed by member function `hash_function()`,
and checked for equality using `key_equal` which is accessed by member function `key_eq()`.
There is no function object for compared or hashing values.]
]
[
[Constructors have optional extra parameters for the comparison object.]
[Constructors have optional extra parameters for the initial minimum
number of buckets, a hash function and an equality object.]
]
[
[Keys `k1`, `k2` are considered equivalent if
`!Compare(k1, k2) && !Compare(k2, k1)`]
[Keys `k1`, `k2` are considered equivalent if `Pred(k1, k2)`]
]
[
[Member function `lower_bound(k)` and `upper_bound(k)`]
[No equivalent. Since the elements aren't ordered `lower_bound` and
`upper_bound` would be meaningless.]
]
[
[`equal_range(k)` returns an empty range at the position that k
would be inserted if k isn't present in the container.]
[`equal_range(k)` returns a range at the end of the container if
k isn't present in the container. It can't return a positioned
range as k could be inserted into multiple place. To find out the
bucket that k would be inserted into use `bucket(k)`. But remember
that an insert can cause the container to rehash - meaning that the
element can be inserted into a different bucket.]
]
[
[`iterator`, `const_iterator` are of the bidirectional category.]
[`iterator`, `const_iterator` are of at least the forward category.]
]
[
[Iterators, pointers and references to the container's elements are
never invalidated.]
[[link unordered.buckets.iterator_invalidation Iterators can
be invalidated by calls to insert or rehash]. Pointers and
references to the container's elements are never invalidated.]
]
[
[Iterators iterate through the container in the order defined by
the comparison object.]
[Iterators iterate through the container in an arbitrary order, that
can change as elements are inserted. Although, equivalent elements
are always adjacent.]
]
[
[No equivalent]
[Local iterators can be used to iterate through individual buckets.
(The order of local iterators and iterators aren't
required to have any correspondence.)]
]
[
[Can be compared using the `==`, `!=`, `<`, `<=`, `>`, `>=` operators.]
[Can be compared using the `==` and `!=` operators.]
]
[
[]
[When inserting with a hint, implementations are permitted to ignore
the hint.]
]
[
[`erase` never throws an exception]
[The containers' hash or predicate function can throw exceptions
from `erase`]
]
]
[table:complexity_guarantees Complexity Guarantees
[[Operation] [Associative Containers] [Unordered Associative Containers]]
[
[Construction of empty container]
[constant]
[O(/n/) where /n/ is the minimum number of buckets.]
]
[
[Construction of container from a range of /N/ elements]
[O(/N/ log /N/), O(/N/) if the range is sorted with `value_comp()`]
[Average case O(/N/), worst case
O(/N/'''<superscript>2</superscript>''')]
]
[
[Insert a single element]
[logarithmic]
[Average case constant, worst case linear]
]
[
[Insert a single element with a hint]
[Amortized constant if t elements inserted right after hint,
logarithmic otherwise]
[Average case constant, worst case linear (ie. the same as
a normal insert).]
]
[
[Inserting a range of /N/ elements]
[ /N/ log(`size()`+/N/) ]
[Average case O(/N/), worst case O(/N/ * `size()`)]
]
[
[Erase by key, `k`]
[O(log(`size()`) + `count(k)`)]
[Average case: O(`count(k)`), Worst case: O(`size()`)]
]
[
[Erase a single element by iterator]
[Amortized constant]
[Average case: O(1), Worst case: O(`size()`)]
]
[
[Erase a range of /N/ elements]
[O(log(`size()`) + /N/)]
[Average case: O(/N/), Worst case: O(`size()`)]
]
[
[Clearing the container]
[O(`size()`)]
[O(`size()`)]
]
[
[Find]
[logarithmic]
[Average case: O(1), Worst case: O(`size()`)]
]
[/ TODO: Average case is probably wrong. ]
[
[Count]
[O(log(`size()`) + `count(k)`)]
[Average case: O(1), Worst case: O(`size()`)]
]
[
[`equal_range(k)`]
[logarithmic]
[Average case: O(`count(k)`), Worst case: O(`size()`)]
]
[
[`lower_bound`,`upper_bound`]
[logarithmic]
[n/a]
]
]
[endsect]
+111
View File
@@ -0,0 +1,111 @@
[/ Copyright 2011 Daniel James.
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) ]
[section:compliance C++11 Compliance]
[section:move Move emulation]
Support for move semantics is implemented using Boost.Move. If rvalue
references are available it will use them, but if not it uses a close,
but imperfect emulation. On such compilers you'll need to use Boost.Move
to take advantage of using movable container elements, also note that:
* Non-copyable objects can be stored in the containers, but without support
for rvalue references the container will not be movable.
* Argument forwarding is not perfect.
[endsect]
[section:allocator_compliance Use of allocators]
C++11 introduced a new allocator system. It's backwards compatible due to
the lax requirements for allocators in the old standard, but might need
some changes for allocators which worked with the old versions of the
unordered containers.
It uses a traits class, `allocator_traits` to handle the allocator
adding extra functionality, and making some methods and types optional.
During development a stable release of
`allocator_traits` wasn't available so an internal partial implementation
is always used in this version. Hopefully a future version will use the
standard implementation where available.
The member functions `construct`, `destroy` and `max_size` are now
optional, if they're not available a fallback is used.
A full implementation of `allocator_traits` requires sophisticated
member function detection so that the fallback is used whenever the
member function call is not well formed.
This requires support for SFINAE expressions, which are available on
GCC from version 4.4 and Clang.
On other compilers, there's just a test to see if the allocator has
a member, but no check that it can be called. So rather than using a
fallback there will just be a compile error.
`propagate_on_container_copy_assignment`,
`propagate_on_container_move_assignment`,
`propagate_on_container_swap` and
`select_on_container_copy_construction` are also supported.
Due to imperfect move emulation, some assignments might check
`propagate_on_container_copy_assignment` on some compilers and
`propagate_on_container_move_assignment` on others.
The use of the allocator's construct and destruct methods might be a bit
surprising.
Nodes are constructed and destructed using the allocator, but the elements
are stored in aligned space within the node and constructed and destructed
by calling the constructor and destructor directly.
In C++11 the allocator's construct function has the signature:
template <class U, class... Args>
void construct(U* p, Args&&... args);
which supports calling `construct` for the contained object, but
most existing allocators don't support this. If member function detection
was good enough then with old allocators it would fall back to calling
the element's constructor directly but in general, detection isn't good
enough to do this which is why Boost.Unordered just calls the constructor
directly every time. In most cases this will work okay.
`pointer_traits` aren't used. Instead, pointer types are obtained from
rebound allocators, this can cause problems if the allocator can't be
used with incomplete types. If `const_pointer` is not defined in the
allocator, `boost::pointer_to_other<pointer, const value_type>::type`
is used to obtain a const pointer.
[endsect]
[section:pairs Pairs]
Since the containers use `std::pair` they're limited to the version
from the current standard library. But since C++11 `std::pair`'s
`piecewise_construct` based constructor is very useful, `emplace`
emulates it with a `piecewise_construct` in the `boost::unordered`
namespace. So for example, the following will work:
boost::unordered_multimap<std::string, std::complex> x;
x.emplace(
boost::unordered::piecewise_construct,
boost::make_tuple("key"), boost::make_tuple(1, 2));
Older drafts of the standard also supported variadic constructors
for `std::pair`, where the first argument would be used for the
first part of the pair, and the remaining for the second part.
[endsect]
[section:misc Miscellaneous]
When swapping, `Pred` and `Hash` are not currently swapped by calling
`swap`, their copy constructors are used. As a consequence when swapping
an exception may be throw from their copy constructor.
Variadic constructor arguments for `emplace` are only used when both
rvalue references and variadic template parameters are available.
Otherwise `emplace` can only take up to 10 constructors arguments.
[endsect]
[endsect]
Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

Some files were not shown because too many files have changed in this diff Show More