From 203516d8cf47e7fcd030e9a8cb7303ff5138b15e Mon Sep 17 00:00:00 2001 From: Leonardo Vencovsky Date: Fri, 12 Jun 2020 12:34:33 -0300 Subject: [PATCH] v0.24.0 preview --- .travis.yml | 10 +-- CHANGELOG | 18 +++++ README.md | 71 +++++++++++++++++++ benchmarks/C++ Comparison/hashtable/hashmap.c | 46 ++++++++++++ benchmarks/C++ Comparison/hashtable/script.py | 58 +++++++++++++++ .../C++ Comparison/hashtable/unorderedmap.cpp | 16 +++++ examples/sieve.c | 42 +++++++++++ 7 files changed, 256 insertions(+), 5 deletions(-) create mode 100644 benchmarks/C++ Comparison/hashtable/hashmap.c create mode 100644 benchmarks/C++ Comparison/hashtable/script.py create mode 100644 benchmarks/C++ Comparison/hashtable/unorderedmap.cpp create mode 100644 examples/sieve.c diff --git a/.travis.yml b/.travis.yml index 27f2ed8..7a47c6d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,11 +12,11 @@ addons: - cmake - python3.8 script: - - sudo ln -s /usr/bin/gcc-6 /usr/local/bin/gcc - - export CC=gcc-7 + - sudo ln -s /usr/bin/gcc-9 /usr/local/bin/gcc + - sudo ln -s /usr/bin/python3.8 /usr/local/bin/python + - export CC=gcc-9 - gcc --version - - python3 --version - - python3.8 autogen.py - - make coverage + - python --version + - make after_success: - bash <(curl -s https://codecov.io/bash) diff --git a/CHANGELOG b/CHANGELOG index 922e52d..b125b5b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,22 @@ # RELEASE.MAJOR.MINOR +0.24.0 + A lot of breaking changes, with the library more modular now + Comeback of PUBLIC and PRIVATE for the CMC sub-library + Callbacks are now optional and can be turned off by defining CMC_NO_CALLBACKS + Added EXT library + New macro CMC_CAMEL_CASE can change the library's style where the default is snake case + Added: + ./cor/alloc.h + ./cor/callbacks.h + ./cor/heap.h + Complete rework of tests and code coverage + Removed cmc_flags, where now every flag is an enum (CMC_FLAGS_OK, CMC_FLAGS_ERROR, etc) + Different include guards + New way to generate code using PARAM + Code now completely changed by using the CMC_ macro to concatenate tokens + Token concatenation with SNAME now uses special macros that allow customization of the suffixes + 0.23.1 Added ./cor/bitset.h Added foreach tests @@ -8,6 +25,7 @@ 0.23.0 Added ./cmc/bitset.h + Added ./cor/flags.h Added ./utl/futils.h Added ./utl/mutex.h Added ./utl/thread.h diff --git a/README.md b/README.md index d85b64f..efd7eec 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,77 @@ ## Table of Contents +* Installation +* Usage +* Contributing +* License +* Features + +## Installation + +No installation is required. The entire library is made of header files and can be directly included into your project. + +## Usage + +The header `macro_collections.h` includes every feature from the library and comes with higher level APIs that help you generate collections from specific sub-libraries with specific extensions. + +```c +// WIP +#include "macro_collections.h" + +// A PARAM is a standard way to pass required parameters to the lower level API. +// It is a tuple of form (PFX, SNAME, SIZE, K, V). In this case, not all of them +// are used, so we can leave them empty. +#define MY_LIST_PARAMS (si32, stacki32, , , int32_t) + +// High level API. Generate a LIST from the CMC library with the STR extension +C_MACRO_COLLECTIONS_GEN(PUBLIC, CMC, LIST, MY_LIST_PARAMS, (STR)) + +bool int_str(FILE *fptr, int val) +{ + return 0 <= fprintf(fptr, "%d", val); +} + +int main(void) +{ + struct stack_int *stack = sint_new(100, &(struct stack_int_fval) { .str = int_str }); + + si32_free(stack); +} + +``` + +## Contributing + +## Features + +The C Macro Collections library is organized into many other sub-libraries. The following table is a quick overview. + +| Library | Name | Description | +| :-----: | -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| CMC | The main C Macro Collections | The main library with variably-sized collections | +| COR | Core | Core functionalities used by more than one collection, usually from different sub-libraries | +| EXT | Extensions | Extension to collections from CMC, SAC and TSC libraries | +| INT | Integrations | Macros that facilitate the creation of code that involves more than one type of collection, e.g. transforming a set to a list | +| SAC | Statically Allocated Collections | Collections with a fixed sized array that don't use any heap allocation | +| TSC | Thread-Safe Collections | Collections that allow multiple operations from multiple threads | +| UTL | Utilities | Utilities for the library | + +Every macro that generates code for a certain collection can be found with the following template. Some exceptions exist as certain collections don't have or can't have some features. One big example is the `UTL` library, which does not follow this pattern. + +``` +macro_name := CMC_[ lib ]_[ collection ]_[ part ]_[ file ] + +lib := CMC | COR | DEV | EXT | INT | SAC | TSC +collection := BITSET | DEQUE | HASHBIDIMAP | HASHMAP | HASHMULTIMAP | HASHMULTISET | HASHSET | HEAP | INTERVALHEAP | LINKEDLIST | LIST | QUEUE | SORTEDLIST | STACK | TREEBIDIMAP | TREEMAP | TREEMULTIMAP | TREEMULTISET | TREESET +part := CORE | ITER | INIT | SETF | NODE ... +file := HEADER | SOURCE +``` + +__Some `collection`s might not be present in a certain `lib`. Check the documentation.__ + +Every macro is suffixed by `CMC` and each section is separated by an underscore (`_`). The first section is the library (`lib`). The second is the collection name in all uppercase. Then the `part` (or which feature from it) that you wish to generate. And last, if it is code that should belong to a header file or code that should belong to a source file. + * Project Structure * Available Collections * Features diff --git a/benchmarks/C++ Comparison/hashtable/hashmap.c b/benchmarks/C++ Comparison/hashtable/hashmap.c new file mode 100644 index 0000000..d2c4dbe --- /dev/null +++ b/benchmarks/C++ Comparison/hashtable/hashmap.c @@ -0,0 +1,46 @@ +#ifndef COUNT +#error "Please define COUNT" +#endif + +#include "cmc/hashmap.h" + +CMC_CMC_HASHMAP_CORE(PUBLIC, HEADER, (hmi32, hashmapi32, , int32_t, int32_t)) +CMC_CMC_HASHMAP_CORE(PUBLIC, SOURCE, (hmi32, hashmapi32, , int32_t, int32_t)) + +int cmp(int32_t a, int32_t b) +{ + return (a > b) - (a < b); +} + +size_t hash(int32_t a) +{ + size_t x = (size_t)a; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = ((x >> 16) ^ x) * 0x45d9f3b; + x = (x >> 16) ^ x; + return x; +} + +struct hashmapi32_fkey *fkey = &(struct hashmapi32_fkey) { + .cmp = cmp, + .hash = hash, + NULL +}; + +struct hashmapi32_fval *fval = &(struct hashmapi32_fval) { + .cmp = cmp, + .hash = hash, + NULL +}; + +int main(void) +{ + struct hashmapi32 *map = hmi32_new(COUNT, 0.6, fkey, fval); + + for (int32_t i = 0; i < COUNT; i++) + hmi32_insert(map, i, i); + + hmi32_free(map); + + return 0; +} diff --git a/benchmarks/C++ Comparison/hashtable/script.py b/benchmarks/C++ Comparison/hashtable/script.py new file mode 100644 index 0000000..bcda9fb --- /dev/null +++ b/benchmarks/C++ Comparison/hashtable/script.py @@ -0,0 +1,58 @@ +import sys +import time +import subprocess + +ITERATIONS = 10 +VERBOSE = False + +NL = '\n' + +INCLUDE = "../../../src" +CC = 'gcc' +CCPP = 'g++' # LoL +OPT = '-O3' +COUNT = 2000000 + +# Common args +args = ['-I', INCLUDE, OPT, f'-DCOUNT={COUNT}', '-DCMC_NO_CALLBACKS'] + +c_args = [CC, 'hashmap.c', '-o', 'hashmap.exe'] +cpp_args = [CCPP, 'unorderedmap.cpp', '-I', INCLUDE, '-o', 'unorderedmap.exe', OPT, f'-DCOUNT={COUNT}'] + +c_args.extend(args) +cpp_args.extend(args) + +subprocess.check_output(c_args) +subprocess.check_output(cpp_args) + +total = 0 +for i in range(ITERATIONS): + print(f'{i}...', end='') + sys.stdout.flush() + start_time = time.time() + subprocess.call(['./hashmap.exe']) + + delta = time.time() - start_time + total += delta + + if VERBOSE: + print(f'({delta})') + +avg = int(((total / ITERATIONS) * 1e9) / COUNT) +print(f'{"" if VERBOSE else NL}{total / ITERATIONS} seconds for {COUNT} -> AVG[{avg} ns] [hashmap.exe]{NL if VERBOSE else ""}') + +total = 0 +for i in range(ITERATIONS): + print(f'{i}...', end='') + sys.stdout.flush() + start_time = time.time() + subprocess.call(['./unorderedmap.exe']) + + delta = time.time() - start_time + total += delta + + if VERBOSE: + print(f'({delta})') + +avg = int(((total / ITERATIONS) * 1e9) / COUNT) +print(f'{"" if VERBOSE else NL}{total / ITERATIONS} seconds for {COUNT} -> AVG[{avg} ns] [unorderedmap.exe]{NL if VERBOSE else ""}') diff --git a/benchmarks/C++ Comparison/hashtable/unorderedmap.cpp b/benchmarks/C++ Comparison/hashtable/unorderedmap.cpp new file mode 100644 index 0000000..72cbf0b --- /dev/null +++ b/benchmarks/C++ Comparison/hashtable/unorderedmap.cpp @@ -0,0 +1,16 @@ +#ifndef COUNT +#error "Please define COUNT" +#endif + +#include +#include + +int main(void) +{ + std::unordered_map map(COUNT); + + for (int32_t i = 0; i < COUNT; i++) + map.insert({i, i}); + + return 0; +} diff --git a/examples/sieve.c b/examples/sieve.c new file mode 100644 index 0000000..131f76b --- /dev/null +++ b/examples/sieve.c @@ -0,0 +1,42 @@ +#include +#include "cmc/bitset.h" + +CMC_CMC_BITSET_CORE(PUBLIC, HEADER, (bs, bitset)) +CMC_CMC_BITSET_CORE(PUBLIC, SOURCE, (bs, bitset)) + +// All the prime numbers up to 1 billion +// With -O3: +// 28.583 s runtime +// 120 MB RAM +// 490 MB file size +int main(void) +{ + const size_t n = 1000000000; + + struct bitset *set = bs_new(n); + + if (!set) + return 1; + + bs_set_range(set, 0, n); + + size_t up_to = sqrt((double)n) + 1; + for (size_t i = 2; i < up_to; ++i) + { + if (bs_get(set, i)) + { + for (size_t j = 2 * i; j < n; j += i) + bs_clear(set, j); + } + } + + FILE *file = fopen("primes.txt", "w"); + + for (size_t i = 2; i < n; i++) + { + if (bs_get(set, i)) + fprintf(file, "%" PRIuMAX " ", i); + } + + fclose(file); +}