star-enclosures-2d

Extract enclosures from 2D geometry
git clone git://git.meso-star.fr/star-enclosures-2d.git
Log | Files | Refs | README | LICENSE

commit b727febcd1f3cfdf423d4e9d684298381e2a5b34
parent 434046e459c3944170c94904bf740175fa250299
Author: Christophe Coustet <christophe.coustet@meso-star.com>
Date:   Sun, 28 Apr 2019 13:55:06 +0200

Merge branch 'release_0.3.1'

Diffstat:
MREADME.md | 8+++++++-
Mcmake/CMakeLists.txt | 2+-
Msrc/senc2d_device.c | 11+++++++++++
Msrc/senc2d_device_c.h | 12++++++++++++
Msrc/senc2d_internal_types.h | 5+++++
Msrc/senc2d_scene_analyze.c | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
6 files changed, 105 insertions(+), 14 deletions(-)

diff --git a/README.md b/README.md @@ -35,6 +35,12 @@ variable the install directories of its dependencies. Release notes ------------- +### Version 0.3.1 + +- Performance Fix: when a connex component was canceled by a thread the + complexity was O(n^2). New algorithm is O(n). +- Output execution time for analysis steps in the log. + ### Version 0.3 - Add API calls to access to geometry frontiers. @@ -61,7 +67,7 @@ Release notes License ------- -StarEnclosures 2D is Copyright (C) \\\|Meso\\\|Star&gt; 2018 +StarEnclosures 2D is Copyright (C) |Méso|Star> 2018 (<a href="mailto:contact@meso-star.com" class="email">contact@meso-star.com</a>). It is free software released under the GPLv3+ license. You are welcome to redistribute it under certain conditions; refer to the COPYING files diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -47,7 +47,7 @@ endif() ################################################################################ set(VERSION_MAJOR 0) set(VERSION_MINOR 3) -set(VERSION_PATCH 0) +set(VERSION_PATCH 1) set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) set(SENC2D_FILES_SRC diff --git a/src/senc2d_device.c b/src/senc2d_device.c @@ -73,6 +73,17 @@ log_warn(struct senc2d_device* dev, const char* msg, ...) va_end(vargs_list); } +void +log_info(struct senc2d_device* dev, const char* msg, ...) +{ + va_list vargs_list; + ASSERT(dev && msg); + + va_start(vargs_list, msg); + log_msg(dev, LOG_OUTPUT, msg, vargs_list); + va_end(vargs_list); +} + /******************************************************************************* * Exported functions ******************************************************************************/ diff --git a/src/senc2d_device_c.h b/src/senc2d_device_c.h @@ -56,4 +56,16 @@ log_warn #endif ; +/* Conditionally log a message on the LOG_OUTPUT stream of the device logger, + * with respect to the device verbose flag */ +extern LOCAL_SYM void +log_info + (struct senc2d_device* dev, + const char* msg, + ...) +#ifdef COMPILER_GCC + __attribute((format(printf, 2, 3))) +#endif + ; + #endif /* SENC2D_DEVICE_C_H */ diff --git a/src/senc2d_internal_types.h b/src/senc2d_internal_types.h @@ -123,6 +123,11 @@ SEGSIDE_2_SIDEFLAG(side_id_t s) { return (s & 1) ? FLAG_BACK : FLAG_FRONT; } +static FINLINE enum side_flag +SIDE_CANCELED_FLAG(enum side_flag f) { + return ((unsigned char)f) << 4; +} + static FINLINE side_id_t SEGIDxSIDE_2_SEGSIDE(seg_id_t s, enum side_id i) { ASSERT((((size_t)s << 1) | (i == SIDE_BACK)) < SIDE_MAX__); diff --git a/src/senc2d_scene_analyze.c b/src/senc2d_scene_analyze.c @@ -26,6 +26,7 @@ #include <rsys/hash_table.h> #include <rsys/dynamic_array.h> #include <rsys/dynamic_array_uchar.h> +#include <rsys/clock_time.h> #include <star/s2d.h> @@ -227,6 +228,7 @@ extract_connex_components vrtx_id_t max_y_vrtx_id = VRTX_NULL__; struct cc_descriptor *cc; double max_y = -DBL_MAX; + component_canceled = 0; ASSERT(start_side_id == SIDE_NULL__ || start_side_id < 2 * scn->nusegs); darray_side_id_clear(&current_component); @@ -314,15 +316,21 @@ extract_connex_components enum side_flag nbour_side_id = SEGSIDE_2_SIDEFLAG(neighbour_id); unsigned char* nbour_used = processed + nbour_seg_id; const struct segside* neighbour = segsides + neighbour_id; - if(*nbour_used & nbour_side_id) continue; /* Already processed */ - if(neighbour->medium < m) { - /* Not the same medium. + if(neighbour->medium < m + || (*nbour_used & (unsigned char)SIDE_CANCELED_FLAG(nbour_side_id))) + { + /* 1) Not the same medium. * Neighbour's medium id is less than current medium: the whole * component is to be processed by another thread (possibly the one - * associated with neighbour's medium). */ + * associated with neighbour's medium). + * 2) Neighbour was canceled: no need to replay the component + * again as it will eventually rediscover the side with low medium + * id and recancel all the work in progress. */ component_canceled = 1; darray_side_id_clear(&stack); - /* Reverse the used flag for sides in cancelled component */ + /* Don't cancel used flags as all these sides will get us back to + * (at least) the neighbour side we have just discovered, that will + * cancel them again and again */ sz = darray_side_id_size_get(&current_component); FOR_EACH(ii, 0, sz) { side_id_t used_side @@ -332,11 +340,14 @@ extract_connex_components = SEGSIDE_2_SIDEFLAG(used_side); unsigned char* used = processed + used_trg_id; ASSERT(*used & (unsigned char)used_side_flag); - *used &= 0xFF ^ (unsigned char)used_side_flag; + /* Set the used flag for sides in cancelled component as leading + * to further cancellations */ + *used |= (unsigned char)SIDE_CANCELED_FLAG(used_side_flag); } goto canceled; } + if(*nbour_used & nbour_side_id) continue; /* Already processed */ /* Mark neighbour as processed and stack it */ *nbour_used |= (unsigned char)nbour_side_id; OK2(darray_side_id_push_back(&stack, &neighbour_id)); @@ -375,15 +386,17 @@ canceled: current_media = NULL; /* Write component membership in the global structure - * No need for sync here as an unique thread write a given side */ + * No need for sync here as an unique thread writes a given side */ {STATIC_ASSERT(sizeof(cc->cc_id) >= 4, Cannot_write_IDs_sync_free);} ASSERT(IS_ALIGNED(segments_comp->component, sizeof(cc->cc_id))); FOR_EACH(ii, 0, sz) { const side_id_t s = darray_side_id_cdata_get(&current_component)[ii]; - ASSERT(segments_comp[SEGSIDE_2_SEG(s)].component[SEGSIDE_2_SIDE(s)] - == COMPONENT_NULL__); - segments_comp[SEGSIDE_2_SEG(s)].component[SEGSIDE_2_SIDE(s)] - = cc->cc_id; + seg_id_t segid = SEGSIDE_2_SEG(s); + enum side_id sid = SEGSIDE_2_SIDE(s); + component_id_t* cmp = segments_comp[segid].component; + ASSERT(cmp[sid] == COMPONENT_NULL__); + ASSERT(segsides[s].medium >= m); + cmp[sid] = cc->cc_id; } /* Compute the normal at the max_y vertex. */ @@ -402,7 +415,7 @@ canceled: darray_position_cdata_get(&scn->vertices); double edge[2], normal[2], norm; - /* To garanty that segments with 2 sides in the component total to 0 + /* To ensure that segments with 2 sides in the component total to 0 * regardless of numeric accuracy, we need to prevent them to * contribute (remember than x + y - y == x can be false). */ ASSERT(seg_comp->component[s] == cc->cc_id); (void)s; @@ -1123,6 +1136,8 @@ senc2d_scene_analyze /* Atomic counters to share beetwen threads */ ATOMIC component_count = 0; ATOMIC next_enclosure_id = 1; + char dump[64]; + struct time t0, t1; res_T res = RES_OK; res_T res2 = RES_OK; @@ -1137,6 +1152,11 @@ senc2d_scene_analyze if(!scn->nusegs) goto exit; + #pragma omp single nowait + if(scn->dev->verbose) { + time_current(&t0); + } + darray_segment_tmp_init(scn->dev->allocator, &segments_tmp); segments_tmp_initialized = 1; darray_vrtx_id_init(scn->dev->allocator, &frontiers); @@ -1218,6 +1238,15 @@ senc2d_scene_analyze neighbourhood_by_vertex_initialized = 0; } /* No barrier here */ + #pragma omp single nowait + if(scn->dev->verbose) { + time_sub(&t1, time_current(&t1), &t0); + time_current(&t0); + time_dump(&t1, TIME_MSEC | TIME_SEC | TIME_MIN, NULL, dump, sizeof(dump)); + log_info(scn->dev, + "senc2d_scene_analyze: collect_and_link_neighbours step in %s\n", dump); + } + /* Step 2: extract segment connex components */ extract_connex_components(desc, segsides, &connex_components, &segments_tmp, &segments_comp, &s2d_view, &component_count, &res); @@ -1245,6 +1274,15 @@ senc2d_scene_analyze segments_tmp_initialized = 0; } /* No barrier here */ + #pragma omp single nowait + if(scn->dev->verbose) { + time_sub(&t1, time_current(&t1), &t0); + time_current(&t0); + time_dump(&t1, TIME_MSEC | TIME_SEC | TIME_MIN, NULL, dump, sizeof(dump)); + log_info(scn->dev, + "senc2d_scene_analyze: extract_connex_components step in %s\n", dump); + } + /* Step 3: group components */ group_connex_components(desc, segsides, &segments_comp, &connex_components, s2d_view, &next_enclosure_id, &res); @@ -1268,6 +1306,15 @@ senc2d_scene_analyze s2d_view = NULL; } /* No barrier here */ + #pragma omp single nowait + if(scn->dev->verbose) { + time_sub(&t1, time_current(&t1), &t0); + time_current(&t0); + time_dump(&t1, TIME_MSEC | TIME_SEC | TIME_MIN, NULL, dump, sizeof(dump)); + log_info(scn->dev, + "senc2d_scene_analyze: group_connex_components step in %s\n", dump); + } + /* Step 4: Build result */ build_result(desc, &connex_components, &segments_comp, &frontiers, &res); @@ -1300,6 +1347,16 @@ senc2d_scene_analyze segments_comp_initialized = 0; } } /* No barrier here */ + + #pragma omp single nowait + if(scn->dev->verbose) { + time_sub(&t1, time_current(&t1), &t0); + time_current(&t0); + time_dump(&t1, TIME_MSEC | TIME_SEC | TIME_MIN, NULL, dump, sizeof(dump)); + log_info(scn->dev, + "senc2d_scene_analyze: build_result step in %s\n", dump); + } + error_: ; } /* Implicit barrier here */