--- old/src/hotspot/share/gc/g1/g1CollectedHeap.cpp 2019-11-26 16:18:11.074846434 +0100 +++ new/src/hotspot/share/gc/g1/g1CollectedHeap.cpp 2019-11-26 16:18:10.678844471 +0100 @@ -98,6 +98,7 @@ #include "runtime/threadSMR.hpp" #include "runtime/vmThread.hpp" #include "utilities/align.hpp" +#include "utilities/bitMap.inline.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/stack.inline.hpp" @@ -2366,6 +2367,10 @@ _collection_set.iterate(cl); } +void G1CollectedHeap::collection_set_par_iterate_all(HeapRegionClosure* cl, HeapRegionClaimer* hr_claimer, uint worker_id) { + _collection_set.par_iterate(cl, hr_claimer, worker_id, workers()->active_workers()); +} + void G1CollectedHeap::collection_set_iterate_increment_from(HeapRegionClosure *cl, HeapRegionClaimer* hr_claimer, uint worker_id) { _collection_set.iterate_incremental_part_from(cl, hr_claimer, worker_id, workers()->active_workers()); } @@ -4079,7 +4084,6 @@ assert(!hr->is_free(), "the region should not be free"); assert(!hr->is_empty(), "the region should not be empty"); assert(_hrm->is_available(hr->hrm_index()), "region should be committed"); - assert(free_list != NULL, "pre-condition"); if (G1VerifyBitmaps) { MemRegion mr(hr->bottom(), hr->end()); @@ -4094,7 +4098,9 @@ } hr->hr_clear(skip_remset, true /* clear_space */, locked /* locked */); _policy->remset_tracker()->update_at_free(hr); - free_list->add_ordered(hr); + if (free_list != NULL) { + free_list->add_ordered(hr); + } } void G1CollectedHeap::free_humongous_region(HeapRegion* hr, @@ -4115,6 +4121,11 @@ } +void G1CollectedHeap::abandon_freelist() { + MutexLocker x(FreeList_lock, Mutex::_no_safepoint_check_flag); + _hrm->abandon_free_list(); +} + void G1CollectedHeap::prepend_to_freelist(FreeRegionList* list) { assert(list != NULL, "list can't be null"); if (!list->is_empty()) { @@ -4123,286 +4134,361 @@ } } +void G1CollectedHeap::append_to_freelist(FreeRegionList* list) { + assert(list != NULL, "list can't be null"); + if (!list->is_empty()) { + MutexLocker x(FreeList_lock, Mutex::_no_safepoint_check_flag); + _hrm->append_to_free_list(list); + } +} + void G1CollectedHeap::decrement_summary_bytes(size_t bytes) { decrease_used(bytes); } class G1FreeCollectionSetTask : public AbstractGangTask { -private: - - // Closure applied to all regions in the collection set to do work that needs to - // be done serially in a single thread. - class G1SerialFreeCollectionSetClosure : public HeapRegionClosure { - private: - G1EvacuationInfo* _evacuation_info; - const size_t* _surviving_young_words; - - // Bytes used in successfully evacuated regions before the evacuation. - size_t _before_used_bytes; - // Bytes used in unsucessfully evacuated regions before the evacuation - size_t _after_used_bytes; - - size_t _bytes_allocated_in_old_since_last_gc; - - size_t _failure_used_words; - size_t _failure_waste_words; - - FreeRegionList _local_free_list; + // Helper class to keep statistics for the collection set freeing + class FreeCSetStats { + size_t _before_used_bytes; // Usage in regions successfully evacutate + size_t _after_used_bytes; // Usage in regions failing evacuation + size_t _bytes_allocated_in_old_since_last_gc; // Size of young regions turned into old + size_t _failure_used_words; // Live size in failed regions + size_t _failure_waste_words; // Wasted size in failed regions + size_t _rs_length; // Remembered set size + uint _regions_freed; // Number of regions freed public: - G1SerialFreeCollectionSetClosure(G1EvacuationInfo* evacuation_info, const size_t* surviving_young_words) : - HeapRegionClosure(), - _evacuation_info(evacuation_info), - _surviving_young_words(surviving_young_words), - _before_used_bytes(0), - _after_used_bytes(0), - _bytes_allocated_in_old_since_last_gc(0), - _failure_used_words(0), - _failure_waste_words(0), - _local_free_list("Local Region List for CSet Freeing") { - } + FreeCSetStats() : + _before_used_bytes(0), + _after_used_bytes(0), + _bytes_allocated_in_old_since_last_gc(0), + _failure_used_words(0), + _failure_waste_words(0), + _rs_length(0), + _regions_freed(0) { } + + void merge_stats(FreeCSetStats* other) { + assert(other != NULL, "invariant"); + _before_used_bytes += other->_before_used_bytes; + _after_used_bytes += other->_after_used_bytes; + _bytes_allocated_in_old_since_last_gc += other->_bytes_allocated_in_old_since_last_gc; + _failure_used_words += other->_failure_used_words; + _failure_waste_words += other->_failure_waste_words; + _rs_length += other->_rs_length; + _regions_freed += other->_regions_freed; + } + + void report(G1CollectedHeap* g1h, G1EvacuationInfo* evacuation_info) { + evacuation_info->set_regions_freed(_regions_freed); + evacuation_info->increment_collectionset_used_after(_after_used_bytes); - virtual bool do_heap_region(HeapRegion* r) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); + g1h->decrement_summary_bytes(_before_used_bytes); + g1h->alloc_buffer_stats(G1HeapRegionAttr::Old)->add_failure_used_and_waste(_failure_used_words, _failure_waste_words); - assert(r->in_collection_set(), "Region %u should be in collection set.", r->hrm_index()); - g1h->clear_region_attr(r); + G1Policy *policy = g1h->policy(); + policy->add_bytes_allocated_in_old_since_last_gc(_bytes_allocated_in_old_since_last_gc); + policy->record_rs_length(_rs_length); + policy->cset_regions_freed(); + } - if (r->is_young()) { - assert(r->young_index_in_cset() != 0 && (uint)r->young_index_in_cset() <= g1h->collection_set()->young_region_length(), - "Young index %u is wrong for region %u of type %s with %u young regions", - r->young_index_in_cset(), - r->hrm_index(), - r->get_type_str(), - g1h->collection_set()->young_region_length()); - size_t words_survived = _surviving_young_words[r->young_index_in_cset()]; - r->record_surv_words_in_group(words_survived); - } + void update_used_before(size_t value) { _before_used_bytes += value; } + void update_used_after(size_t value) { _after_used_bytes += value; } + void update_bytes_alloc_in_old(size_t value) { _bytes_allocated_in_old_since_last_gc += value; } + void update_failure_used(size_t value) { _failure_used_words += value; } + void update_failure_waste(size_t value) { _failure_waste_words += value; } + void update_rs_length(size_t value) { _rs_length += value; } + void increment_regions_freed() { _regions_freed += 1; } + }; - if (!r->evacuation_failed()) { - assert(!r->is_empty(), "Region %u is an empty region in the collection set.", r->hrm_index()); - _before_used_bytes += r->used(); - g1h->free_region(r, - &_local_free_list, - true, /* skip_remset */ - true, /* skip_hot_card_cache */ - true /* locked */); - } else { - r->uninstall_surv_rate_group(); - r->clear_young_index_in_cset(); - r->set_evacuation_failed(false); - // When moving a young gen region to old gen, we "allocate" that whole region - // there. This is in addition to any already evacuated objects. Notify the - // policy about that. - // Old gen regions do not cause an additional allocation: both the objects - // still in the region and the ones already moved are accounted for elsewhere. - if (r->is_young()) { - _bytes_allocated_in_old_since_last_gc += HeapRegion::GrainBytes; + // Closure applied to all regions in the collection set. + class G1FreeCollectionSetClosure : public HeapRegionClosure { + // Helper to send JFR events for regions. + class EventForRegion { + EventGCPhaseParallel _event; + public: + EventForRegion(HeapRegion* region, uint worker_id) : _event() { + _event.set_gcId(GCId::current()); + _event.set_gcWorkerId(worker_id); + if (region->is_young()) { + _event.set_name(G1GCPhaseTimes::phase_name(G1GCPhaseTimes::YoungFreeCSet)); + } else { + _event.set_name(G1GCPhaseTimes::phase_name(G1GCPhaseTimes::NonYoungFreeCSet)); } - // The region is now considered to be old. - r->set_old(); - // Do some allocation statistics accounting. Regions that failed evacuation - // are always made old, so there is no need to update anything in the young - // gen statistics, but we need to update old gen statistics. - size_t used_words = r->marked_bytes() / HeapWordSize; + } - _failure_used_words += used_words; - _failure_waste_words += HeapRegion::GrainWords - used_words; + ~EventForRegion() { + _event.commit(); + } + }; - g1h->old_set_add(r); - _after_used_bytes += r->used(); + // Helper to do timing for region work. + class TimerForRegion { + double& _time; + double _start_time; + public: + TimerForRegion(double& time) : _time(time), _start_time(os::elapsedTime()) { } + ~TimerForRegion() { + _time += os::elapsedTime() - _start_time; } - return false; - } + }; - void complete_work() { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); + // G1FreeCollectionSetClosure members + G1CollectedHeap* _g1h; + const size_t* _surviving_young_words; + uint _worker_id; + double _young_time; + double _non_young_time; + FreeCSetStats* _stats; + + void assert_in_cset(HeapRegion* r) { + assert(r->young_index_in_cset() != 0 && + (uint)r->young_index_in_cset() <= _g1h->collection_set()->young_region_length(), + "Young index %u is wrong for region %u of type %s with %u young regions", + r->young_index_in_cset(), r->hrm_index(), r->get_type_str(), _g1h->collection_set()->young_region_length()); + } + + void handle_evacuated_region(HeapRegion* r) { + assert(!r->is_empty(), "Region %u is an empty region in the collection set.", r->hrm_index()); + // Update stats. + stats()->update_used_before(r->used()); + stats()->increment_regions_freed(); - _evacuation_info->set_regions_freed(_local_free_list.length()); - _evacuation_info->increment_collectionset_used_after(_after_used_bytes); + // Free the region and mark it in the bit map so it gets added to the new free list. + r->rem_set()->clear_locked(); + _g1h->free_region(r, NULL, true /* skip_remset */, true /* skip_hot_card_cache */, true /* locked */); + } - g1h->prepend_to_freelist(&_local_free_list); - g1h->decrement_summary_bytes(_before_used_bytes); + void handle_failed_region(HeapRegion* r) { + // Do some allocation statistics accounting. Regions that failed evacuation + // are always made old, so there is no need to update anything in the young + // gen statistics, but we need to update old gen statistics. + size_t used_words = r->marked_bytes() / HeapWordSize; + stats()->update_failure_used(used_words); + stats()->update_failure_waste(HeapRegion::GrainWords - used_words); + stats()->update_used_after(r->used()); + // When moving a young gen region to old gen, we "allocate" that whole + // region there. This is in addition to any already evacuated objects. + // Notify the policy about that. Old gen regions do not cause an + // additional allocation: both the objects still in the region and the + // ones already moved are accounted for elsewhere. + if (r->is_young()) { + stats()->update_bytes_alloc_in_old(HeapRegion::GrainBytes); + } - G1Policy* policy = g1h->policy(); - policy->add_bytes_allocated_in_old_since_last_gc(_bytes_allocated_in_old_since_last_gc); + // Update the region state due to the failed evacuation. + r->handle_evacuation_failed(); - g1h->alloc_buffer_stats(G1HeapRegionAttr::Old)->add_failure_used_and_waste(_failure_used_words, _failure_waste_words); + // Add region to old set, need to hold lock. + MutexLocker x(OldSets_lock, Mutex::_no_safepoint_check_flag); + _g1h->old_set_add(r); } - }; - G1CollectionSet* _collection_set; - G1SerialFreeCollectionSetClosure _cl; - const size_t* _surviving_young_words; - - size_t _rs_length; - - volatile jint _serial_work_claim; - - struct WorkItem { - uint region_idx; - bool is_young; - bool evacuation_failed; - - WorkItem(HeapRegion* r) { - region_idx = r->hrm_index(); - is_young = r->is_young(); - evacuation_failed = r->evacuation_failed(); + double& timer_for_region(HeapRegion* r) { + return r->is_young() ? _young_time : _non_young_time; } - }; - - volatile size_t _parallel_work_claim; - size_t _num_work_items; - WorkItem* _work_items; + public: + G1FreeCollectionSetClosure(const size_t* surviving_young_words, + uint worker_id, + FreeCSetStats* stats) : + HeapRegionClosure(), + _g1h(G1CollectedHeap::heap()), + _surviving_young_words(surviving_young_words), + _worker_id(worker_id), + _young_time(0.0), + _non_young_time(0.0), + _stats(stats) { } - void do_serial_work() { - // Need to grab the lock to be allowed to modify the old region list. - MutexLocker x(OldSets_lock, Mutex::_no_safepoint_check_flag); - _collection_set->iterate(&_cl); - } + virtual bool do_heap_region(HeapRegion* r) { + assert(r->in_collection_set(), "Invariant: %u missing from CSet", r->hrm_index()); + EventForRegion event(r, _worker_id); + TimerForRegion timer(timer_for_region(r)); - void do_parallel_work_for_region(uint region_idx, bool is_young, bool evacuation_failed) { - G1CollectedHeap* g1h = G1CollectedHeap::heap(); + _g1h->clear_region_attr(r); + stats()->update_rs_length(r->rem_set()->occupied_locked()); - HeapRegion* r = g1h->region_at(region_idx); - assert(!g1h->is_on_master_free_list(r), "sanity"); + if (r->is_young()) { + assert_in_cset(r); + r->record_surv_words_in_group(_surviving_young_words[r->young_index_in_cset()]); + } else { + _g1h->hot_card_cache()->reset_card_counts(r); + } - Atomic::add(&_rs_length, r->rem_set()->occupied_locked()); + if (r->evacuation_failed()) { + handle_failed_region(r); + } else { + handle_evacuated_region(r); + } + assert(!_g1h->is_on_master_free_list(r), "sanity"); - if (!is_young) { - g1h->hot_card_cache()->reset_card_counts(r); + return false; } - if (!evacuation_failed) { - r->rem_set()->clear_locked(); + FreeCSetStats* stats() { + return _stats; } - } - - class G1PrepareFreeCollectionSetClosure : public HeapRegionClosure { - private: - size_t _cur_idx; - WorkItem* _work_items; - public: - G1PrepareFreeCollectionSetClosure(WorkItem* work_items) : HeapRegionClosure(), _cur_idx(0), _work_items(work_items) { } - virtual bool do_heap_region(HeapRegion* r) { - _work_items[_cur_idx++] = WorkItem(r); - return false; + void report_timing(double parallel_time) { + G1GCPhaseTimes* pt = _g1h->phase_times(); + pt->record_time_secs(G1GCPhaseTimes::ParFreeCSet, _worker_id, parallel_time); + if (_young_time > 0.0) { + pt->record_time_secs(G1GCPhaseTimes::YoungFreeCSet, _worker_id, _young_time); + } + if (_non_young_time > 0.0) { + pt->record_time_secs(G1GCPhaseTimes::NonYoungFreeCSet, _worker_id, _non_young_time); + } } }; - void prepare_work() { - G1PrepareFreeCollectionSetClosure cl(_work_items); - _collection_set->iterate(&cl); - } + // G1FreeCollectionSetTask members + G1CollectedHeap* _g1h; + G1EvacuationInfo* _evacuation_info; + FreeCSetStats* _worker_stats; + HeapRegionClaimer _claimer; + const size_t* _surviving_young_words; + uint _active_workers; - void complete_work() { - _cl.complete_work(); + FreeCSetStats* worker_stats(uint worker) { + return &_worker_stats[worker]; + } - G1Policy* policy = G1CollectedHeap::heap()->policy(); - policy->record_rs_length(_rs_length); - policy->cset_regions_freed(); + void report_statistics() { + // Merge the accounting + FreeCSetStats total_stats; + for (uint worker = 0; worker < _active_workers; worker++) { + total_stats.merge_stats(worker_stats(worker)); + } + total_stats.report(_g1h, _evacuation_info); } + public: - G1FreeCollectionSetTask(G1CollectionSet* collection_set, G1EvacuationInfo* evacuation_info, const size_t* surviving_young_words) : - AbstractGangTask("G1 Free Collection Set"), - _collection_set(collection_set), - _cl(evacuation_info, surviving_young_words), - _surviving_young_words(surviving_young_words), - _rs_length(0), - _serial_work_claim(0), - _parallel_work_claim(0), - _num_work_items(collection_set->region_length()), - _work_items(NEW_C_HEAP_ARRAY(WorkItem, _num_work_items, mtGC)) { - prepare_work(); + G1FreeCollectionSetTask(G1EvacuationInfo* evacuation_info, const size_t* surviving_young_words, uint active_workers) : + AbstractGangTask("G1 Free Collection Set"), + _g1h(G1CollectedHeap::heap()), + _evacuation_info(evacuation_info), + _worker_stats(NEW_C_HEAP_ARRAY(FreeCSetStats, active_workers, mtGC)), + _claimer(active_workers), + _surviving_young_words(surviving_young_words), + _active_workers(active_workers) { + for (uint worker = 0; worker < active_workers; worker++) { + ::new (&_worker_stats[worker]) FreeCSetStats(); + } } ~G1FreeCollectionSetTask() { - complete_work(); - FREE_C_HEAP_ARRAY(WorkItem, _work_items); + double serial_time = os::elapsedTime(); + report_statistics(); + FREE_C_HEAP_ARRAY(FreeCSetStats, _worker_stats); + _g1h->phase_times()->record_serial_free_cset_time_ms((os::elapsedTime() - serial_time) * 1000.0); } - // Chunk size for work distribution. The chosen value has been determined experimentally - // to be a good tradeoff between overhead and achievable parallelism. - static uint chunk_size() { return 32; } - virtual void work(uint worker_id) { - G1GCPhaseTimes* timer = G1CollectedHeap::heap()->phase_times(); + EventGCPhaseParallel event; + double start = os::elapsedTime(); + G1FreeCollectionSetClosure cl(_surviving_young_words, worker_id, worker_stats(worker_id)); + _g1h->collection_set_par_iterate_all(&cl, &_claimer, worker_id); + + // Report the total parallel time along with some more detailed metrics. + cl.report_timing(os::elapsedTime() - start); + event.commit(GCId::current(), worker_id, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::ParFreeCSet)); + } +}; - // Claim serial work. - if (_serial_work_claim == 0) { - jint value = Atomic::add(&_serial_work_claim, 1) - 1; - if (value == 0) { - double serial_time = os::elapsedTime(); - do_serial_work(); - timer->record_serial_free_cset_time_ms((os::elapsedTime() - serial_time) * 1000.0); - } +class G1RebuildFreeListTask : public AbstractGangTask { + G1CollectedHeap* _g1h; + FreeRegionList* _worker_freelists; + uint _worker_chunk_size; + uint _num_workers; + + FreeRegionList* worker_freelist(uint worker) { return &_worker_freelists[worker]; } + + // Take the partial free lists and link them together. + void rebuild_freelist() { + // Abandon current free list to allow a rebuild using the + // workers chunked free lists. + _g1h->abandon_freelist(); + for (uint worker = 0; worker < _num_workers; worker++) { + _g1h->append_to_freelist(worker_freelist(worker)); } + } +public: + G1RebuildFreeListTask(G1CollectedHeap* g1, uint num_workers) : + AbstractGangTask("G1 Rebuild Free List Task"), + _g1h(g1), + _worker_freelists(NEW_C_HEAP_ARRAY(FreeRegionList, num_workers, mtGC)), + _worker_chunk_size((_g1h->max_regions() + num_workers - 1) / num_workers), + _num_workers(num_workers) { + for (uint worker = 0; worker < _num_workers; worker++) { + ::new (&_worker_freelists[worker]) FreeRegionList("Appendable Worker Free List"); + } + } - // Start parallel work. - double young_time = 0.0; - bool has_young_time = false; - double non_young_time = 0.0; - bool has_non_young_time = false; - - while (true) { - size_t end = Atomic::add(&_parallel_work_claim, chunk_size()); - size_t cur = end - chunk_size(); - - if (cur >= _num_work_items) { - break; - } + ~G1RebuildFreeListTask() { + double serial_time = os::elapsedTime(); + rebuild_freelist(); + FREE_C_HEAP_ARRAY(FreeRegionList, _worker_freelists); + _g1h->phase_times()->record_serial_rebuild_freelist_time_ms((os::elapsedTime() - serial_time) * 1000.0); + } - EventGCPhaseParallel event; - double start_time = os::elapsedTime(); + // Each worker creates a free list for a chunk of the heap. The chunks won't + // be overlapping so we don't need to do any claiming. + virtual void work(uint worker_id) { + double start_time = os::elapsedTime(); + EventGCPhaseParallel event; - end = MIN2(end, _num_work_items); + uint start = worker_id * _worker_chunk_size; + uint end = MIN2(start + _worker_chunk_size, _g1h->max_regions()); - for (; cur < end; cur++) { - bool is_young = _work_items[cur].is_young; + // If start is outside the heap, this worker has nothing to do. + if (start > end) { + return; + } - do_parallel_work_for_region(_work_items[cur].region_idx, is_young, _work_items[cur].evacuation_failed); - - double end_time = os::elapsedTime(); - double time_taken = end_time - start_time; - if (is_young) { - young_time += time_taken; - has_young_time = true; - event.commit(GCId::current(), worker_id, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::YoungFreeCSet)); - } else { - non_young_time += time_taken; - has_non_young_time = true; - event.commit(GCId::current(), worker_id, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::NonYoungFreeCSet)); - } - start_time = end_time; + FreeRegionList* free_list = worker_freelist(worker_id); + for (uint i = start; i < end; i++) { + if (!_g1h->hrm()->is_available(i)) { + continue; + } + HeapRegion* region = _g1h->region_at(i); + if (region->is_free()) { + region->unlink_from_list(); + free_list->add_to_tail(region); } } - if (has_young_time) { - timer->record_time_secs(G1GCPhaseTimes::YoungFreeCSet, worker_id, young_time); - } - if (has_non_young_time) { - timer->record_time_secs(G1GCPhaseTimes::NonYoungFreeCSet, worker_id, non_young_time); - } + event.commit(GCId::current(), worker_id, G1GCPhaseTimes::phase_name(G1GCPhaseTimes::RebuildFreeList)); + _g1h->phase_times()->record_time_secs(G1GCPhaseTimes::RebuildFreeList, worker_id, os::elapsedTime() - start_time); } }; void G1CollectedHeap::free_collection_set(G1CollectionSet* collection_set, G1EvacuationInfo& evacuation_info, const size_t* surviving_young_words) { _eden.clear(); - double free_cset_start_time = os::elapsedTime(); + // The free collections set is split up in two tasks, the first + // frees the collection set and records what regions are free, + // and the second one rebuilds the free list. This proved to be + // more efficient than adding a sorted list to another. + double free_cset_start_time = os::elapsedTime(); { - uint const num_regions = _collection_set.region_length(); - uint const num_chunks = MAX2(num_regions / G1FreeCollectionSetTask::chunk_size(), 1U); - uint const num_workers = MIN2(workers()->active_workers(), num_chunks); + uint const num_cs_regions = _collection_set.region_length(); + uint const num_workers = clamp(num_cs_regions, 1u, workers()->active_workers()); + G1FreeCollectionSetTask cl(&evacuation_info, surviving_young_words, num_workers); + + log_debug(gc, ergo)("Running %s using %u workers for collection set length %u (%u)", + cl.name(), num_workers, num_cs_regions, num_regions()); + workers()->run_task(&cl, num_workers); + } - G1FreeCollectionSetTask cl(collection_set, &evacuation_info, surviving_young_words); + double free_cset_end_time = os::elapsedTime(); + phase_times()->record_total_free_cset_time_ms((free_cset_end_time - free_cset_start_time) * 1000.0); + { + uint const num_workers = workers()->active_workers(); + G1RebuildFreeListTask cl(this, num_workers); - log_debug(gc, ergo)("Running %s using %u workers for collection set length %u", - cl.name(), num_workers, num_regions); + log_debug(gc, ergo)("Running %s using %u workers for rebuilding free list of %u (%u) regions", + cl.name(), num_workers, num_free_regions(), num_regions()); workers()->run_task(&cl, num_workers); } - phase_times()->record_total_free_cset_time_ms((os::elapsedTime() - free_cset_start_time) * 1000.0); + phase_times()->record_total_rebuild_freelist_time_ms((os::elapsedTime() - free_cset_end_time) * 1000.0); collection_set->clear(); }