Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
f928cd4
FIX do while: check associated(node_ptr) before accessing node_ptr%kv
hennink Sep 30, 2021
2b09824
deleted redundant nullify(this%next) after deallocate
hennink Sep 30, 2021
46dc37f
fix intent statement in node_depth: INOUT --> IN
hennink Sep 30, 2021
fdebc81
fix intent statement in n_collisions: INOUT --> IN
hennink Sep 30, 2021
3c609b1
fix intent statement in bucket_count: INOUT --> IN
hennink Sep 30, 2021
51255db
added warning to "reserve" for when n_buckets is too large
hennink Sep 30, 2021
56d1089
fix intent statement in key_count: INOUT --> IN
hennink Sep 30, 2021
62fbb3a
make all procedures non_overridable (clearer and also theoretically, …
hennink Sep 30, 2021
db729c2
fix intent statement in node_get: INOUT --> IN
hennink Sep 30, 2021
6915982
fix intent statement in get: INOUT --> IN
hennink Sep 30, 2021
5f885ff
delete trailing blanks
hennink Sep 30, 2021
ba3c4b5
fix typo in comment
hennink Sep 30, 2021
5419972
use standard "ieor" intead of GNU extension "xor"
hennink Sep 30, 2021
6b6ed50
in "remove": make "sizes" a parameter
hennink Sep 30, 2021
3a65005
added "assert" subroutine
hennink Sep 30, 2021
9b4989a
added missing assertion in "begin"
hennink Sep 30, 2021
025cbf3
generalized hash_value_ints for 64-bit ints
hennink Sep 30, 2021
ffcc069
Makefile: much more extensive flags
hennink Sep 30, 2021
29ed16f
Merge branch 'fix-glitches'
hennink Sep 30, 2021
f82b7ff
simplified "remove"
hennink Sep 30, 2021
5b21919
in remove: "associate" avoids copying node (and is simpler)
hennink Sep 30, 2021
a83be3e
added finalizers to note_type
hennink Sep 30, 2021
fc366c6
Merge branch 'final-method'
hennink Sep 30, 2021
ff7d0b5
implemented optional KEYS_EQUAL_FUNC macro
hennink Oct 1, 2021
1e05187
fhash_modules uses KEYS_EQUAL_FUNC
hennink Oct 1, 2021
01e2c4c
Merge branch 'custum-equality-func'
Oct 4, 2021
7b44d94
implemeneted HASH_FUNC macro
hennink Oct 3, 2021
b53abfa
simplified "hash_value_ints"
hennink Oct 3, 2021
da019e7
BREAKING CHANGE: added defaults for HASH_FUNC
hennink Oct 3, 2021
e0ea059
eliminated redundant "int_module "
hennink Oct 3, 2021
be839ca
corrected dummy declarations in ints_ptr_assign
hennink Oct 3, 2021
c3f7e31
implemented KEY_IS_ARRAY macro for fhash
hennink Oct 3, 2021
686848b
fix alignment in fhash_modules
hennink Oct 3, 2021
cf9263d
deleted redundant statement in fhash
hennink Oct 3, 2021
9fe731f
deleted redundant VALUE_VALUE macro, looked like a mistake
hennink Oct 3, 2021
b6fc25e
deleted redundant special treatment of gfortran
hennink Oct 3, 2021
cc57ceb
updated README
hennink Oct 3, 2021
0051702
Merge branch 'macros-for-funcs'
Oct 4, 2021
eaf080c
do not use `nint` in parameter initialization
Oct 3, 2021
7125a3e
deleted non-portable portion of fhash_test
Oct 3, 2021
3e57ee9
added "benchmark" with fixed-size keys
Oct 3, 2021
5f5a5ae
run benchmark separate from tests, with fixed-size int array
Oct 4, 2021
62a9e20
no finalization for old gortran versions
Oct 4, 2021
8ebbeee
Makefile: ajusted to old gfortran version
Oct 4, 2021
0f8bd0d
deleted benshmark from fhash_test
Oct 4, 2021
a7599b6
Merge branch 'separate-benchmark'
hennink Oct 4, 2021
d91b00f
make 'clear' and 'reserve' elemental
AldoHennink Oct 9, 2021
38f650c
no need to #define KEY_IS_ARRAY
AldoHennink Oct 9, 2021
dca46f3
addd assertion: at least one bucket
AldoHennink Oct 12, 2021
3559147
added assignment oper
AldoHennink Oct 22, 2021
a6673b1
added "deep_storage_size" function
AldoHennink Oct 25, 2021
0d563b9
added "get_ptr" method
hennink Oct 27, 2021
e0a4878
remove redundant n_buckets attribute; triggers gfortran (compiler?) e…
hennink Oct 27, 2021
7a7f745
make kv_type public
hennink Nov 5, 2021
1f3d113
added hash: i2char
hennink Nov 5, 2021
12a5f19
added "as_list" method to hash
hennink Nov 5, 2021
9392049
added "as_sorted_list" method to hash; FAILS on WSL on Windows?!
hennink Nov 5, 2021
d5994a9
FIX sorting on WSL on Windows by not passing pointer to contained pro…
hennink Nov 5, 2021
693daa1
Make "SHORTNAME" macro mandatory
hennink Nov 5, 2021
a96041d
Makefile: added comments for ifort
hennink Nov 5, 2021
f89728b
less verbose type names
hennink Nov 5, 2021
e6b47fa
delted redundant "#undef"
hennink Nov 5, 2021
22f65fb
simplified node_set
hennink Nov 9, 2021
deda660
added "autoval" option to "get_ptr"
hennink Nov 10, 2021
87bbd75
Merge branch 'as_list'
hennink Nov 10, 2021
15b7815
Merge branch 'auto-val'
hennink Nov 10, 2021
f2db640
made "key_count" elemental
hennink Nov 10, 2021
2ab9cdf
made sort_kv_list public
hennink Nov 10, 2021
9cb0c36
make passed dummy intent(out) in "reserver"
hennink Nov 11, 2021
de048cb
made clear more performant
hennink Nov 11, 2021
611cc71
Makefile: improved rules for benchmark
hennink Nov 11, 2021
dce2c78
simplified clear
hennink Nov 11, 2021
930783d
README: updated benchmark
hennink Nov 11, 2021
0f4d696
Merge branch 'optim-clear'
hennink Nov 11, 2021
d15677e
as_[sorted_]list does not take a dummy
hennink Nov 12, 2021
6a792c8
added "get" to benchmark
hennink Nov 12, 2021
085aef6
sort without putting arrays on the stack (also makes permutation faster)
hennink Nov 14, 2021
c2921e2
simplify node_remove
hennink Nov 19, 2021
dcedd51
simplify node_get so ifort can do tail call resursion (gfortran alrea…
hennink Nov 24, 2021
7195039
FIX memory leak in "remove" (had been there since before the repo fork)
hennink Nov 22, 2021
ad02701
FIX memory leak in test_insert_get_and_remove_int_ints_ptr
hennink Nov 22, 2021
f851a72
deleted redundant nullify
hennink Nov 25, 2021
eda644a
Merge branch 'fix-memory-leak'
hennink Nov 25, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 39 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,15 +1,49 @@
CPPC = g++
FC := gfortran
FFLAGS := -O3 -g -fbounds-check -Wall -Wextra -cpp -Wno-unused-dummy-argument
FFLAGS_BASIC = -g -fbacktrace -std=f2008 -pedantic -Wall -Wextra -cpp
FFLAGS_BASIC += -Werror -Werror=shadow -Werror=intrinsic-shadow -Wuninitialized
FFLAGS_BASIC += -Wunreachable-code -Wconversion
FFLAGS_BASIC += -Waliasing -Wampersand -Wc-binding-type -Wcharacter-truncation
FFLAGS_BASIC += -Wfunction-elimination -Wimplicit-interface -Wimplicit-procedure -Wintrinsic-shadow -Wintrinsics-std -Wline-truncation -Wno-tabs
FFLAGS_BASIC += -Wreal-q-constant -Wsurprising
FFLAGS_BASIC += -Wunused-parameter
FFLAGS_BASIC += -Wno-maybe-uninitialized -Wno-unused-dummy-argument -Wno-error=return-type
FFLAGS_BASIC += -Wno-unused-function
FFLAGS_BASIC += -Wno-conversion
FFLAGS_BASIC += -Wno-implicit-interface -Wno-strict-overflow # implicit interface is necessary for calling qsort with general types. Conversions from/to C ints are harmless.

.PHONY: all test clean ref
FFLAGS_DEVEL = -O0 -fcheck=all -fbounds-check -Warray-bounds -Wstrict-overflow=5 -Wunderflow -ffpe-trap=invalid,zero,overflow
# FFLAGS_DEVEL += -ftrapv
FFLAGS_RELEASE = -O3

# not yet in gfortran 4.8.5:
# FFLAGS_BASIC += -Wdo-subscript -std=f2018 -Wfrontend-loop-interchange
# FFLAGS_DEVEL += -fsanitize-address-use-after-scope

# CPPC = icpc
# FC = ifort
# FFLAGS_BASIC = -g -traceback -cpp
# FFLAGS_DEVEL = -O0
# FFLAGS_RELEASE = -Ofast

FFLAGS = $(FFLAGS_DEVEL) $(FFLAGS_BASIC)

.PHONY: all test clean

all: test

test: fhash_modules fhash_test.f90
$(FC) $(FFLAGS) fhash_modules.f90 fhash_test.f90 -o fhash_test.out && ./fhash_test.out
$(FC) $(FFLAGS) fhash_modules.f90 fhash_test.f90 -o fhash_test.out \
&& ./fhash_test.out

benchmark: fhash_benchmark.out stl_benchmark.out
./fhash_benchmark.out && ./stl_benchmark.out

fhash_benchmark.out: fhash.f90 fhash_modules.f90 benchmark.f90
$(FC) $(FFLAGS_BASIC) $(FFLAGS_RELEASE) fhash_modules.f90 benchmark.f90 -o fhash_benchmark.out

ref: benchmark.cc
g++ -O3 -std=c++14 benchmark.cc -o ref.out && ./ref.out
stl_benchmark.out: benchmark.cc
$(CPPC) -std=c++11 -O3 $< -o $@

clean:
rm -rf *.mod *.o
Expand Down
55 changes: 26 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,41 @@
Fast hash map implementation in fortran

## Description
Implemention of the GCC hashmap structure in Fortran. With the usage of macros, it can support any types of keys and values, as long as you implement (or the compiler provides) the corresponding equal operator(==), assignment operator(=) and the hash_value interface of the key type and the assignment operator of the value type.
Implemention of the GCC hashmap structure in Fortran. It supports any types of keys and values, as long as you set the following macros:

## Benchmarks
* `FHASH_NAME`;

* `KEY_TYPE` and `VALUE_TYPE` with corresponding use statements `KEY_USE` and `VALUE_USE`,

and, optionally,

Here are the benchmarks between my Fortran implementation and GCC 4.8 standard library:
* `KEYS_EQUAL_FUNC`: the comparison operator for the keys (defaults to either `a == b` or `all(a == b)`, depending on whether the key is a scalar;

For 14 integer array as the key, double precision floating point as the value, 10M entries:
* `HASH_FUNC`, which takes a key and returns a hash integer. There are defaults for integers and integer arrays;

* `VALUE_POINTER`: when defined the values are assumed to be pointers.

## Benchmarks

Fortran hash:
For

> Insert: 1.80 s
>
> Clean: 1.70 s
>
> 1.59 GB
* key: integer array of size 2;

GCC unordered_map:
* value: double precision (64-bit) floating point;

> Insert: 2.02 s
>
> Clean: 0.61 s
>
> 1.38 GB
* 10M entries,

For 2 integer array as the key, double precision floating point as the value, 20M entries:
on

Fortran hash:
* Intel(R) Core(TM) i7-6820HQ CPU @ 2.70GHz;

> Insert: 2.66 s
>
> Clean: 2.54 s
>
> 2.57 GB
* Ubuntu 20.04.3 LTS,

GCC unordered_map:
I got

> Insert: 3.60 s
>
> Clean: 1.07 s
>
> 2.16 GB
| | | ifort 2021 | gfortran 9 |
|-----------|---------------|------------|------------|
| *insert* | **fhash** | 2.99 | 2.38 |
| | **C++ (STL)** | 2.80 | 2.69 |
| *clear* | **fhash** | 1.24 | 0.391 |
| | **C++ (STL)** | 0.37 | 0.328 |
52 changes: 30 additions & 22 deletions benchmark.cc
Original file line number Diff line number Diff line change
@@ -1,38 +1,46 @@
#include <boost/functional/hash.hpp>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <ctime>
#include <unordered_map>
#include <vector>

#define N_INTS 2
#define N_KEYS 20000000
constexpr int N_INTS = 2;
constexpr int N_KEYS = 1e7;

int main() {
std::cout << "Start C++ STL benchmark:\n";

typedef std::array<int, N_INTS> KeyType;
std::unordered_map<std::array<int, N_INTS>, double, boost::hash<KeyType>> h;

void benchmark() {
typedef std::vector<int> KeyType;
const std::clock_t start = std::clock();
std::unordered_map<KeyType, double, boost::hash<KeyType>> h;
h.reserve(N_KEYS * 2);
KeyType key(N_INTS);
const double t0 = std::clock();

KeyType key;
for (int i = 1; i <= N_KEYS; i++) {
for (int j = 1; j <= N_INTS; j++) {
key[j - 1] = i + j;
}
h[key] = i * 0.5;
}
const std::clock_t finish = std::clock();
printf("Time insert: %.3g s\n",
static_cast<double>(finish - start) / CLOCKS_PER_SEC);
const double t1 = std::clock();

double val;
for (int i = 1; i <= N_KEYS; i++) {
for (int j = 1; j <= N_INTS; j++) {
key[j - 1] = i + j;
}
val = h[key];
}
const double t2 = std::clock();

h.clear();
}
const double t3 = std::clock();

int main() {
typedef std::vector<int> KeyType;
const std::clock_t start = std::clock();
benchmark();
const std::clock_t finish = std::clock();
printf("Time finish: %.3g s\n",
static_cast<double>(finish - start) / CLOCKS_PER_SEC);
std::cout << "Time to assemble / get / clear:"
<< " " << (t1 - t0) / CLOCKS_PER_SEC
<< " " << (t2 - t1) / CLOCKS_PER_SEC
<< " " << (t3 - t2) / CLOCKS_PER_SEC
<< "\n";

return 0;
}
}
55 changes: 55 additions & 0 deletions benchmark.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#define KEY_ARRAY_SIZE 2

#define FHASH_NAME int2real
#define KEY_TYPE integer, dimension(KEY_ARRAY_SIZE)
#define VALUE_TYPE real(real64)
#define VALUE_USE use, intrinsic :: iso_fortran_env, only: real64
#include "fhash.f90"

program test_benchmark
implicit none

call benchmark(n_ints=KEY_ARRAY_SIZE, n_keys=10**7)

contains
subroutine benchmark(n_ints, n_keys)
use int2real_mod
use iso_fortran_env, only: real64

integer, intent(in) :: n_ints, n_keys

type(int2real_t) :: h
integer :: key(n_ints)
integer :: i, j
real :: t0, t1, t2, t3
real(real64), pointer :: val

write(*,'(a)') "Start fhash benchmark:"

write(*,'("n_ints: ", I0, ", n_keys: ", I0)') n_ints, n_keys

call h%reserve(n_keys * 2)
call cpu_time(t0)

do i = 1, n_keys
do j = 1, n_ints
key(j) = i + j
enddo
call h%set(key, i * 0.5d0)
enddo
call cpu_time(t1)

do i = 1, n_keys
do j = 1, n_ints
key(j) = i + j
enddo
val => h%get_ptr(key) ! , autoval=3.0_real64)
enddo
call cpu_time(t2)

call h%clear()
call cpu_time(t3)

write(*,'(a,3(g15.3))') "Time to assemble/ get / clear: ", t1 - t0, t2 - t1, t3 - t2
end subroutine
end program
Loading