mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 04:43:03 +01:00
Some more cost based calculation. Fine-tunning will follow
This commit is contained in:
parent
374a9a33c7
commit
c3f9c5bebc
@ -778,7 +778,8 @@ IndexScratchSegment::IndexScratchSegment(MemoryPool& p, IndexScratchSegment* seg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexScratch::IndexScratch(MemoryPool& p, index_desc* ix) :
|
IndexScratch::IndexScratch(MemoryPool& p, thread_db* tdbb, index_desc* ix,
|
||||||
|
CompilerScratch::csb_repeat* csb_tail) :
|
||||||
idx(ix), segments(p)
|
idx(ix), segments(p)
|
||||||
{
|
{
|
||||||
/**************************************
|
/**************************************
|
||||||
@ -800,10 +801,30 @@ IndexScratch::IndexScratch(MemoryPool& p, index_desc* ix) :
|
|||||||
|
|
||||||
segments.grow(idx->idx_count);
|
segments.grow(idx->idx_count);
|
||||||
|
|
||||||
|
int length = 0;
|
||||||
|
const Format* format = csb_tail->csb_format;
|
||||||
|
index_desc::idx_repeat* idx_desc = idx->idx_rpt;
|
||||||
IndexScratchSegment** segment = segments.begin();
|
IndexScratchSegment** segment = segments.begin();
|
||||||
for (int i = 0; i < segments.getCount(); i++) {
|
for (int i = 0; i < segments.getCount(); i++, idx_desc++) {
|
||||||
segment[i] = FB_NEW(p) IndexScratchSegment(p);
|
segment[i] = FB_NEW(p) IndexScratchSegment(p);
|
||||||
}
|
length += format->fmt_desc[idx_desc->idx_field].dsc_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AB: Calculate the cardinality which should reflect the total number
|
||||||
|
// of index pages for this index.
|
||||||
|
// We assume that the average index-key can be compressed by a factor 0.5
|
||||||
|
// In the future the average key-length should be stored and retrieved
|
||||||
|
// from a system table (RDB$INDICES for example).
|
||||||
|
// Multipling the selectivity with this cardinality gives the estimated
|
||||||
|
// number of index pages that are read for the index retrieval.
|
||||||
|
double factor = 0.5;
|
||||||
|
if (segments.getCount() >= 2) {
|
||||||
|
// Compound indexes are generally less compressed.
|
||||||
|
factor = 0.7;
|
||||||
|
}
|
||||||
|
Database* dbb = tdbb->tdbb_database;
|
||||||
|
cardinality = (csb_tail->csb_cardinality * (2 + (length * factor))) /
|
||||||
|
(dbb->dbb_page_size - BTR_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
IndexScratch::IndexScratch(MemoryPool& p, IndexScratch* scratch) :
|
IndexScratch::IndexScratch(MemoryPool& p, IndexScratch* scratch) :
|
||||||
@ -819,6 +840,7 @@ IndexScratch::IndexScratch(MemoryPool& p, IndexScratch* scratch) :
|
|||||||
*
|
*
|
||||||
**************************************/
|
**************************************/
|
||||||
selectivity = scratch->selectivity;
|
selectivity = scratch->selectivity;
|
||||||
|
cardinality = scratch->cardinality;
|
||||||
candidate = scratch->candidate;
|
candidate = scratch->candidate;
|
||||||
scopeCandidate = scratch->scopeCandidate;
|
scopeCandidate = scratch->scopeCandidate;
|
||||||
lowerCount = scratch->lowerCount;
|
lowerCount = scratch->lowerCount;
|
||||||
@ -866,6 +888,7 @@ InversionCandidate::InversionCandidate(MemoryPool& p) :
|
|||||||
*
|
*
|
||||||
**************************************/
|
**************************************/
|
||||||
selectivity = MAXIMUM_SELECTIVITY;
|
selectivity = MAXIMUM_SELECTIVITY;
|
||||||
|
cost = 0;
|
||||||
indexes = 0;
|
indexes = 0;
|
||||||
dependencies = 0;
|
dependencies = 0;
|
||||||
nonFullMatchedSegments = MAX_INDEX_SEGMENTS + 1;
|
nonFullMatchedSegments = MAX_INDEX_SEGMENTS + 1;
|
||||||
@ -913,7 +936,7 @@ OptimizerRetrieval::OptimizerRetrieval(MemoryPool& p, OptimizerBlk* opt,
|
|||||||
IndexScratch** indexScratch = indexScratches.begin();
|
IndexScratch** indexScratch = indexScratches.begin();
|
||||||
index_desc* idx = csb_tail->csb_idx->items;
|
index_desc* idx = csb_tail->csb_idx->items;
|
||||||
for (int i = 0; i < csb_tail->csb_indices; ++i, ++idx) {
|
for (int i = 0; i < csb_tail->csb_indices; ++i, ++idx) {
|
||||||
indexScratch[i] = FB_NEW(p) IndexScratch(p, idx);
|
indexScratch[i] = FB_NEW(p) IndexScratch(p, tdbb, idx, csb_tail);
|
||||||
}
|
}
|
||||||
|
|
||||||
inversionCandidates.shrink(0);
|
inversionCandidates.shrink(0);
|
||||||
@ -1198,7 +1221,12 @@ InversionCandidate* OptimizerRetrieval::generateInversion(RecordSource** rsb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
invCandidate = makeInversion(&inversions);
|
#ifdef OPT_DEBUG_RETRIEVAL
|
||||||
|
// Debug
|
||||||
|
printCandidates(&inversions);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
invCandidate = makeInversion(&inversions, true);
|
||||||
|
|
||||||
// Add the streams where this stream is depending on.
|
// Add the streams where this stream is depending on.
|
||||||
if (invCandidate) {
|
if (invCandidate) {
|
||||||
@ -1208,6 +1236,11 @@ InversionCandidate* OptimizerRetrieval::generateInversion(RecordSource** rsb)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef OPT_DEBUG_RETRIEVAL
|
||||||
|
// Debug
|
||||||
|
printFinalCandidate(invCandidate);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (invCandidate && setConjunctionsMatched) {
|
if (invCandidate && setConjunctionsMatched) {
|
||||||
Firebird::SortedArray<jrd_nod*> matches;
|
Firebird::SortedArray<jrd_nod*> matches;
|
||||||
// AB: Putting a unsorted array in a sorted array directly by join isn't
|
// AB: Putting a unsorted array in a sorted array directly by join isn't
|
||||||
@ -1409,10 +1442,11 @@ InversionCandidate* OptimizerRetrieval::getCost()
|
|||||||
return inversion;
|
return inversion;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// No index will be used
|
// No index will be used, thus
|
||||||
InversionCandidate* invCandidate = FB_NEW(pool) InversionCandidate(pool);
|
InversionCandidate* invCandidate = FB_NEW(pool) InversionCandidate(pool);
|
||||||
invCandidate->indexes = 0;
|
invCandidate->indexes = 0;
|
||||||
invCandidate->selectivity = MAXIMUM_SELECTIVITY;
|
invCandidate->selectivity = MAXIMUM_SELECTIVITY;
|
||||||
|
invCandidate->cost = csb->csb_rpt[stream].csb_cardinality;
|
||||||
return invCandidate;
|
return invCandidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1443,6 +1477,7 @@ InversionCandidate* OptimizerRetrieval::getInversion(RecordSource** rsb)
|
|||||||
InversionCandidate* invCandidate = FB_NEW(pool) InversionCandidate(pool);
|
InversionCandidate* invCandidate = FB_NEW(pool) InversionCandidate(pool);
|
||||||
invCandidate->indexes = 0;
|
invCandidate->indexes = 0;
|
||||||
invCandidate->selectivity = MAXIMUM_SELECTIVITY;
|
invCandidate->selectivity = MAXIMUM_SELECTIVITY;
|
||||||
|
invCandidate->cost = csb->csb_rpt[stream].csb_cardinality;
|
||||||
return invCandidate;
|
return invCandidate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1583,6 +1618,9 @@ bool OptimizerRetrieval::getInversionCandidates(InversionCandidateList* inversio
|
|||||||
FB_NEW(pool) InversionCandidate(pool);
|
FB_NEW(pool) InversionCandidate(pool);
|
||||||
invCandidate->unique = unique;
|
invCandidate->unique = unique;
|
||||||
invCandidate->selectivity = scratch[i]->selectivity;
|
invCandidate->selectivity = scratch[i]->selectivity;
|
||||||
|
// Calculate the cost (only index pages) for this index.
|
||||||
|
// The constant 3 is an avergae for the rootpage and non-leaf pages.
|
||||||
|
invCandidate->cost = 3 + (scratch[i]->selectivity * scratch[i]->cardinality);
|
||||||
invCandidate->nonFullMatchedSegments =
|
invCandidate->nonFullMatchedSegments =
|
||||||
scratch[i]->nonFullMatchedSegments;
|
scratch[i]->nonFullMatchedSegments;
|
||||||
invCandidate->matchedSegments =
|
invCandidate->matchedSegments =
|
||||||
@ -1791,8 +1829,8 @@ jrd_nod* OptimizerRetrieval::makeIndexScanNode(IndexScratch* indexScratch) const
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* inversions)
|
InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* inversions,
|
||||||
const
|
bool top) const
|
||||||
{
|
{
|
||||||
/**************************************
|
/**************************************
|
||||||
*
|
*
|
||||||
@ -1800,7 +1838,11 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in
|
|||||||
*
|
*
|
||||||
**************************************
|
**************************************
|
||||||
*
|
*
|
||||||
* Functional description
|
* Select best available inversion candidates
|
||||||
|
* and compose them to 1 inversion.
|
||||||
|
* If top is true the datapages-cost is
|
||||||
|
* also used in the calculation (only needed
|
||||||
|
* for top InversionNode generation).
|
||||||
*
|
*
|
||||||
**************************************/
|
**************************************/
|
||||||
|
|
||||||
@ -1810,9 +1852,15 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in
|
|||||||
|
|
||||||
const jrd_nod* const plan = csb->csb_rpt[stream].csb_plan;
|
const jrd_nod* const plan = csb->csb_rpt[stream].csb_plan;
|
||||||
|
|
||||||
InversionCandidate* invCandidate = NULL;
|
|
||||||
int i = 0;
|
|
||||||
double totalSelectivity = MAXIMUM_SELECTIVITY; // worst selectivity
|
double totalSelectivity = MAXIMUM_SELECTIVITY; // worst selectivity
|
||||||
|
double totalCost = 0;
|
||||||
|
double totalIndexCost = 0;
|
||||||
|
const double maximumCost = csb->csb_rpt[stream].csb_cardinality * 0.95;
|
||||||
|
const double minimumSelectivity = 1 / csb->csb_rpt[stream].csb_cardinality;
|
||||||
|
double previousTotalCost = maximumCost;
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
InversionCandidate* invCandidate = NULL;
|
||||||
InversionCandidate** inversion = inversions->begin();
|
InversionCandidate** inversion = inversions->begin();
|
||||||
for (i = 0; i < inversions->getCount(); i++) {
|
for (i = 0; i < inversions->getCount(); i++) {
|
||||||
inversion[i]->used = false;
|
inversion[i]->used = false;
|
||||||
@ -1852,6 +1900,7 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in
|
|||||||
}
|
}
|
||||||
invCandidate->unique = inversion[currentPosition]->unique;
|
invCandidate->unique = inversion[currentPosition]->unique;
|
||||||
invCandidate->selectivity = inversion[currentPosition]->selectivity;
|
invCandidate->selectivity = inversion[currentPosition]->selectivity;
|
||||||
|
invCandidate->cost = inversion[currentPosition]->cost;
|
||||||
invCandidate->indexes = inversion[currentPosition]->indexes;
|
invCandidate->indexes = inversion[currentPosition]->indexes;
|
||||||
invCandidate->nonFullMatchedSegments = 0;
|
invCandidate->nonFullMatchedSegments = 0;
|
||||||
invCandidate->matchedSegments = inversion[currentPosition]->matchedSegments;
|
invCandidate->matchedSegments = inversion[currentPosition]->matchedSegments;
|
||||||
@ -1964,29 +2013,62 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in
|
|||||||
|
|
||||||
// If we have a candidate which is interesting build the inversion
|
// If we have a candidate which is interesting build the inversion
|
||||||
// else we're done.
|
// else we're done.
|
||||||
if (bestCandidate) {
|
if (bestCandidate)
|
||||||
if (plan || bestCandidate->selectivity < (totalSelectivity * SELECTIVITY_THRESHOLD_FACTOR_ADD)) {
|
{
|
||||||
// Estimate new selectivity
|
// AB: Here we test if our new candidate is interesting enough to be added for
|
||||||
// Assign selectivity for the formula
|
// index retrieval.
|
||||||
double bestSel = bestCandidate->selectivity;
|
|
||||||
double worstSel = totalSelectivity;
|
|
||||||
if (bestCandidate->selectivity > totalSelectivity) {
|
|
||||||
worstSel = bestCandidate->selectivity;
|
|
||||||
bestSel = totalSelectivity;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestSel >= 1) {
|
// AB: For now i'll use the calculation that's often used for and-ing selectivities (S1 * S2).
|
||||||
totalSelectivity = 1;
|
// I think this calculation is not right for many cases.
|
||||||
}
|
// For example two "good" selectivities will result in a very good selectivity, but
|
||||||
else if (bestSel == 0) {
|
// mostly a filter is made by adding criteria's where every criteria is an extra filter
|
||||||
totalSelectivity = 0;
|
// compared to the previous one. Thus with the second criteria in _most_ cases still
|
||||||
}
|
// records are returned. (Think also on the segment-selectivity in compound indexes)
|
||||||
else {
|
// Assume a table with 100000 records and two selectivities of 0.001 (100 records) which
|
||||||
totalSelectivity = (bestSel + (((worstSel - bestSel) / (1 - bestSel)) * bestSel)) / 2;
|
// are both AND-ed (with S1 * S2 => 0.001 * 0.001 = 0.000001 => 0.1 record).
|
||||||
}
|
//
|
||||||
|
// A better formula could be where the result is between "Sbest" and "Sbest * factor"
|
||||||
|
// The reducing factor should be between 0 and 1 (Sbest = best selectivity)
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
/*
|
||||||
|
double newTotalSelectivity = 0;
|
||||||
|
double bestSel = bestCandidate->selectivity;
|
||||||
|
double worstSel = totalSelectivity;
|
||||||
|
if (bestCandidate->selectivity > totalSelectivity) {
|
||||||
|
worstSel = bestCandidate->selectivity;
|
||||||
|
bestSel = totalSelectivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestSel >= 1) {
|
||||||
|
newTotalSelectivity = 1;
|
||||||
|
}
|
||||||
|
else if (bestSel == 0) {
|
||||||
|
newTotalSelectivity = 0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
newTotalSelectivity = bestSel - ((1 - worstSel) * (bestSel - (bestSel * 0.01)));
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
double newTotalSelectivity = bestCandidate->selectivity * totalSelectivity;
|
||||||
|
totalIndexCost += bestCandidate->cost;
|
||||||
|
totalCost = totalIndexCost;
|
||||||
|
if (top) {
|
||||||
|
totalCost += (newTotalSelectivity * csb->csb_rpt[stream].csb_cardinality);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test if the new totalCost will be higher as the maximumCost or previous totalCost
|
||||||
|
// and if the current selectivty (without the bestCandidate) is already good enough.
|
||||||
|
//if (plan || bestCandidate->selectivity < (totalSelectivity * SELECTIVITY_THRESHOLD_FACTOR_ADD)) {
|
||||||
|
if (plan || ((totalCost < maximumCost) && (totalCost < previousTotalCost) &&
|
||||||
|
(totalSelectivity > minimumSelectivity)))
|
||||||
|
{
|
||||||
// Exclude index from next pass
|
// Exclude index from next pass
|
||||||
bestCandidate->used = true;
|
bestCandidate->used = true;
|
||||||
|
previousTotalCost = totalCost;
|
||||||
|
|
||||||
|
totalSelectivity = newTotalSelectivity;
|
||||||
|
|
||||||
if (!invCandidate) {
|
if (!invCandidate) {
|
||||||
invCandidate = FB_NEW(pool) InversionCandidate(pool);
|
invCandidate = FB_NEW(pool) InversionCandidate(pool);
|
||||||
@ -1998,6 +2080,7 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in
|
|||||||
}
|
}
|
||||||
invCandidate->unique = bestCandidate->unique;
|
invCandidate->unique = bestCandidate->unique;
|
||||||
invCandidate->selectivity = bestCandidate->selectivity;
|
invCandidate->selectivity = bestCandidate->selectivity;
|
||||||
|
invCandidate->cost = totalCost;
|
||||||
invCandidate->indexes = bestCandidate->indexes;
|
invCandidate->indexes = bestCandidate->indexes;
|
||||||
invCandidate->nonFullMatchedSegments = 0;
|
invCandidate->nonFullMatchedSegments = 0;
|
||||||
invCandidate->matchedSegments = bestCandidate->matchedSegments;
|
invCandidate->matchedSegments = bestCandidate->matchedSegments;
|
||||||
@ -2026,6 +2109,7 @@ InversionCandidate* OptimizerRetrieval::makeInversion(InversionCandidateList* in
|
|||||||
}
|
}
|
||||||
invCandidate->unique = (invCandidate->unique || bestCandidate->unique);
|
invCandidate->unique = (invCandidate->unique || bestCandidate->unique);
|
||||||
invCandidate->selectivity = totalSelectivity;
|
invCandidate->selectivity = totalSelectivity;
|
||||||
|
invCandidate->cost = totalCost;
|
||||||
invCandidate->indexes += bestCandidate->indexes;
|
invCandidate->indexes += bestCandidate->indexes;
|
||||||
invCandidate->nonFullMatchedSegments = 0;
|
invCandidate->nonFullMatchedSegments = 0;
|
||||||
invCandidate->matchedSegments =
|
invCandidate->matchedSegments =
|
||||||
@ -2364,6 +2448,7 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes(
|
|||||||
composeInversion(invCandidate1->inversion, invCandidate2->inversion, nod_bit_or);
|
composeInversion(invCandidate1->inversion, invCandidate2->inversion, nod_bit_or);
|
||||||
invCandidate->unique = (invCandidate1->unique && invCandidate2->unique);
|
invCandidate->unique = (invCandidate1->unique && invCandidate2->unique);
|
||||||
invCandidate->selectivity = invCandidate1->selectivity + invCandidate2->selectivity;
|
invCandidate->selectivity = invCandidate1->selectivity + invCandidate2->selectivity;
|
||||||
|
invCandidate->cost = invCandidate1->cost + invCandidate2->cost;
|
||||||
invCandidate->indexes = invCandidate1->indexes + invCandidate2->indexes;
|
invCandidate->indexes = invCandidate1->indexes + invCandidate2->indexes;
|
||||||
invCandidate->nonFullMatchedSegments = 0;
|
invCandidate->nonFullMatchedSegments = 0;
|
||||||
invCandidate->matchedSegments =
|
invCandidate->matchedSegments =
|
||||||
@ -2423,6 +2508,86 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef OPT_DEBUG_RETRIEVAL
|
||||||
|
void OptimizerRetrieval::printCandidate(InversionCandidate* candidate) const
|
||||||
|
{
|
||||||
|
/**************************************
|
||||||
|
*
|
||||||
|
* p r i n t C a n d i d a t e
|
||||||
|
*
|
||||||
|
**************************************
|
||||||
|
*
|
||||||
|
* Functional description
|
||||||
|
*
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
FILE *opt_debug_file = fopen(OPTIMIZER_DEBUG_FILE, "a");
|
||||||
|
fprintf(opt_debug_file, " cost(%1.2f), selectivity(%1.10f), indexes(%d), matched(%d, %d)",
|
||||||
|
candidate->cost, candidate->selectivity, candidate->indexes, candidate->matchedSegments,
|
||||||
|
candidate->nonFullMatchedSegments);
|
||||||
|
if (candidate->unique) {
|
||||||
|
fprintf(opt_debug_file, ", unique");
|
||||||
|
}
|
||||||
|
int depFromCount = candidate->dependentFromStreams.getCount();
|
||||||
|
if (depFromCount >= 1) {
|
||||||
|
fprintf(opt_debug_file, ", dependent from ");
|
||||||
|
for (int i = 0; i < depFromCount; i++) {
|
||||||
|
if (i == 0) {
|
||||||
|
fprintf(opt_debug_file, "%d", candidate->dependentFromStreams[i]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(opt_debug_file, ", %d", candidate->dependentFromStreams[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(opt_debug_file, "\n");
|
||||||
|
fclose(opt_debug_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptimizerRetrieval::printCandidates(InversionCandidateList* inversions) const
|
||||||
|
{
|
||||||
|
/**************************************
|
||||||
|
*
|
||||||
|
* p r i n t C a n d i d a t e s
|
||||||
|
*
|
||||||
|
**************************************
|
||||||
|
*
|
||||||
|
* Functional description
|
||||||
|
*
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
FILE *opt_debug_file = fopen(OPTIMIZER_DEBUG_FILE, "a");
|
||||||
|
fprintf(opt_debug_file, " retrieval candidates:\n");
|
||||||
|
fclose(opt_debug_file);
|
||||||
|
InversionCandidate** inversion = inversions->begin();
|
||||||
|
for (int i = 0; i < inversions->getCount(); i++) {
|
||||||
|
InversionCandidate* candidate = inversion[i];
|
||||||
|
printCandidate(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptimizerRetrieval::printFinalCandidate(InversionCandidate* candidate) const
|
||||||
|
{
|
||||||
|
/**************************************
|
||||||
|
*
|
||||||
|
* p r i n t C a n d i d a t e
|
||||||
|
*
|
||||||
|
**************************************
|
||||||
|
*
|
||||||
|
* Functional description
|
||||||
|
*
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
if (candidate) {
|
||||||
|
FILE *opt_debug_file = fopen(OPTIMIZER_DEBUG_FILE, "a");
|
||||||
|
fprintf(opt_debug_file, " final candidate: ");
|
||||||
|
fclose(opt_debug_file);
|
||||||
|
printCandidate(candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
bool OptimizerRetrieval::validateStarts(IndexScratch* indexScratch,
|
bool OptimizerRetrieval::validateStarts(IndexScratch* indexScratch,
|
||||||
jrd_nod* boolean, USHORT segment) const
|
jrd_nod* boolean, USHORT segment) const
|
||||||
{
|
{
|
||||||
@ -2604,7 +2769,8 @@ OptimizerInnerJoin::OptimizerInnerJoin(MemoryPool& p, OptimizerBlk* opt, UCHAR*
|
|||||||
innerStream[i]->stream = streams[i + 1];
|
innerStream[i]->stream = streams[i + 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateCardinalities();
|
// Cardinalities are calculated in OPT_compile()
|
||||||
|
//calculateCardinalities();
|
||||||
calculateStreamInfo();
|
calculateStreamInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2676,7 +2842,7 @@ void OptimizerInnerJoin::calculateStreamInfo()
|
|||||||
OptimizerRetrieval* optimizerRetrieval = FB_NEW(pool)
|
OptimizerRetrieval* optimizerRetrieval = FB_NEW(pool)
|
||||||
OptimizerRetrieval(pool, optimizer, innerStreams[i]->stream, false, false, NULL);
|
OptimizerRetrieval(pool, optimizer, innerStreams[i]->stream, false, false, NULL);
|
||||||
InversionCandidate* candidate = optimizerRetrieval->getCost();
|
InversionCandidate* candidate = optimizerRetrieval->getCost();
|
||||||
innerStreams[i]->baseCost = candidate->selectivity * csb_tail->csb_cardinality;
|
innerStreams[i]->baseCost = candidate->cost;//candidate->selectivity * csb_tail->csb_cardinality;
|
||||||
innerStreams[i]->baseIndexes = candidate->indexes;
|
innerStreams[i]->baseIndexes = candidate->indexes;
|
||||||
innerStreams[i]->baseUnique = candidate->unique;
|
innerStreams[i]->baseUnique = candidate->unique;
|
||||||
innerStreams[i]->baseConjunctionMatches = candidate->matches.getCount();
|
innerStreams[i]->baseConjunctionMatches = candidate->matches.getCount();
|
||||||
@ -2795,12 +2961,13 @@ bool OptimizerInnerJoin::estimateCost(USHORT stream, double *cost,
|
|||||||
// Based on the page-size we make an estimated number of keys per index leaf page.
|
// Based on the page-size we make an estimated number of keys per index leaf page.
|
||||||
// This is really a wild estimated number because it depends on key size and how good
|
// This is really a wild estimated number because it depends on key size and how good
|
||||||
// the prefix compression does its work.
|
// the prefix compression does its work.
|
||||||
const double nodesPerPage = ((double)database->dbb_page_size / 10);
|
//const double nodesPerPage = ((double)database->dbb_page_size / 10);
|
||||||
// The estimated index cost reflects the number of pages fetched for this index read.
|
// The estimated index cost reflects the number of pages fetched for this index read.
|
||||||
// The number of pages is an index pointer page + the B-Tree level - 1 (leaf page) +
|
// The number of pages is an index pointer page + the B-Tree level - 1 (leaf page) +
|
||||||
// index leaf pages to be read.
|
// index leaf pages to be read.
|
||||||
const double indexCost = 2 + ((cardinality * selectivity) / nodesPerPage);
|
//const double indexCost = 2 + ((cardinality * selectivity) / nodesPerPage);
|
||||||
*cost = (cardinality * selectivity) + (candidate->indexes * indexCost);
|
//*cost = (cardinality * selectivity) + (candidate->indexes * indexCost);
|
||||||
|
*cost = candidate->cost;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// No indexes are used, this meant for every record a data-page is read.
|
// No indexes are used, this meant for every record a data-page is read.
|
||||||
@ -2843,6 +3010,11 @@ int OptimizerInnerJoin::findJoinOrder()
|
|||||||
|
|
||||||
optimizer->opt_best_count = 0;
|
optimizer->opt_best_count = 0;
|
||||||
|
|
||||||
|
#ifdef OPT_DEBUG
|
||||||
|
// Debug
|
||||||
|
printStartOrder();
|
||||||
|
#endif
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
remainingStreams = 0;
|
remainingStreams = 0;
|
||||||
for (i = 0; i < innerStreams.getCount(); i++) {
|
for (i = 0; i < innerStreams.getCount(); i++) {
|
||||||
@ -2881,6 +3053,11 @@ int OptimizerInnerJoin::findJoinOrder()
|
|||||||
streamInfo->used = true;
|
streamInfo->used = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef OPT_DEBUG
|
||||||
|
// Debug
|
||||||
|
printBestOrder();
|
||||||
|
#endif
|
||||||
|
|
||||||
return optimizer->opt_best_count;
|
return optimizer->opt_best_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2943,7 +3120,7 @@ void OptimizerInnerJoin::findBestOrder(int position, InnerJoinStreamInfo* stream
|
|||||||
|
|
||||||
#ifdef OPT_DEBUG
|
#ifdef OPT_DEBUG
|
||||||
// Debug information
|
// Debug information
|
||||||
printFoundOrder(position, new_cost, new_cardinality);
|
printFoundOrder(position, position_cost, position_cardinality, new_cost, new_cardinality);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// mark this stream as "used" in the sense that it is already included
|
// mark this stream as "used" in the sense that it is already included
|
||||||
@ -3058,7 +3235,7 @@ void OptimizerInnerJoin::getIndexedRelationship(InnerJoinStreamInfo* baseStream,
|
|||||||
OptimizerRetrieval* optimizerRetrieval = FB_NEW(pool)
|
OptimizerRetrieval* optimizerRetrieval = FB_NEW(pool)
|
||||||
OptimizerRetrieval(pool, optimizer, testStream->stream, false, false, NULL);
|
OptimizerRetrieval(pool, optimizer, testStream->stream, false, false, NULL);
|
||||||
InversionCandidate* candidate = optimizerRetrieval->getCost();
|
InversionCandidate* candidate = optimizerRetrieval->getCost();
|
||||||
double cost = candidate->selectivity * csb_tail->csb_cardinality;
|
double cost = candidate->cost;// candidate->selectivity * csb_tail->csb_cardinality;
|
||||||
if (candidate->unique) {
|
if (candidate->unique) {
|
||||||
// If we've an unique index retrieval the cost is equal to 1
|
// If we've an unique index retrieval the cost is equal to 1
|
||||||
// The cost calculation can be far away from the real cost value if there
|
// The cost calculation can be far away from the real cost value if there
|
||||||
@ -3120,7 +3297,34 @@ InnerJoinStreamInfo* OptimizerInnerJoin::getStreamInfo(int stream)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef OPT_DEBUG
|
#ifdef OPT_DEBUG
|
||||||
void OptimizerInnerJoin::printFoundOrder(int position, double cost, double cardinality) const
|
void OptimizerInnerJoin::printBestOrder() const
|
||||||
|
{
|
||||||
|
/**************************************
|
||||||
|
*
|
||||||
|
* p r i n t B e s t O r d e r
|
||||||
|
*
|
||||||
|
**************************************
|
||||||
|
*
|
||||||
|
* Dump finally selected stream order.
|
||||||
|
*
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
FILE *opt_debug_file = fopen(OPTIMIZER_DEBUG_FILE, "a");
|
||||||
|
fprintf(opt_debug_file, " best order, streams: ");
|
||||||
|
for (int i = 0; i < optimizer->opt_best_count; i++) {
|
||||||
|
if (i == 0) {
|
||||||
|
fprintf(opt_debug_file, "%d", optimizer->opt_streams[i].opt_best_stream);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(opt_debug_file, ", %d", optimizer->opt_streams[i].opt_best_stream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(opt_debug_file, "\n");
|
||||||
|
fclose(opt_debug_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptimizerInnerJoin::printFoundOrder(int position, double positionCost,
|
||||||
|
double positionCardinality, double cost, double cardinality) const
|
||||||
{
|
{
|
||||||
/**************************************
|
/**************************************
|
||||||
*
|
*
|
||||||
@ -3134,13 +3338,21 @@ void OptimizerInnerJoin::printFoundOrder(int position, double cost, double cardi
|
|||||||
**************************************/
|
**************************************/
|
||||||
|
|
||||||
FILE *opt_debug_file = fopen(OPTIMIZER_DEBUG_FILE, "a");
|
FILE *opt_debug_file = fopen(OPTIMIZER_DEBUG_FILE, "a");
|
||||||
fprintf(opt_debug_file, "order pos. %2.2d, streams: ", position);
|
fprintf(opt_debug_file, " position %2.2d:", position);
|
||||||
|
fprintf(opt_debug_file, " pos. cardinality(%10.2f) pos. cost(%10.2f)", positionCardinality, positionCost);
|
||||||
|
fprintf(opt_debug_file, " cardinality(%10.2f) cost(%10.2f)", cardinality, cost);
|
||||||
|
fprintf(opt_debug_file, ", streams: ", position);
|
||||||
const OptimizerBlk::opt_stream* tail = optimizer->opt_streams.begin();
|
const OptimizerBlk::opt_stream* tail = optimizer->opt_streams.begin();
|
||||||
const OptimizerBlk::opt_stream* const order_end = tail + position;
|
const OptimizerBlk::opt_stream* const order_end = tail + position;
|
||||||
for (; tail < order_end; tail++) {
|
for (; tail < order_end; tail++) {
|
||||||
fprintf(opt_debug_file, "%2.2d - ", tail->opt_stream_number);
|
if (tail == optimizer->opt_streams.begin()) {
|
||||||
|
fprintf(opt_debug_file, "%d", tail->opt_stream_number);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fprintf(opt_debug_file, ", %d", tail->opt_stream_number);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fprintf(opt_debug_file, "\tcardinality: %10.2f\tcost: %10.2f\n", cardinality, cost);
|
fprintf(opt_debug_file, "\n");
|
||||||
fclose(opt_debug_file);
|
fclose(opt_debug_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3158,11 +3370,36 @@ void OptimizerInnerJoin::printProcessList(const IndexedRelationships* processLis
|
|||||||
**************************************/
|
**************************************/
|
||||||
|
|
||||||
FILE *opt_debug_file = fopen(OPTIMIZER_DEBUG_FILE, "a");
|
FILE *opt_debug_file = fopen(OPTIMIZER_DEBUG_FILE, "a");
|
||||||
fprintf(opt_debug_file, "processlist, basestream %2.2d, relationships: \n", stream);
|
fprintf(opt_debug_file, " basestream %d, relationships: stream(cost)", stream);
|
||||||
const IndexRelationship* const* relationships = processList->begin();
|
const IndexRelationship* const* relationships = processList->begin();
|
||||||
for (int i = 0; i < processList->getCount(); i++) {
|
for (int i = 0; i < processList->getCount(); i++) {
|
||||||
fprintf(opt_debug_file, "\t\t%2.2d (%10.2f)\n", relationships[i]->stream, relationships[i]->cost);
|
fprintf(opt_debug_file, ", %d (%1.2f)", relationships[i]->stream, relationships[i]->cost);
|
||||||
}
|
}
|
||||||
|
fprintf(opt_debug_file, "\n");
|
||||||
|
fclose(opt_debug_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OptimizerInnerJoin::printStartOrder() const
|
||||||
|
{
|
||||||
|
/**************************************
|
||||||
|
*
|
||||||
|
* p r i n t B e s t O r d e r
|
||||||
|
*
|
||||||
|
**************************************
|
||||||
|
*
|
||||||
|
* Dump finally selected stream order.
|
||||||
|
*
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
FILE *opt_debug_file = fopen(OPTIMIZER_DEBUG_FILE, "a");
|
||||||
|
fprintf(opt_debug_file, "Start join order: with stream(baseCost)");
|
||||||
|
bool firstStream = true;
|
||||||
|
for (int i = 0; i < innerStreams.getCount(); i++) {
|
||||||
|
if (!innerStreams[i]->used) {
|
||||||
|
fprintf(opt_debug_file, ", %d (%1.2f)", innerStreams[i]->stream, innerStreams[i]->baseCost);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fprintf(opt_debug_file, "\n");
|
||||||
fclose(opt_debug_file);
|
fclose(opt_debug_file);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -30,10 +30,11 @@
|
|||||||
#define OPTIMIZER_H
|
#define OPTIMIZER_H
|
||||||
|
|
||||||
//#define OPT_DEBUG
|
//#define OPT_DEBUG
|
||||||
|
//#define OPT_DEBUG_RETRIEVAL
|
||||||
|
|
||||||
#ifdef OPT_DEBUG
|
//#ifdef OPT_DEBUG
|
||||||
#define OPTIMIZER_DEBUG_FILE "opt_debug.out"
|
#define OPTIMIZER_DEBUG_FILE "opt_debug.out"
|
||||||
#endif
|
//#endif
|
||||||
|
|
||||||
|
|
||||||
#include "../common/classes/alloc.h"
|
#include "../common/classes/alloc.h"
|
||||||
@ -114,7 +115,7 @@ public:
|
|||||||
class IndexScratch
|
class IndexScratch
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IndexScratch(MemoryPool& p, index_desc* idx);
|
IndexScratch(MemoryPool& p, thread_db* tdbb, index_desc* idx, CompilerScratch::csb_repeat* csb_tail);
|
||||||
IndexScratch(MemoryPool& p, IndexScratch* scratch);
|
IndexScratch(MemoryPool& p, IndexScratch* scratch);
|
||||||
~IndexScratch();
|
~IndexScratch();
|
||||||
|
|
||||||
@ -125,6 +126,7 @@ public:
|
|||||||
int lowerCount; //
|
int lowerCount; //
|
||||||
int upperCount; //
|
int upperCount; //
|
||||||
int nonFullMatchedSegments; //
|
int nonFullMatchedSegments; //
|
||||||
|
double cardinality; // Estimated cardinality when using the whole index
|
||||||
|
|
||||||
Firebird::Array<IndexScratchSegment*> segments;
|
Firebird::Array<IndexScratchSegment*> segments;
|
||||||
};
|
};
|
||||||
@ -137,6 +139,7 @@ public:
|
|||||||
InversionCandidate(MemoryPool& p);
|
InversionCandidate(MemoryPool& p);
|
||||||
|
|
||||||
double selectivity;
|
double selectivity;
|
||||||
|
double cost;
|
||||||
USHORT nonFullMatchedSegments;
|
USHORT nonFullMatchedSegments;
|
||||||
USHORT matchedSegments;
|
USHORT matchedSegments;
|
||||||
int indexes;
|
int indexes;
|
||||||
@ -174,10 +177,17 @@ protected:
|
|||||||
IndexScratchList* indexScratches, USHORT scope) const;
|
IndexScratchList* indexScratches, USHORT scope) const;
|
||||||
jrd_nod* makeIndexNode(const index_desc* idx) const;
|
jrd_nod* makeIndexNode(const index_desc* idx) const;
|
||||||
jrd_nod* makeIndexScanNode(IndexScratch* indexScratch) const;
|
jrd_nod* makeIndexScanNode(IndexScratch* indexScratch) const;
|
||||||
InversionCandidate* makeInversion(InversionCandidateList* inversions) const;
|
InversionCandidate* makeInversion(InversionCandidateList* inversions, bool top = false) const;
|
||||||
bool matchBoolean(IndexScratch* indexScratch, jrd_nod* boolean, USHORT scope) const;
|
bool matchBoolean(IndexScratch* indexScratch, jrd_nod* boolean, USHORT scope) const;
|
||||||
InversionCandidate* matchOnIndexes(IndexScratchList* indexScratches,
|
InversionCandidate* matchOnIndexes(IndexScratchList* indexScratches,
|
||||||
jrd_nod* boolean, USHORT scope) const;
|
jrd_nod* boolean, USHORT scope) const;
|
||||||
|
|
||||||
|
#ifdef OPT_DEBUG_RETRIEVAL
|
||||||
|
void printCandidate(InversionCandidate* candidate) const;
|
||||||
|
void printCandidates(InversionCandidateList* inversions) const;
|
||||||
|
void printFinalCandidate(InversionCandidate* candidate) const;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool validateStarts(IndexScratch* indexScratch, jrd_nod* boolean, USHORT segment) const;
|
bool validateStarts(IndexScratch* indexScratch, jrd_nod* boolean, USHORT segment) const;
|
||||||
private:
|
private:
|
||||||
MemoryPool& pool;
|
MemoryPool& pool;
|
||||||
@ -252,8 +262,11 @@ protected:
|
|||||||
InnerJoinStreamInfo* testStream);
|
InnerJoinStreamInfo* testStream);
|
||||||
InnerJoinStreamInfo* getStreamInfo(int stream);
|
InnerJoinStreamInfo* getStreamInfo(int stream);
|
||||||
#ifdef OPT_DEBUG
|
#ifdef OPT_DEBUG
|
||||||
void printFoundOrder(int position, double cost, double cardinality) const;
|
void printBestOrder() const;
|
||||||
|
void printFoundOrder(int position, double positionCost,
|
||||||
|
double positionCardinality, double cost, double cardinality) const;
|
||||||
void printProcessList(const IndexedRelationships* processList, int stream) const;
|
void printProcessList(const IndexedRelationships* processList, int stream) const;
|
||||||
|
void printStartOrder() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -583,6 +583,10 @@ RecordSource* OPT_compile(thread_db* tdbb,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
csb->csb_rpt[stream].csb_indices = 0;
|
csb->csb_rpt[stream].csb_indices = 0;
|
||||||
|
|
||||||
|
const Format* format = CMP_format(tdbb, csb, stream);
|
||||||
|
csb->csb_rpt[stream].csb_cardinality =
|
||||||
|
getRelationCardinality(tdbb, relation, format);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user