void ClauseAllocator::updateAllOffsetsAndPointers( Solver* solver , const vector<ClOffset>& offsets ) { //Must be at toplevel, otherwise propBy reset will not work //and also, detachReattacher will fail assert(solver->decisionLevel() == 0); //We are at decision level 0, so we can reset all PropBy-s for (auto& vdata: solver->varData) { vdata.reason = PropBy(); } //Detach long clauses CompleteDetachReatacher detachReattach(solver); detachReattach.detach_nonbins_nontris(); //Make sure all non-freed clauses were accessible from solver const size_t origNumClauses = solver->longIrredCls.size() + solver->longRedCls.size(); if (origNumClauses != offsets.size()) { std::cerr << "ERROR: Not all non-freed clauses are accessible from Solver" << endl << " This usually means that a clause was not freed, i.e. a mem leak" << endl << " no. clauses accessible from solver: " << origNumClauses << endl << " no. clauses non-freed: " << offsets.size() << endl; assert(origNumClauses == offsets.size()); std::exit(-1); } //Clear clauses solver->longIrredCls.clear(); solver->longRedCls.clear(); //Add back to the solver the correct red & irred clauses for(auto offset: offsets) { Clause* cl = ptr(offset); assert(!cl->freed()); //Put it in the right bucket if (cl->red()) { solver->longRedCls.push_back(offset); } else { solver->longIrredCls.push_back(offset); } } //Finally, reattach long clauses detachReattach.reattachLongs(); }
void CNF::find_all_attach() const { #ifdef SLOW_DEBUG for (size_t i = 0; i < watches.size(); i++) { const Lit lit = Lit::toLit(i); for (uint32_t i2 = 0; i2 < watches[lit].size(); i2++) { const Watched& w = watches[lit][i2]; if (!w.isClause()) continue; //Get clause Clause* cl = cl_alloc.ptr(w.get_offset()); assert(!cl->freed()); //Assert watch correctness if ((*cl)[0] != lit && (*cl)[1] != lit ) { std::cerr << "ERROR! Clause " << (*cl) << " not attached?" << endl; assert(false); std::exit(-1); } //Clause in one of the lists if (!find_clause(w.get_offset())) { std::cerr << "ERROR! did not find clause " << *cl << endl; assert(false); std::exit(1); } } } find_all_attach(longIrredCls); find_all_attach(longRedCls); #endif }
Clause* BVA::find_cl_for_bva( const vector<Lit>& torem , const bool red ) const { Clause* cl = NULL; for(const Lit lit: torem) { seen[lit.toInt()] = 1; } for(Watched w: solver->watches[torem[0]]) { if (!w.isClause()) continue; cl = solver->cl_alloc.ptr(w.get_offset()); if (cl->red() != red || cl->size() != torem.size() ) { continue; } #ifdef SLOW_DEBUG assert(!cl->freed()); assert(!cl->getRemoved()); #endif bool OK = true; for(const Lit lit: *cl) { if (seen[lit.toInt()] == 0) { OK = false; break; } } if (OK) break; } for(const Lit lit: torem) { seen[lit.toInt()] = 0; } assert(cl != NULL); return cl; }
void ClauseAllocator::move_one_watchlist( watch_subarray& ws, ClOffset* newDataStart, ClOffset*& new_ptr) { for(Watched& w: ws) { if (w.isClause()) { Clause* old = ptr(w.get_offset()); assert(!old->freed()); Lit blocked = w.getBlockedLit(); if (old->reloced) { ClOffset new_offset = (*old)[0].toInt(); #ifdef LARGE_OFFSETS new_offset += ((uint64_t)(*old)[1].toInt())<<32; #endif w = Watched(new_offset, blocked); } else { ClOffset new_offset = move_cl(newDataStart, new_ptr, old); w = Watched(new_offset, blocked); } } } }
/** @brief If needed, compacts stacks, removing unused clauses Firstly, the algorithm determines if the number of useless slots is large or small compared to the problem size. If it is small, it does nothing. If it is large, then it allocates new stacks, copies the non-freed clauses to these new stacks, updates all pointers and offsets, and frees the original stacks. */ void ClauseAllocator::consolidate( Solver* solver , const bool force ) { const double myTime = cpuTime(); //If re-allocation is not really neccessary, don't do it //Neccesities: //1) There is too much memory allocated. Re-allocation will save space // Avoiding segfault (max is 16 outerOffsets, more than 10 is near) //2) There is too much empty, unused space (>30%) if (!force && ((double)currentlyUsedSize/(double)size > 0.7) ) { if (solver->conf.verbosity >= 3) { cout << "c Not consolidating memory." << endl; } return; } //Data for new struct vector<uint32_t> newOrigClauseSizes; vector<ClOffset> newOffsets; uint64_t newSize = 0; //Pointers that will be moved along BASE_DATA_TYPE* newDataStart = dataStart; BASE_DATA_TYPE* tmpDataStart = dataStart; assert(sizeof(Clause) % sizeof(BASE_DATA_TYPE) == 0); assert(sizeof(BASE_DATA_TYPE) % sizeof(Lit) == 0); for (const size_t sz: origClauseSizes) { Clause* clause = (Clause*)tmpDataStart; //Already freed, so skip entirely if (clause->freed()) { #ifdef VALGRIND_MAKE_MEM_DEFINED VALGRIND_MAKE_MEM_DEFINED(((char*)clause)+sizeof(Clause), clause->size()*sizeof(Lit)); #endif tmpDataStart += sz; continue; } //Move to new position size_t bytesNeeded = sizeof(Clause) + clause->size()*sizeof(Lit); size_t sizeNeeded = bytesNeeded/sizeof(BASE_DATA_TYPE) + (bool)(bytesNeeded % sizeof(BASE_DATA_TYPE)); assert(sizeNeeded <= sz && "New clause size must not be bigger than orig clause size"); memmove(newDataStart, tmpDataStart, sizeNeeded*sizeof(BASE_DATA_TYPE)); //Record position newOffsets.push_back(newSize); //Record sizes newOrigClauseSizes.push_back(sizeNeeded); newSize += sizeNeeded; //Move pointers along newDataStart += sizeNeeded; tmpDataStart += sz; } //Update offsets & pointers(?) now, when everything is in memory still updateAllOffsetsAndPointers(solver, newOffsets); const double time_used = cpuTime() - myTime; if (solver->conf.verbosity >= 2) { cout << "c [mem] Consolidated memory "; cout << " cls"; print_value_kilo_mega(newOrigClauseSizes.size()); cout << " old size"; print_value_kilo_mega(size); cout << " new size"; print_value_kilo_mega(newSize); cout << solver->conf.print_times(time_used) << endl; } if (solver->sqlStats) { solver->sqlStats->time_passed_min( solver , "consolidate" , time_used ); } //Update sizes size = newSize; currentlyUsedSize = newSize; newOrigClauseSizes.swap(origClauseSizes); }
void ClauseAllocator::consolidate(Solver* solver) { double myTime = cpuTime(); //if (dataStarts.size() > 2) { uint32_t sum = 0; for (uint32_t i = 0; i < sizes.size(); i++) { sum += currentlyUsedSize[i]; } uint32_t sumAlloc = 0; for (uint32_t i = 0; i < sizes.size(); i++) { sumAlloc += sizes[i]; } #ifdef DEBUG_CLAUSEALLOCATOR std::cout << "c ratio:" << (double)sum/(double)sumAlloc << std::endl; #endif //DEBUG_CLAUSEALLOCATOR if ((double)sum/(double)sumAlloc > 0.7 /*&& sum > 10000000*/) { if (solver->verbosity >= 2) { std::cout << "c Not consolidating memory." << std::endl; } return; } uint32_t newMaxSize = std::max(sum*2*sizeof(uint32_t), MIN_LIST_SIZE); uint32_t* newDataStarts = (uint32_t*)malloc(newMaxSize); newMaxSize /= sizeof(uint32_t); uint32_t newSize = 0; vec<uint32_t> newOrigClauseSizes; //} map<Clause*, Clause*> oldToNewPointer; map<uint32_t, uint32_t> oldToNewOffset; uint32_t* newDataStartsPointer = newDataStarts; for (uint32_t i = 0; i < dataStarts.size(); i++) { uint32_t currentLoc = 0; for (uint32_t i2 = 0; i2 < origClauseSizes[i].size(); i2++) { Clause* oldPointer = (Clause*)(dataStarts[i] + currentLoc); if (!oldPointer->freed()) { uint32_t sizeNeeded = sizeof(Clause) + oldPointer->size()*sizeof(Lit); memcpy(newDataStartsPointer, dataStarts[i] + currentLoc, sizeNeeded); oldToNewPointer[oldPointer] = (Clause*)newDataStartsPointer; oldToNewOffset[combineOuterInterOffsets(i, currentLoc)] = combineOuterInterOffsets(0, newSize); newSize += sizeNeeded/sizeof(uint32_t); newOrigClauseSizes.push(sizeNeeded/sizeof(uint32_t)); newDataStartsPointer += sizeNeeded/sizeof(uint32_t); } currentLoc += origClauseSizes[i][i2]; } } assert(newSize < newMaxSize); assert(newSize <= newMaxSize/2); updateOffsets(solver->watches, oldToNewOffset); updateOffsetsXor(solver->xorwatches, oldToNewOffset); updatePointers(solver->clauses, oldToNewPointer); updatePointers(solver->learnts, oldToNewPointer); updatePointers(solver->binaryClauses, oldToNewPointer); updatePointers(solver->xorclauses, oldToNewPointer); //No need to update varreplacer, since it only stores binary clauses that //must have been allocated such as to use the pool //updatePointers(solver->varReplacer->clauses, oldToNewPointer); updatePointers(solver->partHandler->clausesRemoved, oldToNewPointer); updatePointers(solver->partHandler->xorClausesRemoved, oldToNewPointer); for(map<Var, vector<Clause*> >::iterator it = solver->subsumer->elimedOutVar.begin(); it != solver->subsumer->elimedOutVar.end(); it++) { updatePointers(it->second, oldToNewPointer); } for(map<Var, vector<XorClause*> >::iterator it = solver->xorSubsumer->elimedOutVar.begin(); it != solver->xorSubsumer->elimedOutVar.end(); it++) { updatePointers(it->second, oldToNewPointer); } vec<PropagatedFrom>& reason = solver->reason; for (PropagatedFrom *it = reason.getData(), *end = reason.getDataEnd(); it != end; it++) { if (!it->isBinary() && !it->isNULL()) { /*if ((it == reason.getData() + (*it->getClause())[0].var()) && (solver->value((*it->getClause())[0]) == l_True)) { assert(oldToNewPointer.find(it->getClause()) != oldToNewPointer.end()); *it = PropagatedFrom(oldToNewPointer[it->getClause()]); } else { *it = PropagatedFrom(); }*/ if (oldToNewPointer.find(it->getClause()) != oldToNewPointer.end()) { *it = PropagatedFrom(oldToNewPointer[it->getClause()]); } } } for (uint32_t i = 0; i < dataStarts.size(); i++) free(dataStarts[i]); dataStarts.clear(); maxSizes.clear(); sizes.clear(); origClauseSizes.clear(); dataStarts.push(newDataStarts); maxSizes.push(newMaxSize); sizes.push(newSize); currentlyUsedSize.clear(); currentlyUsedSize.push(newSize); origClauseSizes.clear(); origClauseSizes.push(); newOrigClauseSizes.moveTo(origClauseSizes[0]); if (solver->verbosity >= 1) { std::cout << "c Consolidated memory. Time: " << cpuTime() - myTime << std::endl; } }
/** @brief If needed, compacts stacks, removing unused clauses Firstly, the algorithm determines if the number of useless slots is large or small compared to the problem size. If it is small, it does nothing. If it is large, then it allocates new stacks, copies the non-freed clauses to these new stacks, updates all pointers and offsets, and frees the original stacks. */ void ClauseAllocator::consolidate( Solver* solver , const bool force ) { //If re-allocation is not really neccessary, don't do it //Neccesities: //1) There is too much memory allocated. Re-allocation will save space // Avoiding segfault (max is 16 outerOffsets, more than 10 is near) //2) There is too much empty, unused space (>30%) if (!force && (float_div(currentlyUsedSize, size) > 0.8 || currentlyUsedSize < (100ULL*1000ULL)) ) { if (solver->conf.verbosity >= 3) { cout << "c Not consolidating memory." << endl; } return; } const double myTime = cpuTime(); //Pointers that will be moved along BASE_DATA_TYPE * const newDataStart = (BASE_DATA_TYPE*)malloc(currentlyUsedSize*sizeof(BASE_DATA_TYPE)); BASE_DATA_TYPE * new_ptr = newDataStart; assert(sizeof(BASE_DATA_TYPE) % sizeof(Lit) == 0); for(auto& ws: solver->watches) { for(Watched& w: ws) { if (w.isClause()) { Clause* old = ptr(w.get_offset()); assert(!old->freed()); Lit blocked = w.getBlockedLit(); if (old->reloced) { Lit newoffset = (*old)[0]; w = Watched(newoffset.toInt(), blocked); } else { uint32_t new_offset = move_cl(newDataStart, new_ptr, old); w = Watched(new_offset, blocked); } } } } #ifdef USE_GAUSS for (Gaussian* gauss : solver->gauss_matrixes) { for(GaussClauseToClear& gcl: gauss->clauses_toclear) { ClOffset& off = gcl.offs; Clause* old = ptr(off); if (old->reloced) { uint32_t new_offset = (*old)[0].toInt(); off = new_offset; } else { uint32_t new_offset = move_cl(newDataStart, new_ptr, old); off = new_offset; } assert(!old->freed()); } } #endif //USE_GAUSS update_offsets(solver->longIrredCls); for(auto& lredcls: solver->longRedCls) { update_offsets(lredcls); } //Fix up propBy for (size_t i = 0; i < solver->nVars(); i++) { VarData& vdata = solver->varData[i]; if (vdata.reason.isClause()) { if (vdata.removed == Removed::none && solver->decisionLevel() >= vdata.level && vdata.level != 0 && solver->value(i) != l_Undef ) { Clause* old = ptr(vdata.reason.get_offset()); assert(!old->freed()); uint32_t new_offset = (*old)[0].toInt(); vdata.reason = PropBy(new_offset); } else { vdata.reason = PropBy(); } } } //Update sizes const uint32_t old_size = size; size = new_ptr-newDataStart; capacity = currentlyUsedSize; currentlyUsedSize = size; free(dataStart); dataStart = newDataStart; const double time_used = cpuTime() - myTime; if (solver->conf.verbosity) { cout << "c [mem] Consolidated memory "; cout << " old size"; print_value_kilo_mega(old_size); cout << " new size"; print_value_kilo_mega(size); cout << solver->conf.print_times(time_used) << endl; } if (solver->sqlStats) { solver->sqlStats->time_passed_min( solver , "consolidate" , time_used ); } }
/** @brief If needed, compacts stacks, removing unused clauses Firstly, the algorithm determines if the number of useless slots is large or small compared to the problem size. If it is small, it does nothing. If it is large, then it allocates new stacks, copies the non-freed clauses to these new stacks, updates all pointers and offsets, and frees the original stacks. */ void ClauseAllocator::consolidate( Solver* solver , const bool force , bool lower_verb ) { //If re-allocation is not really neccessary, don't do it //Neccesities: //1) There is too much memory allocated. Re-allocation will save space // Avoiding segfault (max is 16 outerOffsets, more than 10 is near) //2) There is too much empty, unused space (>30%) if (!force && (float_div(currentlyUsedSize, size) > 0.8 || currentlyUsedSize < (100ULL*1000ULL)) ) { if (solver->conf.verbosity >= 3 || (lower_verb && solver->conf.verbosity) ) { cout << "c Not consolidating memory." << endl; } return; } const double myTime = cpuTime(); //Pointers that will be moved along BASE_DATA_TYPE * const newDataStart = (BASE_DATA_TYPE*)malloc(currentlyUsedSize*sizeof(BASE_DATA_TYPE)); BASE_DATA_TYPE * new_ptr = newDataStart; assert(sizeof(BASE_DATA_TYPE) % sizeof(Lit) == 0); vector<bool> visited(solver->watches.size(), 0); Heap<Solver::VarOrderLt> &order_heap = solver->VSIDS ? solver->order_heap_vsids : solver->order_heap_maple; if (solver->conf.static_mem_consolidate_order) { for(auto& ws: solver->watches) { move_one_watchlist(ws, newDataStart, new_ptr); } } else { for(uint32_t i = 0; i < order_heap.size(); i++) { for(uint32_t i2 = 0; i2 < 2; i2++) { Lit lit = Lit(order_heap[i], i2); assert(lit.toInt() < solver->watches.size()); move_one_watchlist(solver->watches[lit], newDataStart, new_ptr); visited[lit.toInt()] = 1; } } for(uint32_t i = 0; i < solver->watches.size(); i++) { Lit lit = Lit::toLit(i); watch_subarray ws = solver->watches[lit]; if (!visited[lit.toInt()]) { move_one_watchlist(ws, newDataStart, new_ptr); visited[lit.toInt()] = 1; } } } #ifdef USE_GAUSS for (EGaussian* gauss : solver->gmatrixes) { if (gauss == NULL) { continue; } for(auto& gcl: gauss->clauses_toclear) { Clause* old = ptr(gcl.first); if (old->reloced) { ClOffset new_offset = (*old)[0].toInt(); #ifdef LARGE_OFFSETS new_offset += ((uint64_t)(*old)[1].toInt())<<32; #endif gcl.first = new_offset; } else { ClOffset new_offset = move_cl(newDataStart, new_ptr, old); gcl.first = new_offset; } assert(!old->freed()); } } #endif //USE_GAUSS update_offsets(solver->longIrredCls); for(auto& lredcls: solver->longRedCls) { update_offsets(lredcls); } //Fix up propBy for (size_t i = 0; i < solver->nVars(); i++) { VarData& vdata = solver->varData[i]; if (vdata.reason.isClause()) { if (vdata.removed == Removed::none && solver->decisionLevel() >= vdata.level && vdata.level != 0 && solver->value(i) != l_Undef ) { Clause* old = ptr(vdata.reason.get_offset()); assert(!old->freed()); ClOffset new_offset = (*old)[0].toInt(); #ifdef LARGE_OFFSETS new_offset += ((uint64_t)(*old)[1].toInt())<<32; #endif vdata.reason = PropBy(new_offset); } else { vdata.reason = PropBy(); } } } //Update sizes const uint64_t old_size = size; size = new_ptr-newDataStart; capacity = currentlyUsedSize; currentlyUsedSize = size; free(dataStart); dataStart = newDataStart; const double time_used = cpuTime() - myTime; if (solver->conf.verbosity >= 2 || (lower_verb && solver->conf.verbosity) ) { size_t log_2_size = 0; if (size > 0) { //yes, it can be 0 (only binary clauses, for example) std::log2(size); } cout << "c [mem] consolidate "; cout << " old-sz: "; print_value_kilo_mega(old_size*sizeof(BASE_DATA_TYPE)); cout << " new-sz: "; print_value_kilo_mega(size*sizeof(BASE_DATA_TYPE)); cout << " new bits offs: " << std::fixed << std::setprecision(2) << log_2_size; cout << solver->conf.print_times(time_used) << endl; } if (solver->sqlStats) { solver->sqlStats->time_passed_min( solver , "consolidate" , time_used ); } }