blob: 35c51eb14a521e702a0c782d208541b6b129c480 [file] [log] [blame]
/**********************************************************************
// @@@ START COPYRIGHT @@@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//
// @@@ END COPYRIGHT @@@
**********************************************************************/
/* -*-C++-*-
******************************************************************************
*
* File: Cost.C
* Description: Physical Properties and Cost
* Created: 11/12/96
* Language: C++
*
* Purpose: Simple Cost Vector Reduction
*
*
*
******************************************************************************
*/
// -----------------------------------------------------------------------
#include "Sqlcomp.h"
#include "GroupAttr.h"
#include "Cost.h"
#include "DefaultConstants.h"
#include "SQLCLIdev.h"
#include "CmpContext.h"
#include "CostScalar.h"
#include "CmpContext.h"
#include "SQLCLIdev.h"
#include "opt.h"
#include "CompException.h"
#include <math.h>
//<pb>
extern Cost* rollUpUnaryNonBlocking(const Cost& ,
const Cost& ,
const ReqdPhysicalProperty* const);
// -----------------------------------------------------------------------
// global display methods (to be invoked from Objectcenter)
// -----------------------------------------------------------------------
// excluded for coverage because it's a debug code
void displayCost (const Cost & cost)
{
cost.print();
}
//<pb>
// -----------------------------------------------------------------------
// friend functions involving objects of class SimpleCostVector
// -----------------------------------------------------------------------
//<pb>
//==============================================================================
// Operator for traditional vector addition. Each component of the returned
// vector is the sum of the corresponding components of two specified vectors.
//
// Note: this operation makes the number of probes in the result
// vector equal to that of the first specified vector.
//
// Input:
// v1 -- First specified vector.
//
// v2 -- Second specified vector.
//
// Output:
// none
//
// Return:
// Sum of v1 and v2 using traditional vector addition.
//
//==============================================================================
SimpleCostVector
operator+(const SimpleCostVector &v1, const SimpleCostVector &v2)
{
//---------------------------------
// Initialize result to vector v1.
//---------------------------------
SimpleCostVector resultVector = v1;
//---------------------------------------------------------------
// Add each component of v2 to result's corresponding component.
//---------------------------------------------------------------
for (Lng32 vecIdx = 0; vecIdx < COUNT_OF_SIMPLE_COST_COUNTERS; vecIdx++)
{
resultVector.counter_[vecIdx] += v2.counter_[vecIdx];
}
//------------------------------------------------
// Set result's number of probes to that of v1's.
//------------------------------------------------
resultVector.setNumProbes(v1.getNumProbes());
return resultVector;
} // operator+
//<pb>
//==============================================================================
// Operator for traditional vector subtraction. Each component of the returned
// vector is the difference between the corresponding components of two
// specified vectors.
//
// Note: this operation makes the number of probes in the result
// vector equal to that of the first specified vector.
//
// Input:
// v1 -- First specified vector.
//
// v2 -- Second specified vector.
//
// Output:
// none
//
// Return:
// v1 minus v2 using traditional vector addition.
//
//==============================================================================
SimpleCostVector
operator-(const SimpleCostVector &v1, const SimpleCostVector &v2)
{
//---------------------------------
// Initialize result to vector v1.
//---------------------------------
SimpleCostVector resultVector = v1;
//----------------------------------------------------------------------
// Subtract each component of v2 from v1's corresponding component,
// but make sure no resulting component has a negative value.
//----------------------------------------------------------------------
for (Lng32 vecIdx = 0; vecIdx < COUNT_OF_SIMPLE_COST_COUNTERS; vecIdx++)
{
resultVector.counter_[vecIdx] -= v2.counter_[vecIdx];
//if ( resultVector.counter_[vecIdx] < csZero )
// {
// resultVector.counter_[vecIdx] = csZero;
// }
(resultVector.counter_[vecIdx]).minCsZero();
}
//------------------------------------------------
// Set result's number of probes to that of v1's.
//------------------------------------------------
resultVector.setNumProbes(v1.getNumProbes());
return resultVector;
} // operator-
//<pb>
//==============================================================================
// Multiply a specified vector's components by a specified scalar.
//
// Note: this operation leaves the normal memory, persistent memory and number
// of probes unaffected.
//
// Input:
// vector -- Specified vector.
//
// scalar -- Specified scalar.
//
// Output:
// none
//
// Return:
// Specified vector multiplied by a specified scalar.
//
//==============================================================================
SimpleCostVector
operator*(const SimpleCostVector &vector, const CostScalar &scalar)
{
//----------------------------------------
// Initialize result to specified vector.
//----------------------------------------
SimpleCostVector resultVector = vector;
//--------------------------------------------------
// Multiply each component by the specified scalar.
//--------------------------------------------------
for (Lng32 vecIdx = 0; vecIdx < COUNT_OF_SIMPLE_COST_COUNTERS; vecIdx++)
{
resultVector.counter_[vecIdx] *= scalar;
}
//-------------------------------------------------------------------------
// Leave normal memory, persistent memory and number of probes unaffected.
//-------------------------------------------------------------------------
/*j as of 04/12/01 Normal & Persistent Memory not used for costing
resultVector.setNormalMemory ( vector.getNormalMemory() );
resultVector.setPersistentMemory( vector.getPersistentMemory() );
j*/
resultVector.setNumProbes ( vector.getNumProbes() );
return resultVector;
} // operator*
//<pb>
//==============================================================================
// Divide a specified vector's components by a specified scalar.
//
// Note: this operation leaves the normal memory, persistent memory and number
// of probes unaffected.
//
// Input:
// vector -- Specified vector numerator.
//
// scalar -- Specified scalar divisor.
//
// Output:
// none
//
// Return:
// Specified vector divided by a specified scalar.
//
//==============================================================================
SimpleCostVector
operator/(const SimpleCostVector &vector, const CostScalar &scalar)
{
//----------------------
// No division by zero.
//----------------------
CMPASSERT( scalar.isGreaterThanZero() /* > csZero */ );
//----------------------------------------
// Initialize result to specified vector.
//----------------------------------------
SimpleCostVector resultVector = vector;
//------------------------------------------------
// Divide each component by the specified scalar.
//------------------------------------------------
for (Lng32 vecIdx = 0; vecIdx < COUNT_OF_SIMPLE_COST_COUNTERS; vecIdx++)
{
resultVector.counter_[vecIdx] /= scalar;
}
//-------------------------------------------------------------------------
// Leave normal memory, persistent memory and number of probes unaffected.
//-------------------------------------------------------------------------
/*j as of 04/12/01 Normal & Persistent Memory not used for costing
resultVector.setNormalMemory ( vector.getNormalMemory() );
resultVector.setPersistentMemory( vector.getPersistentMemory() );
j*/
resultVector.setNumProbes ( vector.getNumProbes() );
return resultVector;
} // operator/
//<pb>
//==============================================================================
// Add two specified vectors using blocking vector addition.
//
// Blocking vector addition has the property that the sum of the elapsed times
// of the two specified vectors equals the elapsed time of their sum. This is
// achieved by adding an appropriate amount to the idle time component of the
// result.
//
// Elapsed time computations depend on a performance goal as specified in
// required physical properties.
//
// Note: this operation makes the number of probes in the result
// vector equal to that of the first specified vector.
//
// Input:
// v1 -- First specified vector.
//
// v2 -- Second specified vector.
//
// rpp -- Required physical properties containing specified performance goal.
//
// Output:
// none
//
// Return:
// Sum of v1 and v2 using blocking vector addition.
//
//==============================================================================
SimpleCostVector
blockingAdd(const SimpleCostVector &v1,
const SimpleCostVector &v2,
const ReqdPhysicalProperty* const rpp)
{
//--------------------------------------------------------------------
// Initialize result to simple vector sum. The Idle time component
// will be adjusted below.
//--------------------------------------------------------------------
SimpleCostVector resultVector = v1 + v2;
//-------------------------------------------------------------------------
// Make result vector's elapsed time equal to the sum of the elapsed times
// of the two specified vectors.
//-------------------------------------------------------------------------
CostScalar elapsedTimeV1 = v1.getElapsedTime(rpp);
CostScalar elapsedTimeV2 = v2.getElapsedTime(rpp);
CostScalar elapsedTimeResult = resultVector.getElapsedTime(rpp);
resultVector.addToIdleTime(elapsedTimeV1 + elapsedTimeV2 - elapsedTimeResult);
return resultVector;
} // blockingAdd
//<pb>
//============================================================================
// Unary case. Add two specified vectors using overlapped vector addition.
//
// Overlapped vector addition takes into account that in some cases we can
// overlap (at least partially) I/O and messaging. Thus, the CPU, Normal
// Memory, Persistent Memory and Temporary Disk Space components get added as in
// normal vector addition, but the I/O and message related components are
// calculated using the following general formula:
//
// result[C] = MAX(v1[C],v2[C]) + FF[C]*MIN(v1[C],v2[C])
//
// In the above formula, FF[C] is a fractional fudge factor indicating the
// degree of overlap for component C. Thus, for a complete overlap, FF[C] == 0.
// For no overlap (i.e. simple vector addition) FF[C] == 1.
//
// Also, for unary overlapped addition, the idle time of one vector cannot be reduced
// by the active time of the other.
//
// Note: this operation makes the number of probes in the result
// vector equal to that of the first specified vector.
//
// Input:
// v1 -- First specified vector.
//
// v2 -- Second specified vector.
//
// Output:
// none
//
// Return:
// Sum of v1 and v2 using overlapped vector addition.
//
//==============================================================================
SimpleCostVector
overlapAddUnary(const SimpleCostVector &v1, const SimpleCostVector &v2)
{
//--------------------------------------------------------------------
// Initialize result to simple vector sum. IO, Message and Idle time
// components will be adjusted below.
//--------------------------------------------------------------------
SimpleCostVector resultVector = v1 + v2;
//----------------------------------------------------
// Obtain fudge factors for IO and Message components.
//----------------------------------------------------
double ff_IO =
CostPrimitives::getBasicCostFactor(MSCF_OV_IO);
double ff_MSG =
CostPrimitives::getBasicCostFactor(MSCF_OV_MSG);
//------------------------
// Compute overlapped IO.
//------------------------
if ( v1.getIOTime() > v2.getIOTime() )
resultVector.setIOTime (v1.getIOTime() +
CostScalar (ff_IO) * v2.getIOTime());
else
resultVector.setIOTime (v2.getIOTime() +
CostScalar (ff_IO) * v1.getIOTime());
//-------------------------------
// Compute overlapped Messaging.
//-------------------------------
if ( v1.getMessageTime() > v2.getMessageTime() )
resultVector.setMSGTime ( v1.getMessageTime() +
CostScalar (ff_MSG) * v2.getMessageTime() );
else
resultVector.setMSGTime ( v2.getMessageTime() +
CostScalar (ff_MSG) * v1.getMessageTime() );
//---------------------------------------------------------------
// Set new idle time to sum of each vector's idle time.
//---------------------------------------------------------------
resultVector.setIdleTime(v1.getIdleTime() + v2.getIdleTime());
return resultVector;
} // overlapAddUnary
//<pb>
//============================================================================
// Add two specified vectors using overlapped vector addition.
//
// Overlapped vector addition takes into account that in some cases we can
// overlap (at least partially) I/O and messaging. Thus, the CPU, Normal
// Memory, Persistent Memory and Temporary Disk Space components get added as in
// normal vector addition, but the I/O and message related components are
// calculated using the following general formula:
//
// result[C] = MAX(v1[C],v2[C]) + FF[C]*MIN(v1[C],v2[C])
//
// In the above formula, FF[C] is a fractional fudge factor indicating the
// degree of overlap for component C. Thus, for a complete overlap, FF[C] == 0.
// For no overlap (i.e. simple vector addition) FF[C] == 1.
//
// Also, for overlapped addition, the idle time of one vector can be reduced
// by the active time of the other (and vice versa). Idle time, however, can't
// become negative.
//
// Note: this operation makes the number of probes in the result
// vector equal to that of the first specified vector.
//
// Input:
// v1 -- First specified vector.
//
// v2 -- Second specified vector.
//
// Output:
// none
//
// Return:
// Sum of v1 and v2 using overlapped vector addition.
//
//==============================================================================
SimpleCostVector
overlapAdd(const SimpleCostVector &v1, const SimpleCostVector &v2)
{
//--------------------------------------------------------------------
// Initialize result to simple vector sum. IO, Message and Idle time
// components will be adjusted below.
//--------------------------------------------------------------------
SimpleCostVector resultVector = v1 + v2;
//----------------------------------------------------
// Obtain fudge factors for IO and Message components.
//----------------------------------------------------
double ff_IO =
CostPrimitives::getBasicCostFactor(MSCF_OV_IO);
double ff_MSG =
CostPrimitives::getBasicCostFactor(MSCF_OV_MSG);
//------------------------
// Compute overlapped IO.
//------------------------
if ( v1.getIOTime() > v2.getIOTime() )
resultVector.setIOTime (v1.getIOTime() +
CostScalar (ff_IO) * v2.getIOTime());
else
resultVector.setIOTime (v2.getIOTime() +
CostScalar (ff_IO) * v1.getIOTime());
//-------------------------------
// Compute overlapped Messaging.
//-------------------------------
if ( v1.getMessageTime() > v2.getMessageTime() )
resultVector.setMSGTime ( v1.getMessageTime() +
CostScalar (ff_MSG) * v2.getMessageTime() );
else
resultVector.setMSGTime ( v2.getMessageTime() +
CostScalar (ff_MSG) * v1.getMessageTime() );
double minIdleTime = v1.getIdleTime().value();
double v2IdleTime = v2.getIdleTime().value();
double elapsedTimeDif =
v1.getElapsedTime(*(CURRSTMT_OPTDEFAULTS->getDefaultPerformanceGoal())).value() -
v2.getElapsedTime(*(CURRSTMT_OPTDEFAULTS->getDefaultPerformanceGoal())).value();
if ( minIdleTime < v2IdleTime )
{
if ( elapsedTimeDif > 0.0 )
// try to reduce max idle time which is v2IdleTime
minIdleTime = MAXOF(minIdleTime, v2IdleTime - elapsedTimeDif);
else
// no space in v1-v2 to spread idle time difference
minIdleTime = v2IdleTime;
}
else
// v1 v2 change places, v1 has bigger idle time
{
if ( elapsedTimeDif < 0.0 )
// try to spread idle time difference over v2-v1 vector
minIdleTime = MAXOF(v2IdleTime, minIdleTime + elapsedTimeDif);
else
; //no space in v2-v1 to spread idle time difference
}
resultVector.setIdleTime(minIdleTime);
/*
//--------------------------------------------------------------------------
// Make sure the following equality holds:
//
// ElapsedTime(result) >= MAX( ElapsedTime(v1), ElapsedTime(v2) )
//
// In other words, don't let result vector have a smaller elapsed time than
// either input vector.
//--------------------------------------------------------------------------
CostScalar lowerBoundTime
= MAXOF( v1.getElapsedTime(*DefaultPerformanceGoal),
v2.getElapsedTime(*DefaultPerformanceGoal) );
CostScalar resultTime = resultVector.getElapsedTime(*DefaultPerformanceGoal);
if (resultTime < lowerBoundTime)
{
resultVector.addToIdleTime(lowerBoundTime - resultTime);
}
//----------------------------------------------------------------------------
// At this point, one certainly expects the result vector's elapsed time to
// exceed the lower bound, but due to the vagueries of floating point
// arithmetic, the result vector's elapsed time could still be just a tiny bit
// below the lower bound. By adding back double the difference, no matter how
// flaky the floating point arithmetic is, we can guarantee the loop below
// eventually terminates.
//----------------------------------------------------------------------------
resultTime = resultVector.getElapsedTime(*DefaultPerformanceGoal);
while ((resultTime != lowerBoundTime) && (resultTime < lowerBoundTime))
{
resultVector.addToIdleTime( (lowerBoundTime - resultTime) * csTwo );
resultTime = resultVector.getElapsedTime(*DefaultPerformanceGoal);
}
*/
return resultVector;
} // overlapAdd
//<pb>
//==============================================================================
// Of two specified vectors, return the one having the smallest elapsed time.
//
// Note: If neccessary, callers of this routine must assure proper
// normalization of the two specified vectors
//
// Input:
// v1 -- First specified vector.
//
// v2 -- Second specified vector.
//
// rpp -- Required physical properties containing specified performance goal.
//
// Output:
// none
//
// Return:
// Vector having smallest elapsed time.
//
//==============================================================================
SimpleCostVector
etMINOF(const SimpleCostVector &v1,
const SimpleCostVector &v2,
const ReqdPhysicalProperty* const rpp)
{
if (v1.getElapsedTime(rpp) < v2.getElapsedTime(rpp)) {
return v1;
}
else {
return v2;
}
} //etMINOF
//<pb>
//==============================================================================
// Of two specified vectors, return the one having the largest elapsed time.
//
// Note: If neccessary, callers of this routine must assure proper
// normalization of the two specified vectors
//
// Input:
// v1 -- First specified vector.
//
// v2 -- Second specified vector.
//
// rpp -- Required physical properties containing specified performance goal.
//
// Output:
// none
//
// Return:
// Vector having largest elapsed time.
//
//==============================================================================
SimpleCostVector
etMAXOF(const SimpleCostVector &v1,
const SimpleCostVector &v2,
const ReqdPhysicalProperty* const rpp)
{
if (v1.getElapsedTime(rpp) > v2.getElapsedTime(rpp)) {
return v1;
}
else {
return v2;
}
} //etMAXOF
//<pb>
//==============================================================================
// Form a new vector out of two specified vectors such that each component
// of the resulting vector has the minimum value of the two corresponding
// components in the specified vectors. For IO and Message components, choose
// from the vector having the minimum IO time and Message time respectively.
//
// Note: this operation makes the number of probes in the result
// vector equal to that of the first specified vector.
//
// Input:
// v1 -- First specified vector.
//
// v2 -- Second specified vector.
//
// Output:
// none
//
// Return:
// Vector with minimum values in each component.
//
//==============================================================================
SimpleCostVector
vecMINOF(const SimpleCostVector &v1, const SimpleCostVector &v2)
{
SimpleCostVector resultVector;
//-----------------------------------------------------------------
// Choose minimum for all components except
// NUM_PROBES which is the last component of vector
// so repeating the loop < "COUNT_OF_SIMPLE_COST_COUNTERS - 1" times
//------------------------------------------------------------------
for(short index=0;index<COUNT_OF_SIMPLE_COST_COUNTERS - 1;index++)
resultVector.counter_[index] = MINOF(v1.counter_[index],
v2.counter_[index] );
//---------------------------------------------------------------
// By convention, set result's number of probes to that of v1's.
//---------------------------------------------------------------
resultVector.setNumProbes( v1.getNumProbes() );
return resultVector;
} //vecMINOF
//<pb>
//==============================================================================
// Form a new vector out of two specified vectors such that each component
// of the resulting vector has the maximum value of the two corresponding
// components in the specified vectors. For IO and Message components, choose
// from the vector having the maximum IO time and Message time respectively.
//
// Note: this operation makes the number of probes in the result
// vector equal to that of the first specified vector.
//
// Input:
// v1 -- First specified vector.
//
// v2 -- Second specified vector.
//
// Output:
// none
//
// Return:
// Vector with maximum values in each component.
//
//==============================================================================
SimpleCostVector
vecMAXOF(const SimpleCostVector &v1, const SimpleCostVector &v2)
{
SimpleCostVector resultVector;
//-----------------------------------------------------------------
// Choose maximum for all components except
// NUM_PROBES which is the last component of vector
// so repeating the loop < "COUNT_OF_SIMPLE_COST_COUNTERS - 1" times
//------------------------------------------------------------------
for(short index=0;index<COUNT_OF_SIMPLE_COST_COUNTERS - 1;index++)
resultVector.counter_[index] = MAXOF(v1.counter_[index],
v2.counter_[index] );
//---------------------------------------------------------------
// By convention, set result's number of probes to that of v1's.
//---------------------------------------------------------------
resultVector.setNumProbes( v1.getNumProbes() );
return resultVector;
} //vecMAXOF
//<pb>
//==============================================================================
// Determine if first specified vector is a lower bound for second specified
// vector (i.e. each component of first vector is less than or equal to the
// second vector's corresponding component).
//
// Input:
// v1 -- First specified vector.
//
// v2 -- Second specified vector.
//
// Output:
// none
//
// Return:
// True if v1 is a lower bound for v2; false otherwise.
//
//==============================================================================
NABoolean
isLowerBound(const SimpleCostVector &v1, const SimpleCostVector &v2)
{
//---------------------------------------------------------------
// Verify that each component of v1 is less than or equal to the
// corresponding component of v2.
//---------------------------------------------------------------
for (Lng32 vecIdx = 0; vecIdx < COUNT_OF_SIMPLE_COST_COUNTERS; vecIdx++)
{
if (v1.counter_[vecIdx] > v2.counter_[vecIdx])
{
return FALSE;
}
}
return TRUE;
} // isLowerBound()
//<pb>
// -----------------------------------------------------------------------
// methods for class SimpleCostVector used in SCM.
// -----------------------------------------------------------------------
SimpleCostVector::SimpleCostVector(
const CostScalar & CPUTime,
const CostScalar & IOTime,
const CostScalar & MSGTime,
const CostScalar & idleTime,
const CostScalar & tcProc,
const CostScalar & tcProd,
const CostScalar & tcSent,
const CostScalar & ioRand,
const CostScalar & ioSeq,
const CostScalar & numProbes)
{
counter_[CPU_TIME] = CPUTime;
counter_[IO_TIME] = IOTime;
counter_[MSG_TIME] = MSGTime;
counter_[IDLE_TIME] = idleTime;
counter_[TC_PROC] = tcProc;
counter_[TC_PROD] = tcProd;
counter_[TC_SENT] = tcSent;
counter_[IO_RAND] = ioRand;
counter_[IO_SEQ] = ioSeq;
counter_[NUM_PROBES] = numProbes;
}
// -----------------------------------------------------------------------
// methods for class SimpleCostVector used in OCM.
// -----------------------------------------------------------------------
SimpleCostVector::SimpleCostVector(
const CostScalar & CPUTime,
const CostScalar & IOTime,
const CostScalar & MSGTime,
const CostScalar & idleTime,
const CostScalar & numProbes)
{
counter_[CPU_TIME] = CPUTime;
counter_[IO_TIME] = IOTime;
counter_[MSG_TIME] = MSGTime;
counter_[IDLE_TIME] = idleTime;
counter_[TC_PROC] = csZero;
counter_[TC_PROD] = csZero;
counter_[TC_SENT] = csZero;
counter_[IO_RAND] = csZero;
counter_[IO_SEQ] = csZero;
counter_[NUM_PROBES] = numProbes;
}
Lng32 SimpleCostVector::entries() const
{
return COUNT_OF_SIMPLE_COST_COUNTERS;
}
//<pb>
CostScalar SimpleCostVector::operator[] (Lng32 ix) const
{
CMPASSERT((ix >= 0) AND (ix < COUNT_OF_SIMPLE_COST_COUNTERS));
return counter_[ix];
}
SimpleCostVector::SimpleCostVector(const SimpleCostVector &other)
{
for (Lng32 i = 0; i < COUNT_OF_SIMPLE_COST_COUNTERS; i++)
counter_[i] = other.counter_[i];
}
SimpleCostVector & SimpleCostVector::operator = (const SimpleCostVector &other)
{
for (Lng32 i = 0; i < COUNT_OF_SIMPLE_COST_COUNTERS; i++)
counter_[i] = other.counter_[i];
return *this;
}
// Simple addition of vectors; does not affect the number of probes, and they
// must be the same in both vectors
SimpleCostVector & SimpleCostVector::operator += (const SimpleCostVector &other)
{
*this = *this + other;
return *this;
}
//<pb>
// Simple substraction of vectors; does not affect the number of probes, and
// they must be the same in both vectors; all negatives in the result are set
// to zero
SimpleCostVector & SimpleCostVector::operator -= (const SimpleCostVector
&other)
{
*this = *this - other;
return *this;
}
//<pb>
// Simple multiplication of vector by an scalar; does not affect memory
// components or the number of probes
SimpleCostVector & SimpleCostVector::operator *= (const CostScalar &other)
{
*this = *this * other;
return *this;
}
//<pb>
// Simple division of vector by an scalar; does not affect memory
// components or the number of probes
SimpleCostVector & SimpleCostVector::operator /= (const CostScalar &other)
{
*this = *this / other;
return *this;
}
//<pb>
// prints the private members of this class
/*
void SimpleCostVector::print(FILE* ofd) const
{
fprintf(ofd, "CPU cost: %g \n CPU time: %g \n Temporary disk usage: %g \n Idle time: %g \n IO time: %g \n Kilobytes of local messages: %g \n Kilobytes of remote messages: %g \n Time spent processing messages: %g \n Normal memory: %g \n Kilobytes of I/O transfered: %g \n Number of local messages: %g \n Number of probes: %g \n Number of remote messages: %g \n Number of seeks: %g \n Amount of memory persisting, in KB: %g \n",
getCPUCost().value(), getCPUTime().value(), getDiskUsage().value(),
getIdleTime().value(), getIOTime().value(),
getKBLocalMessages().value(), getKBRemoteMessages().value(),
getMessageTime().value(), getNormalMemory().value(), getNumKBytes().value(),
getNumLocalMessages().value(), getNumProbes().value(),
getNumRemoteMessages().value(), getNumSeeks().value(),
getPersistentMemory().value());
}
*/
// excluded for coverage because it's a debug code
void SimpleCostVector::print(FILE* pfp) const
{
fprintf(pfp,"CPUTime=%g\n",counter_[CPU_TIME].value());
fprintf(pfp,"IOTime=%g\n",counter_[IO_TIME].value());
fprintf(pfp,"MSGTime=%g\n",counter_[MSG_TIME].value());
fprintf(pfp,"idleTime=%g\n",counter_[IDLE_TIME].value());
fprintf(pfp,"tuple processed=%g\n",counter_[TC_PROC].value());
fprintf(pfp,"tuple produced=%g\n",counter_[TC_PROD].value());
fprintf(pfp,"tuple sent=%g\n",counter_[TC_SENT].value());
fprintf(pfp,"IO rand=%g\n",counter_[IO_RAND].value());
fprintf(pfp,"IO seq=%g\n",counter_[IO_SEQ].value());
fprintf(pfp,"num Probes=%g\n",counter_[NUM_PROBES].value());
/*
for (Lng32 i = 0; i < COUNT_OF_SIMPLE_COST_COUNTERS; i++)
fprintf(pfp,"%g,",counter_[i].value());
*/
fprintf(pfp,"\n");
}
//<pb>
//<pb>
// Return a string reporting the details of the cost vector in terms of
// its individual components, based on the cost model in use
// The argument ownline indicates if the cost components need to be in their
// own lines.
// The argument prefix optionally gives a prefix string to be printed
// This gets called only when internal CQD EXPLAIN_DETAIL_COST_FOR_CALIBRATION
// is ON for debugging purpose. So, exclude for coverage
const NAString SimpleCostVector::getDetailDesc(const DefaultToken ownline,
const char *prefix) const
{
NAString dtlDesc(CmpCommon::statementHeap());
// Declare detail so that we never have a string larger than this.
char detail[400];
char separator[80];
// When details need to be on their own lines use new line as separator
if (ownline == DF_ON)
{
snprintf(separator, sizeof(separator), "\n%s", prefix);
}
else
{
separator[0] = '\0';
}
// printing all the cost scalars of a simple cost vector
if (CmpCommon::getDefault(SIMPLE_COST_MODEL) == DF_ON)
{
snprintf(detail, sizeof(detail),
"%sTC_PROC: %g %sTC_PROD: %g %sTC_SENT: %g %sIO_SEQ: %g %sIO_RAND: %g",
prefix,
counter_[TC_PROC].value(), separator,
counter_[TC_PROD].value(), separator,
counter_[TC_SENT].value(), separator,
counter_[IO_SEQ].value(), separator,
counter_[IO_RAND].value());
}
else
{
snprintf(detail, sizeof(detail),
"%sCPU_TIME: %g %sIO_TIME: %g %sMSG_TIME: %g %sIDLE_TIME: %g %sPROBES: %g",
prefix,
MINOF(counter_[CPU_TIME], 1e32).value(), separator,
MINOF(counter_[IO_TIME], 1e32).value(), separator,
counter_[MSG_TIME].value(), separator,
counter_[IDLE_TIME].value(), separator,
counter_[NUM_PROBES].value());
}
dtlDesc = detail;
return dtlDesc;
} // SimpleCostVector::getDetailDesc()
//<pb>
// NCM specific method.
CostScalar SimpleCostVector::getElapsedTime() const
{
// assert if called by OCM .
DCMPASSERT(CmpCommon::getDefault(SIMPLE_COST_MODEL) == DF_ON);
//Moved from scmComputeTotalCost() method.
CostScalar seqIOWeight, randIOWeight;
seqIOWeight = ActiveSchemaDB()->getDefaults().getAsDouble(NCM_SEQ_IO_WEIGHT);
randIOWeight = ActiveSchemaDB()->getDefaults().getAsDouble(NCM_RAND_IO_WEIGHT);
CostScalar tuplesProcessed = getTcProc();
CostScalar tuplesProduced = getTcProd();
CostScalar tuplesSent = getTcSent();
CostScalar seqIOs = getIoSeq();
CostScalar randIOs = getIoRand();
return (((tuplesProcessed + tuplesProduced) + tuplesSent) +
((seqIOs * seqIOWeight) + (randIOs * randIOWeight)));
}
// Finds elapsed time in vector
CostScalar SimpleCostVector::getElapsedTime(const ReqdPhysicalProperty* const
rpp) const
{
const CostWeight* cw;
const PerformanceGoal *goal;
if (rpp) {
goal = rpp->getPerformanceGoal();
cw = rpp->getCostWeight();
}
else {
goal = CURRSTMT_OPTDEFAULTS->getDefaultPerformanceGoal();
cw = CURRSTMT_OPTDEFAULTS->getDefaultCostWeight();
}
return getElapsedTime(*goal,cw);
}
//<pb>
// Finds elapsed time in vector. Kind can be last row, first row or total.
CostScalar SimpleCostVector::getElapsedTime(const PerformanceGoal& goal,
const CostWeight* const vectorWeight) const
{
CMPASSERT(goal.isOptimizeForFirstRow() || goal.isOptimizeForLastRow() ||
goal.isOptimizeForResourceConsumption());
CostScalar et = csZero;
const CostScalar & message = getMessageTime();
const CostScalar & cpu = getCPUTime();
const CostScalar & io = getIOTime();
if (goal.isOptimizeForResourceConsumption())
{
CostWeight *vecWeight;
if (vectorWeight == NULL)
vecWeight = CURRSTMT_OPTDEFAULTS->getDefaultCostWeight();
else
vecWeight = (CostWeight*) vectorWeight;
et = vecWeight->convertToElapsedTime(*this);
}
else if ((goal.isOptimizeForFirstRow() || goal.isOptimizeForLastRow()) &&
(CmpCommon::getDefault(TOTAL_RESOURCE_COSTING) == DF_OFF))
{
const CostScalar & maxOfIOAndCpuAndMessage =
MAXOF(io, MAXOF(message,cpu));
et = maxOfIOAndCpuAndMessage + getIdleTime();
}
else // additive resource costing
{
// For now, just add the various resource units (converted
// to time) together. In the future, may want to consider
// waiting each component by the appropriate cost weight.
et = message + cpu + io;
}
return et;
}
// This routine has the effect of setting the number of probes
// to the input factor and scaling all other members accordingly
// with the exception of the memory components. The intention
// is to be able to do operations between the resulting vector
// and another vector whose number of probes is factor.
const SimpleCostVector& SimpleCostVector::normalize(const CostScalar & factor)
{
CMPASSERT( factor.isGreaterThanZero() /* > csZero */ );
//-------------------------------------------------------------------
// Normalization does not affect memory values, so save them off and
// restore them later.
//-------------------------------------------------------------------
/*j const CostScalar normalMemory = getNormalMemory();
const CostScalar persistantMemory = getPersistentMemory();
j*/
//---------------------------------------------
// Normalize each component to the new factor.
//---------------------------------------------
const CostScalar numProbes = getNumProbes() / factor;
for (Lng32 vecIdx = 0; vecIdx < COUNT_OF_SIMPLE_COST_COUNTERS; vecIdx++)
{
counter_[vecIdx] *= numProbes;
}
//-----------------------------------------------------------------------
// Restore original memory values, and set number of probes to specified
// factor.
//-----------------------------------------------------------------------
/*j setNormalMemory(normalMemory);
setPersistentMemory(persistantMemory);
j*/
setNumProbes(factor);
return *this;
}
//<pb>
//==============================================================================
// Return a copy of this vector normalized to a specified factor. Do not
// change this vector.
//
// Input:
// factor -- specified factor to which this vector should be normalized.
//
// Output:
// none
//
// Return:
// normalized cost.
//
//==============================================================================
SimpleCostVector
SimpleCostVector::getNormalizedVersion(const CostScalar & factor) const
{
SimpleCostVector normVec(*this);
return normVec.normalize(factor);
} // SimpleCostVector::getNormalizedVersion
//<pb>
//==============================================================================
// Add a specified vector to this vector using overlapped vector addition.
//
// Overlapped vector addition takes into account that in some cases we can
// overlap (at least partially) I/O and messaging. Thus, the CPU, Normal
// Memory, Persistent Memory and Temporary Disk Space components get added as in
// normal vector addition, but the I/O and message related components are
// calculated using the following general formula:
//
// result[C] = MAX(v1[C],v2[C]) + FF[C]*MIN(v1[C],v2[C])
//
// In the above formula, FF[C] is a fractional fudge factor indicating the
// degree of overlap for component C. Thus, for a complete overlap, FF[C] == 0.
// For no overlap (i.e. simple vector addition) FF[C] == 1.
//
// Also, for overlapped addition, the idle time of one vector can be reduced
// by the active time of the other (and vice versa). Idle time, however, can't
// become negative.
//
// Note: this operation makes the number of probes in the result
// vector equal to that of the first specified vector.
//
// Input:
// other -- other vector whose value will be accumulated into this vector
// using overlapped addition.
//
// Output:
// none
//
// Return:
// Accumulated vector using overlapped addition.
//
//==============================================================================
const SimpleCostVector&
SimpleCostVector::overlappedAdd(const SimpleCostVector& other)
{
*this = overlapAddUnary(*this,other);
return *this;
} // SimpleCostVector::overlappedAdd
//<pb>
//==============================================================================
// Add a specified number of copies of this vector using overlapped vector
// addition .
//
// Note: this operation leaves the normal memory, persistent memory and number
// of probes unaffected.
//
// Input:
// times -- specified number of copies of this vector to add.
//
// Output:
// none
//
// Return:
// Result of adding vector to itself a repeated number of times.
//
//==============================================================================
const SimpleCostVector&
SimpleCostVector::repeatedOverlappedAdd(const Lng32 times)
{
//---------------------------------------------------------------------------
// Number of times must be positive. If it equals 1, we have nothing to do,
// so return immediately.
//---------------------------------------------------------------------------
if (times == 1)
{
return *this;
}
CMPASSERT(times >= 1);
//-------------------------------------------------------------------------
// For idle time, assume that only two instances of this vector overlap at
// any given time. In other words, determine the remaining idle time when
// this vector is added to itself once, and allot that idle time to all
// pairs of instances of this vector.
//-------------------------------------------------------------------------
SimpleCostVector temp = *this;
temp.overlappedAdd(*this);
counter_[IDLE_TIME] = temp.getIdleTime() * (times -1);
//-------------------------------------------------------------------------
// CPU and disk usage not overlappable, so use traditional multiplication.
//-------------------------------------------------------------------------
counter_[CPU_TIME] *= times;
//j counter_[DISK_USAGE] *= times;
//-------------------------------------------------------------------------
// Normal and persistent memory not overlappable, so use traditional
// multiplication. This was added to use repeatedOverlappedAdd instead of
// multiple calls for overlappedAdd with the same cast vector. SP 09/06/00
//-------------------------------------------------------------------------
/*j counter_[NORMAL_MEMORY] *= times;
counter_[PERSISTENT_MEMORY] *= times;
j*/
//----------------------------------------------------
// Obtain fudge factors for IO and Message components.
//----------------------------------------------------
double ff_IO =
CostPrimitives::getBasicCostFactor(MSCF_OV_IO);
double ff_MSG =
CostPrimitives::getBasicCostFactor(MSCF_OV_MSG);
//---------------------------------------------------------------------------
// Calculate repeated overlapped addition for the IO and Message components.
//
// Recall the formula for overlapped addition:
//
// result[C] = MAX(v1[C],v2[C]) + FF[C]*MIN(v1[C],v2[C])
//
// When v1 == v2, this reduces to:
//
// result[C] = v1[C] + FF[C]*v1[C].
//
// For "n" repeated overlapped additions, this becomes:
//
// result[C] = v1[C] + FF[C]*v1[C]*(n - 1).
//
//---------------------------------------------------------------------------
counter_[IO_TIME]
+= (counter_[IO_TIME] * ff_IO * (times - 1) );
counter_[MSG_TIME]
+= (counter_[MSG_TIME] * ff_MSG * (times - 1) );
return *this;
} //SimpleCostVector::repeatedOverlappedAdd()
//<pb>
// -----------------------------------------------------------------------
// scaleUpByNumProbes() scales up a simple cost vector by its number of
// probes. Memory and disk space are considered to be recyclable over
// probes, and hence not scaled up by this method.
// -----------------------------------------------------------------------
const SimpleCostVector& SimpleCostVector::scaleUpByNumProbes()
{
const CostScalar & numProbes = counter_[NUM_PROBES];
// Multiply all the components of the vector except the "NumProbes"
// normal memory, persitent memory & diskusage, as Numprobes in
// the last component and we don't have Normal, persistent memory
// and diskusage currently 04/2001 repeat loop
// "count_of_simple_cost_counters-1" times AND LEAVING PROBES
// Check for overflow. If overflow initialize counter_[i] by DBL_MAX
// The problem occurs only on NSK
// short overflow=0; //to check for overflow
for( Lng32 i = 0; i < COUNT_OF_SIMPLE_COST_COUNTERS - 1; i++ )
{
counter_[i] *= numProbes;
}
return *this;
}
//<pb>
// -----------------------------------------------------------------------
// scaleUpByCountOfCPUs() scales up a simple cost vector by the number
// of CPUs. This is typically used to convert a simple cost vector which
// tracks resource usage on a CPU to another simple cost vector which
// tracks total resource usage over all CPUs. All components except the
// no of probes are scaled up.
// -----------------------------------------------------------------------
const SimpleCostVector& SimpleCostVector::scaleUpByCountOfCPUs(
const Lng32 countOfCPUs)
{
return scaleByValue( countOfCPUs );
}
//<pb>
//==============================================================================
// Scale (up or down) this vector by a specified scalar value but leaving
// number of probes unaffected.
//
// Input:
// scalar -- Specified scalar.
//
// Output:
// none
//
// Return:
// This vector appropriately scaled.
//
//==============================================================================
SimpleCostVector&
SimpleCostVector::scaleByValue(const CostScalar &scalar)
{
//--------------------------------------------------
// Multiply each component by the specified scalar
// except the NUMPROBES which is the last component
// so just run the loop to "count_of_simple_cost_counters-1" times
//--------------------------------------------------
for (Lng32 vecIdx = 0; vecIdx < COUNT_OF_SIMPLE_COST_COUNTERS - 1; vecIdx++)
{
counter_[vecIdx] *= scalar;
}
return *this;
} // SimpleCostVector::scaleByValue()
//<pb>
//-------------------------------------------------------------------------
// SimpleCostVector::setToValue()
//--------------------------------------------------------------------------
void SimpleCostVector::setToValue(const CostScalar &scalar)
{
//--------------------------------------------------
// set each component to the specified scalar
// except the NUMPROBES which is the last component
//--------------------------------------------------
for (Lng32 vecIdx = 0; vecIdx < COUNT_OF_SIMPLE_COST_COUNTERS - 1; vecIdx++)
{
counter_[vecIdx] = scalar;
}
return;
}
//==============================================================================
// Ensure no component of this vector has no component that exceeds the
// corresponding component of a specified upper bound vector.
//
// Input:
// upperBoundVector -- specified vector which serves as an upper bound.
//
// Output:
// none
//
// Return:
// This vector appropriately bounded.
//
//==============================================================================
const SimpleCostVector&
SimpleCostVector::enforceUpperBound(const SimpleCostVector& upperBoundVector)
{
//--------------------------------------------------------------------------
// If any component of this vector exceeds the corresponding component of a
// specified upper bound vector, reduce this vector's component to the upper
// bound vector's corresponding component.
//--------------------------------------------------------------------------
for (Lng32 vecIdx = 0; vecIdx < COUNT_OF_SIMPLE_COST_COUNTERS; vecIdx++)
{
if (counter_[vecIdx] > upperBoundVector.counter_[vecIdx])
{
counter_[vecIdx] = upperBoundVector.counter_[vecIdx];
}
}
return *this;
} // SimpleCostVector::enforceUpperBound()
//<pb>
// Resets all components to zero
const SimpleCostVector& SimpleCostVector::reset()
{
for (Lng32 i = 0; i < COUNT_OF_SIMPLE_COST_COUNTERS; i++) {
counter_[i] = csZero;
}
// But Number of probes to be atleast one.
setNumProbes(csOne);
return *this;
}
//<pb>
//==============================================================================
// Indicates whether or not this object is a zero-vector.
//
// Note: number of probes is not taken into account when determining if this
// is a zero vector.
//
// Input:
// none
//
// Output:
// none
//
// Return:
// TRUE if this is a zero vector; FALSE otherwise.
//
//==============================================================================
// This method is no longer used, isZeroVectorWithProbes is used instead.
// So hide it from coverage
NABoolean
SimpleCostVector::isZeroVector() const
{
Lng32 vecIdx;
//-----------------------------------------------------------------------
// Check all components (except number of probes). If any are non-zero,
// report that this is not a zero vector.
//-----------------------------------------------------------------------
for (vecIdx = 0; vecIdx < NUM_PROBES; vecIdx++)
{
if ( (counter_[vecIdx]).isGreaterThanZero() /* != csZero */)
{
return FALSE;
}
}
//------------------------------------------------------------
// All relevant components have a zero value, so return TRUE.
//------------------------------------------------------------
return TRUE;
} // SimpleCostVector::isZero
// This method needs to be used instead of isZeroVector for checking
// blocking components of Cost Object: cpbcTotal and cpbc1.
// Otherwise we can have a case when after normalizing SimpleCostVector
// with huge number of probes it could look like zero vector - all
// components are less that COSTSCALAR EPSILON - but in fact it is not.
// This might cause inconsistency in costing.
// See Genesis case : 10-031021-5359
NABoolean
SimpleCostVector::isZeroVectorWithProbes() const
{
Lng32 vecIdx;
const CostScalar numProbes = getNumProbes();
//-----------------------------------------------------------------------
// Check all components (except number of probes). If any are non-zero,
// report that this is not a zero vector.
//-----------------------------------------------------------------------
if ( numProbes.isGreaterThanOne() )
{
for (vecIdx = 0; vecIdx < NUM_PROBES; vecIdx++)
{
if ( (counter_[vecIdx]*numProbes).isGreaterThanZero() /* != csZero */)
{
return FALSE;
}
}
}
else
// here we avoid unnecessary CostScalar multiplications
{
for (vecIdx = 0; vecIdx < NUM_PROBES; vecIdx++)
{
if ( (counter_[vecIdx]).isGreaterThanZero() /* != csZero */)
{
return FALSE;
}
}
}
//------------------------------------------------------------
// All relevant components have a zero value, so return TRUE.
//------------------------------------------------------------
return TRUE;
} // SimpleCostVector::isZeroVectorWithProbes
//<pb>
// -----------------------------------------------------------------------
// methods for class Cost
// -----------------------------------------------------------------------
//==============================================================================
// Cost Constructor.
//
// Input:
// currentProcessFirstRowCost -- resources necessary to produce operator's
// first row once blocking activity completes.
//
// currentProcessLastRowCost -- resources necessary to produce operator's
// last row once blocking activity completes.
//
// currentProcessBlockingCost -- resources necessary for blocking activity.
//
// countOfCPUs -- number of CPUs for executing this operator.
//
// planFragmentsPerCPU -- number of plan fragments per CPU.
//
// Output:
// none
//
// Return:
// none.
//
//==============================================================================
Cost::Cost(const SimpleCostVector* currentProcessFirstRowCost,
const SimpleCostVector* currentProcessLastRowCost,
const SimpleCostVector* currentProcessBlockingCost,
const Lng32 countOfCPUs,
const Lng32 planFragmentsPerCPU)
: cpScmlr_(),
cpScmDbg_(),
cpbc1_(),
cpbcTotal_(),
//jo opfr_(),
//jo oplr_(),
countOfCPUs_(countOfCPUs),
planFragmentsPerCPU_(planFragmentsPerCPU),
priority_()
{
//-----------------------------------------------
// First Row and Last Row Cost MUST be supplied!!
//-----------------------------------------------
CMPASSERT(currentProcessFirstRowCost != NULL);
CMPASSERT(currentProcessLastRowCost != NULL);
//-----------------------------------------------
// Checks must come first before using them!
//-----------------------------------------------
totalCost_ = *currentProcessLastRowCost;
cpfr_ = *currentProcessFirstRowCost;
cplr_ = *currentProcessLastRowCost;
#ifndef NDEBUG
//--------------------------------------------------------------------------
// Ensure no component of first row exceeds corresponding component of last
// row.
//--------------------------------------------------------------------------
CMPASSERT(isLowerBound(cpfr_,cplr_));
#endif
//---------------------------------------------------------------------------
// Since cost vectors supplied to the Cost constructor are on a "per stream"
// basis, we convert them to a "per CPU" basis. We use overlapped addition
// for first row and last row vectors because independent streams can overlap
// IO and message costs. For total cost, however, we use simple vector
// addition to reflect actual resources used.
// NOTE! Calls to overlappedAdd function were replaced by repeatedOverlappedAdd
// to save time in case of multiple plan fragments per CPU.
//---------------------------------------------------------------------------
if ( planFragmentsPerCPU > 1 )
{
totalCost_ = totalCost_ * planFragmentsPerCPU;
// NOTE!!! This multiplication operator * above will not multiply
// the memory and persistent memory of the cost vector.
// This needs to be done because if CPU has several plan fragments
// then every fragment will require its own memory and persistent
// memory. That's why we need to adjust them explicitly.
/*j Memory not used currently 05/09/01
totalCost_.setNormalMemory(totalCost_.getNormalMemory()
* planFragmentsPerCPU);
totalCost_.setPersistentMemory(totalCost_.getPersistentMemory()
* planFragmentsPerCPU);
j*/
// In the future this case needs further investigation.
cpfr_.repeatedOverlappedAdd(planFragmentsPerCPU);
cplr_.repeatedOverlappedAdd(planFragmentsPerCPU);
}
//----------------------------------------
// See if cost has any blocking activity.
//----------------------------------------
if(currentProcessBlockingCost != NULL)
{
//------------------------------------------------------------------------
// Cost has blocking activity. Store blocking vector in cpbc1_ and in
// a temporary vector used for reflecting blocking activity in total cost.
//------------------------------------------------------------------------
SimpleCostVector totalBlockingCost(*currentProcessBlockingCost);
cpbc1_ = *currentProcessBlockingCost;
//------------------------------------------------------------------
// Ensure that last row vector and blocking vector have same number
// of probes.
//------------------------------------------------------------------
CCMPASSERT(cplr_.getNumProbes() == cpbc1_.getNumProbes());
//---------------------------------------------------------------------
// As with first row and last row vectors, we need to convert the
// blocking vector from a "per stream" basis to a "per CPU" basis. For
// total cost, use simple vector addition to reflect actual resources
// used.
//---------------------------------------------------------------------
if ( planFragmentsPerCPU > 1 )
{
totalBlockingCost = totalBlockingCost * planFragmentsPerCPU;
// NOTE!!! This multiplication operator * above will not multiply
// the memory and persistent memory of the cost vector.
// This needs to be done because if CPU has several plan fragments
// then every fragment will require its own memory and persistent
// memory. That's why we need to adjust them explicitly.
/*j not used currently 05/2001
totalBlockingCost.setNormalMemory(totalBlockingCost.getNormalMemory()
* planFragmentsPerCPU);
totalBlockingCost.setPersistentMemory(totalBlockingCost.getPersistentMemory()
* planFragmentsPerCPU);
j*/
cpbc1_.repeatedOverlappedAdd(planFragmentsPerCPU);
}
//---------------------------------------------------------------------
// During preliminary costing when a cost object is initially created,
// cpbcTotal_ equals cpbc1_ by convention.
//---------------------------------------------------------------------
cpbcTotal_ = cpbc1_;
//---------------------------------------------------------------------
// Since blocking cost vectors reflect average usage per probe, we
// need to scale up this vector by number of probes before accumulating
// it in total cost.
//
// Note that scaling up by number of probes does not affect memory or
// temporary disk space used since we assume these can be reused for
// every probe.
// --------------------------------------------------------------------
totalBlockingCost.scaleUpByNumProbes();
totalCost_ += totalBlockingCost;
}
else
{
//--------------------------------------------------------------------
// No blocking vector supplied, so cpbc1_ and cpbcTotal_ will be zero
// vectors. For consistency, set their number of probes to that of
// the supplied last row vector.
//--------------------------------------------------------------------
cpbc1_.setNumProbes(cplr_.getNumProbes());
cpbcTotal_.setNumProbes(cplr_.getNumProbes());
}
//---------------------------------------------------------------------
// Since total cost reflects all resources used, we scale it up by the
// number of CPUs executing this query.
//---------------------------------------------------------------------
totalCost_.scaleUpByCountOfCPUs(countOfCPUs);
} // Cost Constructor.
//============================================================================
// Cost Constructor used in SCM code.
//
// Input:
// currentProcessScmCost -- resources used in SCM.
//
// Output:
// none
//
// Return:
// none.
//
//============================================================================
Cost::Cost(const SimpleCostVector* currentProcessScmCost)
: cpScmDbg_(),
cpfr_(),
cplr_(),
cpbc1_(),
cpbcTotal_(),
countOfCPUs_(1),
planFragmentsPerCPU_(1),
priority_()
{
//-----------------------------------------------
// SCM Cost MUST be supplied!!
//-----------------------------------------------
CMPASSERT(currentProcessScmCost != NULL);
//-----------------------------------------------
// Checks must come first before using them!
//-----------------------------------------------
cpScmlr_ = *currentProcessScmCost;
} // Cost Constructor for SCM.
//============================================================================
// Cost Constructor used in SCM code.
//
// Inputs:
// currentProcessScmCost -- resources used in SCM.
// currentOpDebugInfo -- debug information to be used internally only.
//
// Output:
// none
//
// Return:
// none.
//
//============================================================================
Cost::Cost(const SimpleCostVector* currentProcessScmCost,
const SimpleCostVector* currentOpDebugInfo)
: cpfr_(),
cplr_(),
cpbc1_(),
cpbcTotal_(),
countOfCPUs_(1),
planFragmentsPerCPU_(1),
priority_()
{
//-----------------------------------------------
// SCM Cost MUST be supplied!!
//-----------------------------------------------
CMPASSERT(currentProcessScmCost != NULL);
CMPASSERT(currentOpDebugInfo != NULL);
//-----------------------------------------------
// Checks must come first before using them!
//-----------------------------------------------
cpScmlr_ = *currentProcessScmCost;
cpScmDbg_ = *currentOpDebugInfo;
} // Cost Constructor for SCM.
//<pb>
///==============================================================================
// Effectively a virtual constructor for a Cost object.
//
// Input:
// none
//
// Output:
// none
//
// Return:
// none.
//
//==============================================================================
Cost*
Cost::duplicate()
{
Cost* costPtr = new (CmpCommon::statementHeap()) Cost(*this);
return costPtr;
}
//<pb>
//==============================================================================
// Cost destructor.
//
// Input:
// none
//
// Output:
// none
//
// Return:
// none.
//
//==============================================================================
Cost::~Cost()
{
} // Cost Destructor
//<pb>
// -----------------------------------------------------------------------
// Returns the cost vector "representative of" a performance goal. Read
// comments below.
// -----------------------------------------------------------------------
const SimpleCostVector& Cost::getCostVector(
const PerformanceGoal* const pfg) const
{
// ---------------------------------------------------------------------
// It is doubtful whether we should support this method in the new Cost
// object. For FR elapsed time optimization, we should optimize the sum
// ET(cpfr_)+ET(cpbcTotal_). For LR elapsed time optimization, we should
// optimize ET(cplr_)+ET(cpbcTotal_)*NumOfProbes. Thus, in those cases,
// we don't have a representative vector whose ET() is to be optimized,
// but rather, we have an expression made up of ET()'s of two vectors.
// This method, however, gives the false impression that the returned
// vector is the one we should optimize for the given performance goal.
// ---------------------------------------------------------------------
// SIMPLE_COST_MODEL check should be the first condition.
if (CmpCommon::getDefault(SIMPLE_COST_MODEL) == DF_ON)
return cpScmlr_;
else if(pfg->isOptimizeForLastRow())
return cplr_;
else if(pfg->isOptimizeForFirstRow())
return cpfr_;
else // if(pfg->isOptimizeForResourceConsumption())
return cplr_;
}
//<pb>
// -----------------------------------------------------------------------
// Compares two Cost objects according to the performance goal and cost
// weights if applicable, returns MORE if this > other, SAME if they are
// equal and LESS otherwise.
// -----------------------------------------------------------------------
COMPARE_RESULT Cost::compareCosts(
const Cost & other,
const ReqdPhysicalProperty* const rpp) const
{
// Higher priority plans always win the comparison and considered "cheaper"
if (priority_ > other.priority_)
return LESS;
if (priority_ < other.priority_)
return MORE;
// if SIMPLE_COST_MODEL call scmCompareCosts() function.
if (CmpCommon::getDefault(SIMPLE_COST_MODEL) == DF_ON)
{
return scmCompareCosts(other, rpp);
}
// Plans with equal priorities proceed to real cost comparison
// Fix for coverity cid #1373: pointer pfg is unused.
// const PerformanceGoal* pfg;
// pfg = (rpp ? rpp->getPerformanceGoal() : DefaultPerformanceGoal);
const CostWeight * rcw;
rcw = (rpp ? rpp->getCostWeight() : CURRSTMT_OPTDEFAULTS->getDefaultCostWeight());
// ---------------------------------------------------------------------
// If optimized for resource consumption, the set of cost weights
// specified in rpp are used. The weights are implcitly used when the
// elapsed time method is called.
// ---------------------------------------------------------------------
/*if(pfg->isOptimizeForResourceConsumption())
{
return
rcw->compareCostVectors(getCostVector(pfg),other.getCostVector(pfg));
}*/
// ---------------------------------------------------------------------
// We are optimizing for elapsed time
// ---------------------------------------------------------------------
ElapsedTime et1 (convertToElapsedTime(rpp) * priority_.riskPremium());
ElapsedTime et2 (other.convertToElapsedTime(rpp) *
other.priority_.riskPremium());
if ( CURRSTMT_OPTDEFAULTS->OPHuseCompCostThreshold() )
{
double ratio = et1.getValue()/et2.getValue();
if ( ratio < 0.999999 )
return LESS;
else if (ratio > 1.000001)
return MORE;
}
else if (et1 > et2) return MORE;
else if (et1 < et2) return LESS;
// If the elapsed time cost is the same, then look at individual components
// of last row cost as a tie breaker before trying total resources.
if ( CmpCommon::getDefault(COMP_BOOL_95) == DF_OFF )
{
CostScalar cpu_io_msg_1 = getCplr().getCPUTime() + getCplr().getIOTime()
+ getCplr().getMessageTime();
CostScalar cpu_io_msg_2 = other.getCplr().getCPUTime()
+ other.getCplr().getIOTime()
+ other.getCplr().getMessageTime();
if (cpu_io_msg_1 > cpu_io_msg_2)
return MORE;
else if (cpu_io_msg_1 < cpu_io_msg_2)
return LESS;
}
// If the individual components of last row cost is the same,
// then look at total cost (i.e. resources) as a tie-breaker.
// In order to compare total resources, first convert it to
// an elapsed time unit.
CostScalar tc_et1 = getTotalCost().getElapsedTime
(*(CURRSTMT_OPTDEFAULTS->getResourcePerformanceGoal()),rcw);
CostScalar tc_et2 = other.getTotalCost().getElapsedTime
(*(CURRSTMT_OPTDEFAULTS->getResourcePerformanceGoal()),rcw);
if (tc_et1 > tc_et2)
return MORE;
else if (tc_et1 < tc_et2)
return LESS;
else
return SAME;
} // Cost::compareCosts()
//<pb>
// -----------------------------------------------------------------------
// Elapsed time is just loosely interpreted as a single value representing
// the cost of the operation. It is used by Cascades to determine which
// plan costs less. In reality, it might be the real FR/LR elapsed time of
// the operation or it might be just a weighted sum of resource usage.
// -----------------------------------------------------------------------
ElapsedTime Cost::convertToElapsedTime(
const ReqdPhysicalProperty* const rpp) const
{
// if SIMPLE_COST_MODEL call scmComputeTotalCost() function.
if (CmpCommon::getDefault(SIMPLE_COST_MODEL) == DF_ON)
{
return scmComputeTotalCost();
}
const PerformanceGoal* pfg;
pfg = (rpp ? rpp->getPerformanceGoal() : CURRSTMT_OPTDEFAULTS->getDefaultPerformanceGoal());
// ---------------------------------------------------------------------
// Assume the given set of cost weights in rpp converts the Cost object
// into "ElapsedTime". Instead of taking totalCost_ for estimating
// "elapsed time", we consider the last row cost and the blocking cost
// even for the resource consumption goal
// ---------------------------------------------------------------------
/*if(pfg->isOptimizeForResourceConsumption())
{
CMPASSERT(rpp);
return rpp->getCostWeight()->convertToElapsedTime(totalCost_);
}*/
// ---------------------------------------------------------------------
// Use the built-in mechanism to convert the object into elapsed time.
// The elapsed time of a Cost object is made up of two parts. The first
// part is either the elapsed time of cpfr_ or cplr_ depending on the
// performance goal. It represents the resource consumption in the
// current process above the last seen blocking operator. The second
// part is the elapsed time below the blocking operator. Total elapsed
// time is just the algebraic sum of the two, since activities above the
// blocking operator do not overlap with those below it.
// ---------------------------------------------------------------------
ElapsedTime et ( csZero );
CostScalar etBlock = cpbcTotal_.getElapsedTime(*pfg);
// FirstRow optimization is a disabled feature, hide from coverage
if(pfg->isOptimizeForFirstRow())
{
// Assume we get our first row in the first probe.
const CostScalar etFR = cpfr_.getElapsedTime(*pfg) + etBlock;
et = ElapsedTime( etFR );
}
else if(pfg->isOptimizeForLastRow())
{
// cpbcTotal_ is a per-probe average cost.
const CostScalar etLR = cplr_.getElapsedTime(*pfg);
CostScalar etLRPlusBlk;
etLRPlusBlk = etLR + etBlock * cpbcTotal_.getNumProbes();
et = ElapsedTime( etLRPlusBlk );
}
// total resource consumption is a disabled feature, hide from coverage
else if (pfg->isOptimizeForResourceConsumption())
{
// We use LR cost and BK costs for resource consumption goal as well
// The difference is that each component may get a different weight.
// As before cpbcTotal_ is a per-probe average cost.
// totalCost_ is not being used
const CostScalar etLR = cplr_.getElapsedTime(*pfg);
CostScalar etLRPlusBlk;
etLRPlusBlk = etLR + etBlock * cpbcTotal_.getNumProbes();
et = ElapsedTime( etLRPlusBlk );
}
return et;
} // Cost::convertToElapsedTime()
//<pb>
// This method is to be used only for displaying total cost information
// in the context of explain, visual debugger, etc. This is NOT to be used
// for computing and/or comparing plan cost information. In the context of NCM,
// internal costs used during plan computation use very different units than
// the total cost displayed externally to the user, and so these cannot be
// used interchangably.
ElapsedTime Cost::displayTotalCost (const ReqdPhysicalProperty* const rpp) const
{
if ((CmpCommon::getDefault(SIMPLE_COST_MODEL) == DF_OFF) ||
(CmpCommon::getDefault(EXPLAIN_DISPLAY_FORMAT) == DF_INTERNAL))
{
return convertToElapsedTime(rpp);
}
else
{
double cpu, io, msg, idle, seqIOs, randIOs, total;
Lng32 probes;
getExternalCostAttr(cpu, io, msg, idle, seqIOs, randIOs, total, probes);
return total;
}
}
// -----------------------------------------------------------------------
// The cost detail description consists of the individual components of the
// simple cost vector depending on the cost model in effect. This is expected
// to be used by callers like Explain etc. to display the cost details.
// -----------------------------------------------------------------------
const NAString Cost::getDetailDesc() const
{
NAString dtlDesc(CmpCommon::statementHeap());
// Declare line so that we never have a string larger than this.
char line[400];
double cpu, io, msg, idle, seqIOs, randIOs, total;
Lng32 probes;
getExternalCostAttr(cpu, io, msg, idle, seqIOs, randIOs, total, probes);
if ((CmpCommon::getDefault(SIMPLE_COST_MODEL) == DF_OFF) ||
(CmpCommon::getDefault(EXPLAIN_DISPLAY_FORMAT) == DF_EXTERNAL))
{
sprintf(line,
"CPU_TIME: %g IO_TIME: %g MSG_TIME: %g IDLE_TIME: %g PROBES: %d",
cpu, io, msg, idle, probes);
}
else
{
DCMPASSERT(CmpCommon::getDefault(SIMPLE_COST_MODEL) == DF_ON);
if (CmpCommon::getDefault(EXPLAIN_DISPLAY_FORMAT) == DF_EXTERNAL_DETAILED)
{
sprintf(line,
"CPU_TIME: %g IO_TIME: %g MSG_TIME: %g IDLE_TIME: %g PROBES: %d IO_SEQ: %g IO_RAND: %g",
cpu, io, msg, idle, probes, seqIOs, randIOs);
}
else
{ // DF_INTERNAL, print details in NCM internal format.
double tcProc, tcProd, tcSent, ioRand, ioSeq;
getScmCostAttr(tcProc, tcProd, tcSent, ioRand, ioSeq, probes);
sprintf(line,
"TC_PROC: %g TC_PROD: %g TC_SENT: %g IO_SEQ: %g IO_RAND: %g ",
tcProc, tcProd, tcSent, ioSeq, ioRand);
NABoolean scmDebugOn = (CmpCommon::getDefault(NCM_PRINT_ROWSIZE) == DF_ON);
if (scmDebugOn == TRUE)
{
double input0RowSize, input1RowSize, outputRowSize, probeRowSize;
char rowsizeInfo[400];
getScmDebugAttr(input0RowSize, input1RowSize, outputRowSize, probeRowSize);
sprintf(rowsizeInfo,
" I0RS: %g I1RS: %g O_RS: %g P_RS: %g",
input0RowSize, input1RowSize, outputRowSize, probeRowSize);
// Does strcat truncate??
strcat(line, rowsizeInfo);
}
}
}
dtlDesc = line;
return dtlDesc;
} // Cost::getDetailDesc()
//<pb>
// -----------------------------------------------------------------------
// This method returns cost information to WMS (and possibly other callers).
// -----------------------------------------------------------------------
void Cost::getExternalCostAttr(double &cpuTime, double &ioTime,
double &msgTime, double &idleTime,
double &numSeqIOs, double &numRandIOs,
double &totalTime, Lng32 &probes) const
{
// Depending on the cost model in effect get the detailed description for
// the corresponding cost vector
if (CmpCommon::getDefault(SIMPLE_COST_MODEL) == DF_ON)
{
double tcProc, tcProd, tcSent;
getScmCostAttr(tcProc, tcProd, tcSent, numRandIOs, numSeqIOs, probes);
const double cpu_ff = CURRSTMT_OPTDEFAULTS->getTimePerCPUInstructions();
const double randIO_ff = CURRSTMT_OPTDEFAULTS->getTimePerSeek();
const double seqIO_ff = CURRSTMT_OPTDEFAULTS->getTimePerSeqKb();
double randIOTime, seqIOTime;
double blockSizeInKB = 32.0; // better way to get this from CQD?
// compute cpu resources: total rows * copy cost per row
cpuTime = (tcProc + tcProd) *
CostPrimitives::getBasicCostFactor(CPUCOST_COPY_ROW_PER_BYTE);
// convert resources to time
cpuTime *= cpu_ff;
// compute IO times
randIOTime = randIO_ff * numRandIOs;
seqIOTime = seqIO_ff * numSeqIOs * blockSizeInKB;
// compute msg resources
msgTime = tcSent *
CostPrimitives::getBasicCostFactor(CPUCOST_EXCHANGE_COST_PER_BYTE);
// convert msg resources to time
msgTime *= cpu_ff;
// final tuning if needed.
double mapCpuFactor =
ActiveSchemaDB()->getDefaults().getAsDouble(NCM_MAP_CPU_FACTOR);
double mapRandIoFacor =
ActiveSchemaDB()->getDefaults().getAsDouble(NCM_MAP_RANDIO_FACTOR);
double seqIOFactor =
ActiveSchemaDB()->getDefaults().getAsDouble(NCM_MAP_SEQIO_FACTOR);
double mapMsgFactor =
ActiveSchemaDB()->getDefaults().getAsDouble(NCM_MAP_MSG_FACTOR);
cpuTime *= mapCpuFactor;
randIOTime *= mapRandIoFacor;
seqIOTime *= seqIOFactor;
msgTime *= mapMsgFactor;
ioTime = randIOTime + seqIOTime;
totalTime = cpuTime + ioTime + msgTime;
idleTime = 0;
}
else
{
getOcmCostAttr(cpuTime, ioTime, msgTime, idleTime, probes);
totalTime = MINOF(convertToElapsedTime(), 1e32).getValue();
numSeqIOs = numRandIOs = 0;
}
} // Cost::getExternalCostAttr()
// -----------------------------------------------------------------------
// The cost detail description consists of the individual components of the
// simple cost vector depending on the cost model in effect. This is expected
// to be used by callers like Explain etc. to display the cost details.
// -----------------------------------------------------------------------
void Cost::getOcmCostAttr(double &cpu, double &io,
double &msg, double &idleTime,
Lng32 &probes) const
{
cpu = MINOF(cplr_.getCPUTime(), 1e32).getValue()
+ MINOF(cpbcTotal_.getCPUTime(), 1e32).getValue();
io = MINOF(cplr_.getIOTime(), 1e32).getValue()
+ MINOF(cpbcTotal_.getIOTime(), 1e32).getValue();
msg = cplr_.getMessageTime().getValue()
+ cpbcTotal_.getMessageTime().getValue();
idleTime = MAXOF( cplr_.getIdleTime().getValue(),
cpbcTotal_.getIdleTime().getValue());
CostScalar tmpProbe = cplr_.getNumProbes();
probes = Lng32(tmpProbe.getValue());
}
// -----------------------------------------------------------------------
// The cost detail description consists of the individual components of the
// simple cost vector depending on the cost model in effect. This is expected
// to be used by callers like Explain etc. to display the cost details.
// -----------------------------------------------------------------------
// called only when internal debugging CQD NCM_PRINT_ROWSIZE is ON.
// So hide from coverage
void Cost::getScmCostAttr(double &tcProc, double &tcProd,
double &tcSent, double &ioRand,
double &ioSeq, Lng32 &probes) const
{
tcProc = cpScmlr_.getTcProc().getValue();
tcProd = cpScmlr_.getTcProd().getValue();
tcSent = cpScmlr_.getTcSent().getValue();
ioRand = cpScmlr_.getIoRand().getValue();
ioSeq = cpScmlr_.getIoSeq().getValue();
CostScalar tmpProbe = cpScmlr_.getNumProbes();
probes = Lng32(tmpProbe.getValue());
}
// -----------------------------------------------------------------------
// The cost detail description consists of the individual components of the
// simple cost vector depending on the cost model in effect. This is expected
// to be used by callers like Explain etc. to display the cost details.
// -----------------------------------------------------------------------
void Cost::getScmDebugAttr(double &dbg1, double &dbg2,
double &dbg3, double &dbg4) const
{
dbg1 = cpScmDbg_.getTcProc().getValue();
dbg2 = cpScmDbg_.getTcProd().getValue();
dbg3 = cpScmDbg_.getTcSent().getValue();
dbg4 = cpScmDbg_.getIoRand().getValue();
}
//<pb>
// -----------------------------------------------------------------------
// Compares two Cost objects in the simple cost model
// returns MORE if this > other, SAME if they are equal, and LESS otherwise.
// rpp is unused for now, but may be used later if we add different goals to the simple cost model.
// -----------------------------------------------------------------------
COMPARE_RESULT Cost::scmCompareCosts(
const Cost & other,
const ReqdPhysicalProperty* const rpp) const
{
// Plans with equal priorities proceed to real cost comparison.
// We are optimizing based on the total cost represented by the cost objects.
CostScalar cs1 = scmComputeTotalCost() * priority_.riskPremium();
CostScalar cs2 = other.scmComputeTotalCost() * other.priority_.riskPremium();
if (cs1 > cs2)
return MORE;
else if (cs1 < cs2)
return LESS;
else
return SAME;
} // Cost::scmCompareCosts()
//<pb>
// -----------------------------------------------------------------------
// The total cost represented by the cpScmlr SimpleCostVector of "this" Cost object
// is the sum of tuples processed, tuples produced, tuples sent,
// and weighted sequential IOs and random IOs.
// rpp is unused for now.
// -----------------------------------------------------------------------
CostScalar Cost::scmComputeTotalCost(
const ReqdPhysicalProperty* const rpp) const
{
return cpScmlr_.getElapsedTime();
} // Cost::scmComputeTotalCost()
//<pb>
//==============================================================================
// Use this Cost object to produce a corresponding CostLimit object using
// specified required physical properties for elapsed time conversions.
//
// Input:
// rpp -- specified required physical properties.
//
// Output:
// none
//
// Return:
// Pointer to newly created CostLimit object.
//
//==============================================================================
CostLimit*
Cost::convertToCostLimit(const ReqdPhysicalProperty* const rpp) const
{
// Fix for coverity cid #1511 : pointer pfg is not used.
// const PerformanceGoal* pfg;
// pfg = (rpp ? rpp->getPerformanceGoal() : DefaultPerformanceGoal);
//----------------------------------------------------------------------
// If optimized for resource consumption, the set of cost weights
// specified in rpp are used. CostLimit is computed based on lr_ and blocking
// cost for resource consumption as well.
//----------------------------------------------------------------------
/* if(pfg->isOptimizeForResourceConsumption())
{
const CostWeight* rcw;
rcw = (rpp ? rpp->getCostWeight() : DefaultCostWeight);
return rcw->convertToCostLimit(totalCost_);
}*/
//----------------------------------------------------------------------
// Else, we are optimizing for elapsed time (either FR or LR). The new
// CostLimit contains an upper limit equal to the elapsed time of this
// cost object. The CostLimit's accumulated ancestor and sibling costs
// are initially set to be empty.
//----------------------------------------------------------------------
ElapsedTime et (convertToElapsedTime(rpp));
Cost emptyAncestorCost;
Cost emptySiblingCost;
if (CmpCommon::getDefault(SIMPLE_COST_MODEL) == DF_ON)
{
return new(CmpCommon::statementHeap())
ScmElapsedTimeCostLimit(et, getPlanPriority(),
&emptyAncestorCost, &emptySiblingCost);
}
else
{
return new(CmpCommon::statementHeap())
ElapsedTimeCostLimit(et, getPlanPriority(),
&emptyAncestorCost, &emptySiblingCost);
}
} // Cost::convertToCostLimit()
//<pb>
// -----------------------------------------------------------------------
// Some operators on all component vectors. Calls the same overloaded
// operation for all the vectors. Make sure you know what you are doing
// when you use them. Really doubtful whether they should be supported at
// all.
// -----------------------------------------------------------------------
Cost & Cost::operator += (const Cost &other)
{
// ---------------------------------------------------------------------
// Take special care when using this function in the new version Cost
// object. This method simply adds the current process vectors up using
// the vector addition provided by CostVector. When combining cost
// objects, the contexts (such as the operators to which cost objects
// belong) under which they are combined should be considered and we
// should have operator specific implementations.
// ---------------------------------------------------------------------
totalCost_ += other.totalCost_;
cplr_ += other.cplr_;
cpScmlr_ += other.cpScmlr_;
cpfr_ += other.cpfr_;
cpbc1_ += other.cpbc1_;
cpbcTotal_ += other.cpbcTotal_;
//jo opfr_ += other.opfr_;
//jo oplr_ += other.oplr_;
priority_ += other.priority_;
return *this;
}
//<pb>
//==============================================================================
// Merge a sibling cost vector with this cost vector.
//
// Input:
// otherChildCost -- specified other child cost object.
//
// Output:
// none
//
// Return:
// This cost object after being merged with its sibling.
//
//==============================================================================
void
Cost::mergeOtherChildCost(const Cost& otherChildCost)
{
// merge sibling cost for NCM
if (CmpCommon::getDefault(SIMPLE_COST_MODEL) == DF_ON)
cpScmlr_ += otherChildCost.cpScmlr_;
else
{
//---------------------------------------------------------------------------
// Add corresponding vectors of a new style cost object using overlapped
// addition (except for total cost which always uses simple vector addition).
// Remember to normalize other blocking vector's to the number of probes
// for this cost object's blocking vectors.
//---------------------------------------------------------------------------
totalCost_ += otherChildCost.totalCost_;
cplr_.overlappedAdd(otherChildCost.cplr_);
cpfr_.overlappedAdd(otherChildCost.cpfr_);
CostScalar numProbes = cpbc1_.getNumProbes();
cpbc1_.overlappedAdd(otherChildCost.cpbc1_.getNormalizedVersion(numProbes));
numProbes = cpbcTotal_.getNumProbes();
cpbcTotal_.overlappedAdd(
otherChildCost.cpbcTotal_.getNormalizedVersion(numProbes));
//jo opfr_.overlappedAdd(otherChildCost.opfr_);
//jo oplr_.overlappedAdd(otherChildCost.oplr_);
}
priority_.combine(otherChildCost.priority_);
} // Cost::mergeOtherChildCost
//<pb>
//<pb>
//==============================================================================
// Default implementation to produce a priority for an entire subtree
//
// Output:
// Plan Priority
//
// Input:
// op -- specified physical operator.
//
// myContext -- context associated with specified physical operator
//
// childCost -- cost of operator child subtree
//========================================================================
// For use with leaf operators
PlanPriority
Cost::computePlanPriority( RelExpr* op,
const Context* myContext)
{
priority_ = op->computeOperatorPriority(myContext);
return priority_;
}
// For use with unary operators
PlanPriority
Cost::computePlanPriority( RelExpr* op,
const Context* myContext,
const Cost* childCost)
{
priority_ = op->computeOperatorPriority(myContext);
priority_.rollUpUnary(childCost->getPlanPriority());
return priority_;
}
// For use with binary operators
PlanPriority
Cost::computePlanPriority( RelExpr* op,
const Context* myContext,
const Cost* child0Cost,
const Cost* child1Cost,
PlanWorkSpace *pws,
Lng32 planNumber)
{
priority_ = op->computeOperatorPriority(myContext, pws, planNumber);
priority_.rollUpBinary(child0Cost->getPlanPriority(),child1Cost->getPlanPriority());
return priority_;
}
// excluded for coverage because it's a debug code
void Cost::print(FILE * pfp, const char * , const char *) const
{
if (CmpCommon::getDefault(SIMPLE_COST_MODEL) == DF_ON)
{
fprintf(pfp,"SCM Last Row Cost information\n");
cpScmlr_.print(pfp);
}
else
{
fprintf(pfp,"First Row Cost information\n");
cpfr_.print(pfp);
fprintf(pfp,"Last Row Cost information\n");
cplr_.print(pfp);
fprintf(pfp,"Blocking Cost information\n");
cpbc1_.print(pfp);
}
fprintf(pfp,"preference : %d\n", priority_.getLevel()); //sathya
return;
}
void Cost::display() const
{
print();
}
//<pb>
//==============================================================================
// HashJoinCost Constructor.
//
// Input:
// currentProcessFirstRowCost -- resources necessary to produce operator's
// first row once blocking activity completes.
//
// currentProcessLastRowCost -- resources necessary to produce operator's
// last row once blocking activity completes.
//
// currentProcessBlockingCost -- resources necessary for blocking activity.
//
// countOfCPUs -- number of CPUs for executing this hash join.
//
// planFragmentsPerCPU -- number of plan fragments per CPU.
//
// stage2WorkFractionForFR -- percentage of stage 2 work necessary to
// produce hash join's first row.
//
// stage3WorkFractionForFR -- percentage of stage 3 work necessary to
// produce hash join's first row.
//
// stage2Cost -- resources necessary for stage 2 activity.
//
// stage3Cost -- resources necessary for stage 3 activity.
//
//
// Output:
// none
//
// Return:
// none.
//
//==============================================================================
HashJoinCost::HashJoinCost(const SimpleCostVector* currentProcessFirstRowCost,
const SimpleCostVector* currentProcessLastRowCost,
const SimpleCostVector* currentProcessBlockingCost,
const Lng32 countOfCPUs,
const Lng32 planFragmentsPerCPU,
const CostScalar & stage2WorkFractionForFR,
const CostScalar & stage3WorkFractionForFR,
const SimpleCostVector* stage2Cost,
const SimpleCostVector* stage3Cost,
const SimpleCostVector* stage1BkCost,
const SimpleCostVector* stage2BkCost,
const SimpleCostVector* stage3BkCost)
: Cost(currentProcessFirstRowCost,
currentProcessLastRowCost,
currentProcessBlockingCost,
countOfCPUs,
planFragmentsPerCPU),
stage2WorkFractionForFR_(stage2WorkFractionForFR),
stage3WorkFractionForFR_(stage3WorkFractionForFR),
stage2Cost_(*stage2Cost),
stage3Cost_(*stage3Cost),
stage1BkCost_(),
stage2BkCost_(),
stage3BkCost_()
{
if (stage1BkCost != NULL)
stage1BkCost_ = *stage1BkCost;
if (stage2BkCost != NULL)
stage2BkCost_ = *stage2BkCost;
if (stage3BkCost != NULL)
stage3BkCost_ = *stage3BkCost;
//----------------------------------------------------------------------------
// Ensure that both stage two and stage three work fractions for first row
// are between zero and 1. Also, ensure that the stage two work fraction
// for first row is greater than or equal to the stage 3 work fraction for
// first row.
//----------------------------------------------------------------------------
CMPASSERT( stage2WorkFractionForFR.isGreaterOrEqualThanZero() /* >= csZero */ );
CMPASSERT( NOT stage2WorkFractionForFR.isGreaterThanOne() /* <= csOne */ );
CMPASSERT( stage3WorkFractionForFR.isGreaterOrEqualThanZero() /* >= csZero */ );
CMPASSERT( stage2WorkFractionForFR >= stage3WorkFractionForFR );
//-------------------------------------------------------------------------
// Since cost vectors supplied to this constructor are on a "per stream"
// basis, we convert them to a "per CPU" basis. We use overlapped addition
// because independent streams can overlap IO and message costs.
//-------------------------------------------------------------------------
for (Lng32 fragIdx = 1; fragIdx < planFragmentsPerCPU; fragIdx++)
{
stage2Cost_.overlappedAdd(*stage2Cost);
stage3Cost_.overlappedAdd(*stage3Cost);
if (stage1BkCost != NULL)
stage1BkCost_.overlappedAdd(*stage1BkCost);
if (stage2BkCost != NULL)
stage2BkCost_.overlappedAdd(*stage2BkCost);
if (stage3BkCost != NULL)
stage3BkCost_.overlappedAdd(*stage3BkCost);
}
} // HashJoinCost constructor.
//<pb>
//==============================================================================
// Effectively a virtual constructor for a hash join cost object.
//
// Input:
// none
//
// Output:
// none
//
// Return:
// none.
//
//==============================================================================
Cost*
HashJoinCost::duplicate()
{
Cost* costPtr = new (CmpCommon::statementHeap()) HashJoinCost(*this);
return costPtr;
} // HashJoinCost::duplicate()
//<pb>
//==============================================================================
// HashJoinCost destructor.
//
// Input:
// none
//
// Output:
// none
//
// Return:
// none.
//
//==============================================================================
HashJoinCost::~HashJoinCost()
{
} // HashJoinCost destructor.
//<pb>
//==============================================================================
// HashGroupByCost Constructor.
//
// Input:
// currentProcessFirstRowCost -- resources necessary to produce operator's
// first row once blocking activity completes.
//
// currentProcessLastRowCost -- resources necessary to produce operator's
// last row once blocking activity completes.
//
// currentProcessBlockingCost -- resources necessary for blocking activity.
//
// countOfCPUs -- number of CPUs for executing this hash join.
//
// planFragmentsPerCPU -- number of plan fragments per CPU.
//
// groupingFactor -- percentage of groups which fit in memory.
//
// Output:
// none
//
// Return:
// none.
//
//==============================================================================
HashGroupByCost::HashGroupByCost(
const SimpleCostVector* currentProcessFirstRowCost,
const SimpleCostVector* currentProcessLastRowCost,
const SimpleCostVector* currentProcessBlockingCost,
const Lng32 countOfCPUs,
const Lng32 planFragmentsPerCPU,
const CostScalar & groupingFactor)
: Cost(currentProcessFirstRowCost,
currentProcessLastRowCost,
currentProcessBlockingCost,
countOfCPUs,
planFragmentsPerCPU),
groupingFactor_(groupingFactor)
{
//--------------------------------------------------------------
// Ensure that grouping factor is between zero and 1 inclusive.
//--------------------------------------------------------------
CMPASSERT( groupingFactor.isGreaterOrEqualThanZero() /* >= csZero */ );
CMPASSERT( NOT groupingFactor.isGreaterThanOne() /* <= csOne */ );
} // HashGroupByCost constructor.
//<pb>
//==============================================================================
// Effectively a virtual constructor for a Hash GroupBy cost object.
//
// Input:
// none
//
// Output:
// none
//
// Return:
// none.
//
//==============================================================================
Cost*
HashGroupByCost::duplicate()
{
Cost* costPtr = new (CmpCommon::statementHeap()) HashGroupByCost(*this);
return costPtr;
} // HashGroupByCost::duplicate()
//<pb>
//==============================================================================
// HashGroupByCost destructor.
//
// Input:
// none
//
// Output:
// none
//
// Return:
// none.
//
//==============================================================================
HashGroupByCost::~HashGroupByCost()
{
} // HashGroupByCost destructor.
//<pb>
// ***********************************************************************
// -----------------------------------------------------------------------
// methods for class CostWeight
// -----------------------------------------------------------------------
ResourceConsumptionWeight* CostWeight::castToResourceConsumptionWeight() const
{
return NULL; // sorry, wrong number
}
//<pb>
// ***********************************************************************
// -----------------------------------------------------------------------
// methods for class ResourceConsumptionWeight
// -----------------------------------------------------------------------
// the old constructor is kept for reference
/*ResourceConsumptionWeight::ResourceConsumptionWeight(
const CostScalar & cpuWeight,
const CostScalar & numSeeksWeight,
const CostScalar & numKBytesWeight,
const CostScalar & normalMemoryWeight,
const CostScalar & persistentMemoryWeight,
const CostScalar & numLocalMessagesWeight,
const CostScalar & kbLocalMessagesWeight,
const CostScalar & numRemoteMessagesWeight,
const CostScalar & kbRemoteMessagesWeight,
const CostScalar & diskUsageWeight,
const CostScalar & idleTimeWeight,
const CostScalar & numProbesWeight)
{
weighFactors_[CPU_TIME] = cpuWeight;
weighFactors_[IO_TIME] = MAXOF(numSeeksWeight, numKBytesWeight);
weighFactors_[MSG_TIME] =
MAXOF(
MAXOF(numLocalMessagesWeight, kbLocalMessagesWeight),
MAXOF(numRemoteMessagesWeight, kbRemoteMessagesWeight)
);
weighFactors_[IDLE_TIME] = idleTimeWeight;
weighFactors_[TC_PROC] = csZero;
weighFactors_[TC_PROD] = csZero;
weighFactors_[TC_SENT] = csZero;
weighFactors_[IO_RAND] = csZero;
weighFactors_[IO_SEQ] = csZero;
weighFactors_[NUM_PROBES] = numProbesWeight;
} // ResourceConsumptionWeight::ResourceConsumptionWeight
*/
ResourceConsumptionWeight::ResourceConsumptionWeight(
const CostScalar & cpuWeight,
const CostScalar & IOWeight,
const CostScalar & MsgWeight,
const CostScalar & idleTimeWeight,
const CostScalar & numProbesWeight)
{
weighFactors_[CPU_TIME] = cpuWeight;
weighFactors_[IO_TIME] = IOWeight;
weighFactors_[MSG_TIME] = MsgWeight;
weighFactors_[IDLE_TIME] = idleTimeWeight;
weighFactors_[TC_PROC] = csZero;
weighFactors_[TC_PROD] = csZero;
weighFactors_[TC_SENT] = csZero;
weighFactors_[IO_RAND] = csZero;
weighFactors_[IO_SEQ] = csZero;
weighFactors_[NUM_PROBES] = numProbesWeight;
} // ResourceConsumptionWeight::ResourceConsumptionWeight
//<pb>
// -----------------------------------------------------------------------
// Type-safe pointer cast
// -----------------------------------------------------------------------
ResourceConsumptionWeight*
ResourceConsumptionWeight::castToResourceConsumptionWeight() const
{
return (ResourceConsumptionWeight*)this;
}
// -----------------------------------------------------------------------
// ResourceConsumptionWeight::entries()
// -----------------------------------------------------------------------
Lng32 ResourceConsumptionWeight::entries() const
{
return COUNT_OF_SIMPLE_COST_COUNTERS;
} // ResourceConsumptionWeight::entries()
// -----------------------------------------------------------------------
// ResourceConsumptionWeight::convertToElapsedTime()
// -----------------------------------------------------------------------
ElapsedTime ResourceConsumptionWeight::convertToElapsedTime
(const CostVector& cv) const
{
CMPASSERT(cv.entries() == entries());
ElapsedTime result( csZero );
for (Lng32 i = 0; i < entries(); i++)
result += cv[i] * weighFactors_[i];
return result;
}; // ResourceConsumptionWeight::convertToElapsedTime()
// -----------------------------------------------------------------------
// ResourceConsumptionWeight::convertToFloatingPointValue()
// -----------------------------------------------------------------------
double ResourceConsumptionWeight::convertToFloatingPointValue
(const CostVector& cv) const
{
return convertToElapsedTime(cv).value();
}; // ResourceConsumptionWeight::convertToFloatingPointValue()
//<pb>
//==============================================================================
// Use this ResourceConsumptionWeight object to produce a corresponding
// CostLimit object using a specified cost vector for elapsed time calculations.
//
// Input:
// cv -- specified Cost vector.
//
// Output:
// none
//
// Return:
// Pointer to newly created CostLimit object.
//
//==============================================================================
CostLimit*
ResourceConsumptionWeight::convertToCostLimit (const CostVector& cv) const
{
//----------------------------------------------------------------------
// The new CostLimit contains an upper limit equal to the elapsed time
// of the specified cost vector. The CostLimit's accumulated ancestor
// and sibling costs are initially set to be empty.
//----------------------------------------------------------------------
Cost emptyAncestorCost;
Cost emptySiblingCost;
PlanPriority* normalPriority = new(CmpCommon::statementHeap()) PlanPriority();
// coverity flags this as resource leak, since we cleanup
// statement heap later we fix it here using annotations.
// coverity[leaked_storage]
return new(CmpCommon::statementHeap())
ElapsedTimeCostLimit(convertToElapsedTime(cv),
*normalPriority,
&emptyAncestorCost,
&emptySiblingCost);
}; // ResourceConsumptionWeight::convertToCostLimit()
//<pb>
// -----------------------------------------------------------------------
// ResourceConsumptionWeight::compareCostVectors()
// -----------------------------------------------------------------------
COMPARE_RESULT ResourceConsumptionWeight::compareCostVectors
(const CostVector& cv1,
const CostVector& cv2) const
{
ElapsedTime abs1(convertToElapsedTime(cv1));
ElapsedTime abs2(convertToElapsedTime(cv2));
if ( abs1 == abs2 )
return SAME;
else if ( abs1 < abs2 )
return LESS;
else
return MORE;
}; // ResourceConsumptionWeight::compareCostVectors()
//<pb>
// Provides access to a specific entry
CostScalar ResourceConsumptionWeight::operator[] (Lng32 ix) const
{
CMPASSERT((ix >= 0) AND (ix < COUNT_OF_SIMPLE_COST_COUNTERS));
return weighFactors_[ix];
}
// ***********************************************************************
// -----------------------------------------------------------------------
// methods for class CostLimit
// -----------------------------------------------------------------------
ElapsedTimeCostLimit* CostLimit::castToElapsedTimeCostLimit() const
{
return NULL; // sorry, wrong number
}
//<pb>
// ***********************************************************************
// -----------------------------------------------------------------------
// methods for class ElpasedTimeCostLimit
// -----------------------------------------------------------------------
ElapsedTimeCostLimit* ElapsedTimeCostLimit::castToElapsedTimeCostLimit() const
{
return (ElapsedTimeCostLimit*)this;
}
//<pb>
//==============================================================================
// Convert this cost limit to a single value which represents the elapsed time
// remaining after the elapsed time of the associated Cost is subtracted from
// the initial elapsed time limit. Use specified required physical properties
// for elapsed time conversion.
//
// Input:
// rpp -- specified required physical properties.
//
// Output:
// none
//
// Return:
// remaining elapsed time in the form of a double.
//
//==============================================================================
double
ElapsedTimeCostLimit::getValue(const ReqdPhysicalProperty* const rpp) const
{
//-------------------------------------------------------------------------
// Roll up accumulated sibling cost into accumulated ancestor cost using
// the most conservative (i.e. non-blocking) unary roll-up strategy.
//-------------------------------------------------------------------------
Cost* rollUpCost = rollUpUnaryNonBlocking(*ancestorCost_,
*otherKinCost_,
rpp);
double newLimitVal = rollUpCost->convertToElapsedTime(rpp).value();
delete rollUpCost;
//--------------------------------------------------------------------
// Remaining elapsed time is upper limit less the calculated limit of
// ancestors and siblings.
//--------------------------------------------------------------------
return upperLimit_.value() - newLimitVal;
} // ElapsedTimeCostLimit::getValue
//<pb>
//==============================================================================
// Compare this cost limit with a specified cost limit using specified
// required physical properties for any elapsed time conversions. Comparison
// is based on associated value of each cost limit.
//
// Input:
// other -- specified cost limit with which to compare.
//
// rpp -- specified required physical properties.
//
// Output:
// none
//
// Return:
// Result of comparison.
//
//==============================================================================
COMPARE_RESULT
ElapsedTimeCostLimit::compareCostLimits
(CostLimit* other,
const ReqdPhysicalProperty* const rpp)
{
// other == NULL means infinite time, thus return LESS in this case:
if (other==NULL)
{
return LESS;
}
else
{
CMPASSERT(other->castToElapsedTimeCostLimit());
// Higher priority plans always win the comparison and considered "cheaper"
// Similarly a costLimit with higher priority limit should be considered
// a stronger (i.e. lower) limit than one with low priority
if (CmpCommon::getDefault(COMP_BOOL_193) == DF_OFF)
{
if (priorityLimit_ > other->priorityLimit())
return LESS;
if (priorityLimit_ < other->priorityLimit())
return MORE;
}
// try to use cached values if possible
double otherValue = other->getCachedValue();
if ( otherValue == 0.0 OR
NOT CURRSTMT_OPTDEFAULTS->OPHuseCachedElapsedTime())
{
// This shouldn't happen, just in case.
otherValue = other->getValue(rpp) *
other->priorityLimit().riskPremium().value();
other->setCachedValue(otherValue);
}
if ( cachedValue_ == 0.0 OR
NOT CURRSTMT_OPTDEFAULTS->OPHuseCachedElapsedTime())
{
// This may happen if the current cost limit gets modified
cachedValue_ = getValue(rpp) * priorityLimit().riskPremium().value();
}
if (cachedValue_ > otherValue)
{
return MORE;
}
else
{
if (cachedValue_ < otherValue)
{
return LESS;
}
else
{
return SAME;
}
}
} // other != NULL
} // ElapsedTimeCostLimit::compareCostLimits()
//<pb>
//==============================================================================
// Compare this CostLimit with a Cost object by first converting the Cost
// object to a CostLimit and then comparing the two CostLimit objects using
// specified required physical properties for elapsed time calculations.
//
// Input:
// other -- cost object with which to compare.
//
// rpp -- specified required physical properties.
//
// Output:
// none
//
// Return:
// Result of comparison.
//
//==============================================================================
COMPARE_RESULT
ElapsedTimeCostLimit::compareWithCost (const Cost & other,
const ReqdPhysicalProperty* const rpp)
{
// Higher priority plans always win the comparison and considered "cheaper"
// Similarly a costLimit with higher priority limit should be considered
// a stronger (i.e. lower) limit than one with low priority
if (CmpCommon::getDefault(COMP_BOOL_193) == DF_OFF)
{
// xxx We only consider the demotion levels of priority limit for this type
// of cost based pruning. This is because demotions levels are monotonically
// decreasing with upper limit of zero.
Lng32 demotionLimit = priorityLimit_.getDemotionLevel()
- ancestorCost_->getPlanPriority().getDemotionLevel()
- otherKinCost_->getPlanPriority().getDemotionLevel();
if (demotionLimit > other.getPlanPriority().getDemotionLevel())
return LESS;
if (demotionLimit < other.getPlanPriority().getDemotionLevel())
return MORE;
/*
PlanPriority tmpPriorityLimit =
priorityLimit_ - (ancestorCost_->getPlanPriority() + otherKinCost_->getPlanPriority());
if (tmpPriorityLimit > other.getPlanPriority())
return LESS;
if (tmpPriorityLimit < other.getPlanPriority())
return MORE;
*/
}
// Trying to be less aggressive. First, roll-up other cost into cost limit
// then call getValue() if result is negative - return LESS and so on.
// for conservative CL use CQD OPH_USE_CONSERVATIVE_COST_LIMIT 'ON';
// Note that here we cannot use cachedValue.
if ( CURRSTMT_OPTDEFAULTS->OPHuseConservativeCL() )
{
CostLimit * costLimit = copy();
costLimit->ancestorAccum(other, rpp);
double diff = costLimit->getValue(rpp);
// do we want to delete temporary costLimit object here?
// delete costLimit;
if ( diff < -0.000001 )
return LESS;
else if ( diff > 0.000001 )
return MORE;
else return SAME;
}
//---------------------------------------------------------------------------
// Determine remaining time of cost limit and elapsed time of specified cost.
//---------------------------------------------------------------------------
if (cachedValue_ == 0.0 OR
NOT CURRSTMT_OPTDEFAULTS->OPHuseCachedElapsedTime())
cachedValue_ = getValue(rpp) * priorityLimit().riskPremium().value();
double costValue = other.convertToElapsedTime(rpp).value() *
((Cost&)other).planPriority().riskPremium().value();
if ( CURRSTMT_OPTDEFAULTS->OPHuseCompCostThreshold() )
{
double ratio = costValue/cachedValue_;
if ( ratio < 0.999999 )
return MORE;
else if (ratio > 1.000001)
return LESS;
else
return SAME;
}
//---------------------------------------------------------------------------
// Compare remaining time of cost limit with elapsed time of specified cost.
//---------------------------------------------------------------------------
if (cachedValue_ == costValue )
{
return SAME;
}
else
{
if (cachedValue_ < costValue )
{
return LESS;
}
else
{
return MORE;
}
}
}
COMPARE_RESULT
ElapsedTimeCostLimit::compareWithPlanCost (CascadesPlan* plan,
const ReqdPhysicalProperty* const rpp)
{
// Cannot use cached elapsed time if
// CQD OPH_USE_CONSERVATIVE_COST_LIMIT 'ON' need to roll up plan cost
// into cost limit in this case
if ( CURRSTMT_OPTDEFAULTS->OPHuseConservativeCL() )
return compareWithCost(*plan->getRollUpCost(),rpp);
// Higher priority plans always win the comparison and considered "cheaper"
// Similarly a costLimit with higher priority limit should be considered
// a stronger (i.e. lower) limit than one with low priority
if (CmpCommon::getDefault(COMP_BOOL_193) == DF_OFF)
{
// xxx We only consider the demotion levels of priority limit for this type
// of cost based pruning. This is because demotions levels are monotonically
// decreasing with upper limit of zero.
Lng32 demotionLimit = priorityLimit_.getDemotionLevel()
- ancestorCost_->getPlanPriority().getDemotionLevel()
- otherKinCost_->getPlanPriority().getDemotionLevel();
if (demotionLimit > plan->getRollUpCost()->getPlanPriority().getDemotionLevel())
return LESS;
if (demotionLimit < plan->getRollUpCost()->getPlanPriority().getDemotionLevel())
return MORE;
/*
PlanPriority tmpPriorityLimit =
priorityLimit_ - (ancestorCost_->getPlanPriority() + otherKinCost_->getPlanPriority());
if (tmpPriorityLimit > plan->getRollUpCost()->getPlanPriority())
return LESS;
if (tmpPriorityLimit < plan->getRollUpCost()->getPlanPriority())
return MORE;
*/
}
ElapsedTime planElapsedTime = plan->getPlanElapsedTime();
if ( planElapsedTime.isZero() OR
NOT CURRSTMT_OPTDEFAULTS->OPHuseCachedElapsedTime())
{
// Using this plan for comparison for the first time. Compute
// plan elapsed time and save in plan
planElapsedTime = plan->getRollUpCost()->convertToElapsedTime(rpp);
plan->setPlanElapsedTime(planElapsedTime);
}
// check and if necessary compute cachedValue
if ( cachedValue_ == 0.0 OR
NOT CURRSTMT_OPTDEFAULTS->OPHuseCachedElapsedTime())
cachedValue_ = getValue(rpp);
//---------------------------------------------------------------------------
// Determine remaining time of cost limit and elapsed time of specified cost.
//---------------------------------------------------------------------------
double costValue = planElapsedTime.value();
if ( CURRSTMT_OPTDEFAULTS->OPHuseCompCostThreshold() )
{
double ratio = costValue/cachedValue_;
if ( ratio < 0.999999 )
return MORE;
else if (ratio > 1.000001)
return LESS;
else
return SAME;
}
//---------------------------------------------------------------------------
// Compare remaining time of cost limit with elapsed time of specified cost.
//---------------------------------------------------------------------------
if (cachedValue_ == costValue )
{
return SAME;
}
else
{
if (cachedValue_ < costValue )
{
return LESS;
}
else
{
return MORE;
}
}
}
//<pb>
//<pb>
//==============================================================================
// Accumulate a specified cost into this cost limit's ancestor costs using
// specified required physical properties.
//
// Input:
// otherCost -- specified Cost object.
//
// rpp -- specified required physical properties.
//
// Output:
// none
//
// Return:
// none
//
//==============================================================================
void
ElapsedTimeCostLimit::ancestorAccum(const Cost& otherCost,
const ReqdPhysicalProperty* const rpp)
{
//---------------------------------------------------
// Extract number of probes for later normalization.
//---------------------------------------------------
const CostScalar ancestorProbes = ancestorCost_->getCplr().getNumProbes();
const CostScalar otherNumProbes = otherCost.getCplr().getNumProbes();
//--------------------------------------------------------------------------
// This temporary cost object will ultimately contain the newly accumulated
// cost. Create it initially empty.
//--------------------------------------------------------------------------
Cost* tempCost = new (CmpCommon::statementHeap()) Cost();
//<pb>
//---------------------------------------------------------------------------
// Accumulation follows the same basic strategy as cost roll-up. We can
// encounter four cases:
//
// 1) Neither specified cost nor cost accumulated so far is blocking.
// 2) Specified cost is non-blocking but cost accumulated so far is blocking.
// 3) Specified cost is blocking but cost accumulated so far is non-blocking.
// 4) Both specified cost and cost accumulated so far are blocking.
//---------------------------------------------------------------------------
if ( otherCost.getCpbcTotal().isZeroVectorWithProbes() )
{
if ( ancestorCost_->getCpbcTotal().isZeroVectorWithProbes() )
{
//--------------------------------------------------------------------
// Case 1.
//
// Since neither cost is blocking, we need not concern ourselves with
// blocking vectors. The first row and last row vectors are added as
// in non-blocking unary roll-up.
//--------------------------------------------------------------------
tempCost->cpfr() = blockingAdd( ancestorCost_->getCpfr(),
otherCost.getCpfr(),
rpp );
tempCost->cplr() = overlapAdd( ancestorCost_->getCplr(),
otherCost.getCplr()
- otherCost.getCpfr()
)
+ otherCost.getCpfr();
}
else
{
//<pb>
//--------------------------------------------------------------------
// Case 2.
//
// Since the accumulated cost is blocking, its first and last row
// vectors do not change. The specified cost's last row vector gets
// added (after being converted to an average cost) to the accumulated
// cost's blocking vectors using overlapped addition similar to
// blocking unary roll-up.
//--------------------------------------------------------------------
tempCost->cpfr() = ancestorCost_->getCpfr();
tempCost->cplr() = ancestorCost_->getCplr();
tempCost->cpbc1() =
overlapAdd( ancestorCost_->getCpbc1(),
otherCost.getCplr() / otherNumProbes);
tempCost->cpbcTotal() =
overlapAdd( ancestorCost_->getCpbcTotal(),
otherCost.getCplr() / otherNumProbes);
}
}
else
{
if ( ancestorCost_->getCpbcTotal().isZeroVectorWithProbes() )
{
//-------------------------------------------------------------------
// Case 3.
//
// Since accumulated cost is non-blocking, we simply add first and
// last row cost vectors as in case 1 above, and the blocking vectors
// come directly from the specified cost object (which is blocking).
// These blocking vectors are normalized to the number of probes of
// the accumulated cost.
//-------------------------------------------------------------------
tempCost->cpfr() = blockingAdd( ancestorCost_->getCpfr(),
otherCost.getCpfr(),
rpp );
tempCost->cplr() = overlapAdd( ancestorCost_->getCplr(),
otherCost.getCplr()
- otherCost.getCpfr() )
+ otherCost.getCpfr();
tempCost->cpbc1() =
otherCost.getCpbc1().getNormalizedVersion(ancestorProbes);
tempCost->cpbcTotal() =
otherCost.getCpbcTotal().getNormalizedVersion(ancestorProbes);
}
else
{
//<pb>
//------------------------------------------------------------------
// Case 4.
//
// Since the accumulated cost is blocking, the first and last row
// cost vectors come directly from the accumulated cost object as in
// case 2 above.
//
// Since the specified cost is blocking, the first blocking
// cost vector comes directly from the specified cost. It is
// normalized to the number of probes of the accumulated cost.
//
// The final total blocking cost vector needs to include the total
// blocking cost of both the specified cost and the accumulated cost
// as well as the last row cost vector (converted to an average cost
// vector) of the specified cost. The total blocking vector of the
// specified cost must be converted to the number of probes of the
// accumulated cost.
//------------------------------------------------------------------
tempCost->cpfr() = ancestorCost_->getCpfr();
tempCost->cplr() = ancestorCost_->getCplr();
tempCost->cpbc1() =
otherCost.getCpbc1().getNormalizedVersion(ancestorProbes);
tempCost->cpbcTotal() =
blockingAdd(
overlapAdd(ancestorCost_->getCpbcTotal(),
otherCost.getCplr() / otherNumProbes),
otherCost.getCpbcTotal().getNormalizedVersion(ancestorProbes),
rpp
);
}
}
//---------------------------------------------------------------------------
// As in standard roll-up, total cost accumulation always uses simple vector
// addition.
//---------------------------------------------------------------------------
tempCost->totalCost() = ancestorCost_->getTotalCost() + otherCost.getTotalCost();
//---------------------------------------------------------------------------
// As in standard roll-up, priority will be combined. I rather use the mmethod
// combine() here over the + operator, but its OK now since they do the same thing
// and we have no reverse method for combine()
//---------------------------------------------------------------------------
tempCost->planPriority() = ancestorCost_->getPlanPriority() + otherCost.getPlanPriority();
//-------------------------------------------------------------------------
// Delete old accumulated cost and replace it with newly accumulated cost.
//-------------------------------------------------------------------------
delete ancestorCost_;
ancestorCost_ = tempCost;
// invalidate cachedValue;
cachedValue_ = 0.0;
} //ElapsedTimeCostLimit::ancestorAccum()
//<pb>
//==============================================================================
// Accumulate a specified cost into this cost limit's other kin cost.
//
// Input:
// otherCost -- specified Cost object.
//
// Output:
// none
//
// Return:
// none
//
//==============================================================================
void
ElapsedTimeCostLimit::otherKinAccum(const Cost& otherCost)
{
otherKinCost_->mergeOtherChildCost(otherCost);
// Note: no need to accumilate priority of otherKins here since
// the mergeOtherChildCost method does that already
// invalidate cachedValue;
cachedValue_ = 0.0;
} // ElapsedTimeCostLimit::otherKinAccum()
//<pb>
//==============================================================================
// Potentially reduce upper limit of this CostLimit based on the cost of a
// more promising plan. Use specified required physical properties for
// elapsed time conversions.
//
// Input:
// otherCost -- specified Cost object associated with more promising plan.
//
// rpp -- specified required physical properties.
//
// Output:
// none
//
// Return:
// none
//
//==============================================================================
void
ElapsedTimeCostLimit::tryToReduce(const Cost& otherCost,
const ReqdPhysicalProperty* const rpp)
{
//-------------------------------------------------------------------------
// Calculate new potential priority limit as the sum of the priorities of
// the new plan and the siblings and ancestors.
//-------------------------------------------------------------------------
NABoolean priorityLimitIncreased = FALSE;
PlanPriority newPriorityLimit =
otherCost.getPlanPriority() +
ancestorCost_->getPlanPriority() +
otherKinCost_->getPlanPriority();
if (newPriorityLimit > priorityLimit_ AND // we have higher priority option (winner)
CmpCommon::getDefault(COMP_BOOL_193) == DF_OFF)
{
priorityLimit_ = newPriorityLimit;
priorityLimitIncreased = TRUE;
}
//-------------------------------------------------------------------------
// Calculate new potential upper limit as the sum of the elapsed time of
// the cost of the new plan and the elapsed time of the combined costs of
// siblings and ancestors accumulated so far.
//-------------------------------------------------------------------------
double newLimitVal = otherCost.convertToElapsedTime(rpp).value()
+ upperLimit_.value()
- getValue(rpp);
//------------------------------------------------------------------------
// If newly calculated limit is smaller than old upper limit, make it the
// official upper limit.
//------------------------------------------------------------------------
if (newLimitVal < upperLimit_.value() OR
priorityLimitIncreased) // need to compute upperLimit based on new winner
{
upperLimit_ = ElapsedTime(newLimitVal);
}
// invalidate cachedValue;
cachedValue_ = 0.0;
} //ElapsedTimeCostLimit::tryToReduce()
//<pb>
//==============================================================================
// Unilaterally reduce upper limit by a specified elapsed time without going
// below zero.
//
// Input:
// timeReduction -- specified elapsed time reduction.
//
// Output:
// none
//
// Return:
// none
//
//==============================================================================
void
ElapsedTimeCostLimit::unilaterallyReduce(const ElapsedTime & timeReduction)
{
// This method is used for changing elapsed time limit for child of blocking
// binary operators. It should not change priority limit
upperLimit_ -= timeReduction;
//if ( upperLimit_.getValue() < csZero.getValue() )
// {
// upperLimit_ = csZero;
// }
upperLimit_.minCsZero();
// invalidate cachedValue;
cachedValue_ = 0.0;
} //ElapsedTimeCostLimit::unilaterallyReduce()
//<pb>
// ***********************************************************************
// methods for class ElpasedTimeCostLimit
// -----------------------------------------------------------------------
//ScmElapsedTimeCostLimit*
// ScmElapsedTimeCostLimit::castToElapsedTimeCostLimit() const
//{
// return (ScmElapsedTimeCostLimit*)this;
//}
//=============================================================================
// Convert this cost limit to a single value which represents the elapsed time
// remaining after the elapsed time of the associated Cost is subtracted from
// the initial elapsed time limit. Use specified required physical properties
// for elapsed time conversion.
//
// Input:
// rpp -- specified required physical properties.
//
// Output:
// none
//
// Return:
// remaining elapsed time in the form of a double.
//
//=============================================================================
double
ScmElapsedTimeCostLimit::getValue(const ReqdPhysicalProperty* const rpp) const
{
//-----------------------
// Create an empty cost.
//-----------------------
Cost* rollUp = new STMTHEAP Cost();
//-------------------------------------------------------------------------
// Roll up accumulated sibling cost into accumulated ancestor cost
//-------------------------------------------------------------------------
rollUp->cpScmlr() = ancestorCost_->getScmCplr() + otherKinCost_->getScmCplr();
CostScalar ncmCostLimitFF =
ActiveSchemaDB()->getDefaults().getAsDouble(NCM_COSTLIMIT_FACTOR);
rollUp->cpScmlr() = rollUp->cpScmlr().scaleByValue(ncmCostLimitFF);
double newLimitVal = rollUp->convertToElapsedTime(rpp).value();
delete rollUp;
//--------------------------------------------------------------------
// Remaining elapsed time is upper limit less the calculated limit of
// ancestors and siblings.
//--------------------------------------------------------------------
return upperLimit_.value() - newLimitVal;
} // ElapsedTimeCostLimit::getValue
//============================================================================
// Compare this cost limit with a specified cost limit using specified
// required physical properties for any elapsed time conversions. Comparison
// is based on associated value of each cost limit.
//
// Input:
// other -- specified cost limit with which to compare.
//
// rpp -- specified required physical properties.
//
// Output:
// none
//
// Return:
// Result of comparison.
//
//============================================================================
COMPARE_RESULT
ScmElapsedTimeCostLimit::compareCostLimits
(CostLimit* other,
const ReqdPhysicalProperty* const rpp)
{
// other == NULL means infinite time, thus return LESS in this case:
if (other==NULL)
{
return LESS;
}
else
{
// CMPASSERT(other->castToScmElapsedTimeCostLimit());
// Higher priority plans always win the comparison and considered "cheaper"
// Similarly a costLimit with higher priority limit should be considered
// a stronger (i.e. lower) limit than one with low priority
if (CmpCommon::getDefault(COMP_BOOL_193) == DF_OFF)
{
if (priorityLimit_ > other->priorityLimit())
return LESS;
if (priorityLimit_ < other->priorityLimit())
return MORE;
}
// get my value and other value.
double myValue = getValue(rpp) * priorityLimit().riskPremium().value();
double otherValue = other->getValue(rpp) *
other->priorityLimit().riskPremium().value();
if (myValue > otherValue)
{
return MORE;
}
else
{
if (myValue < otherValue)
{
return LESS;
}
else
{
return SAME;
}
}
} // other != NULL
} // ScmElapsedTimeCostLimit::compareCostLimits()
//=============================================================================
// Compare this CostLimit with a Cost object by first converting the Cost
// object to a CostLimit and then comparing the two CostLimit objects using
// specified required physical properties for elapsed time calculations.
//
// Input:
// other -- cost object with which to compare.
//
// rpp -- specified required physical properties.
//
// Output:
// none
//
// Return:
// Result of comparison.
//
//=============================================================================
COMPARE_RESULT
ScmElapsedTimeCostLimit::compareWithCost (const Cost & other,
const ReqdPhysicalProperty* const rpp)
{
// Higher priority plans always win the comparison and considered "cheaper"
// Similarly a costLimit with higher priority limit should be considered
// a stronger (i.e. lower) limit than one with low priority
if (CmpCommon::getDefault(COMP_BOOL_193) == DF_OFF)
{
// xxx We only consider the demotion levels of priority limit for this type
// of cost based pruning. This is because demotions levels are monotonically
// decreasing with upper limit of zero.
Lng32 demotionLimit = priorityLimit_.getDemotionLevel()
- ancestorCost_->getPlanPriority().getDemotionLevel()
- otherKinCost_->getPlanPriority().getDemotionLevel();
if (demotionLimit > other.getPlanPriority().getDemotionLevel())
return LESS;
if (demotionLimit < other.getPlanPriority().getDemotionLevel())
return MORE;
}
// Trying to be less aggressive. First, roll-up other cost into cost limit
// then call getValue() if result is negative - return LESS and so on.
// for conservative CL use CQD OPH_USE_CONSERVATIVE_COST_LIMIT 'ON';
// Note that here we cannot use cachedValue.
if ( CURRSTMT_OPTDEFAULTS->OPHuseConservativeCL() )
{
CostLimit * costLimit = copy();
costLimit->ancestorAccum(other, rpp);
double diff = costLimit->getValue(rpp);
delete costLimit;
if ( diff < -0.000001 )
return LESS;
else if ( diff > 0.000001 )
return MORE;
else return SAME;
}
return compareCostLimits(other.convertToCostLimit(rpp), rpp);
}
COMPARE_RESULT
ScmElapsedTimeCostLimit::compareWithPlanCost (CascadesPlan* plan,
const ReqdPhysicalProperty* const rpp)
{
return compareWithCost(*plan->getRollUpCost(),rpp);
}
//============================================================================
// Accumulate a specified cost into this cost limit's ancestor costs using
// specified required physical properties.
//
// Input:
// otherCost -- specified Cost object.
//
// rpp -- specified required physical properties.
//
// Output:
// none
//
// Return:
// none
//
//============================================================================
void
ScmElapsedTimeCostLimit::ancestorAccum(const Cost& otherCost,
const ReqdPhysicalProperty* const rpp)
{
//--------------------------------------------------------------------------
// This temporary cost object will ultimately contain the newly accumulated
// cost. Create it initially empty.
//--------------------------------------------------------------------------
Cost* tempCost = new (CmpCommon::statementHeap()) Cost();
tempCost->cpScmlr() = ancestorCost_->getScmCplr() + otherCost.getScmCplr();
//-----------------------------------------------------
// As in standard roll-up, priority will be combined.
tempCost->planPriority() = ancestorCost_->getPlanPriority() +
otherCost.getPlanPriority();
//-------------------------------------------------------------------------
// Delete old accumulated cost and replace it with newly accumulated cost.
//-------------------------------------------------------------------------
delete ancestorCost_;
ancestorCost_ = tempCost;
} //ScmElapsedTimeCostLimit::ancestorAccum()
//=========================================================================
// Accumulate a specified cost into this cost limit's other kin cost.
//
// Input:
// otherCost -- specified Cost object.
//
// Output:
// none
//
// Return:
// none
//
//=========================================================================
void
ScmElapsedTimeCostLimit::otherKinAccum(const Cost& otherCost)
{
//--------------------------------------------------------------------------
// This temporary cost object will ultimately contain the newly accumulated
// cost. Create it initially empty.
//--------------------------------------------------------------------------
Cost* tempCost = new (CmpCommon::statementHeap()) Cost();
tempCost->cpScmlr() = otherKinCost_->getScmCplr() + otherCost.getScmCplr();
//-----------------------------------------------------
// As in standard roll-up, priority will be combined.
tempCost->planPriority() = otherKinCost_->getPlanPriority() +
otherCost.getPlanPriority();
//-------------------------------------------------------------------------
// Delete old accumulated cost and replace it with newly accumulated cost.
//-------------------------------------------------------------------------
delete otherKinCost_;
otherKinCost_ = tempCost;
} // ScmElapsedTimeCostLimit::otherKinAccum()
//===========================================================================
// Potentially reduce upper limit of this CostLimit based on the cost of a
// more promising plan. Use specified required physical properties for
// elapsed time conversions.
//
// Input:
// otherCost -- specified Cost object associated with more promising plan.
//
// rpp -- specified required physical properties.
//
// Output:
// none
//
// Return:
// none
//
//===========================================================================
void
ScmElapsedTimeCostLimit::tryToReduce(const Cost& otherCost,
const ReqdPhysicalProperty* const rpp)
{
//-------------------------------------------------------------------------
// Calculate new potential priority limit as the sum of the priorities of
// the new plan and the siblings and ancestors.
//-------------------------------------------------------------------------
NABoolean priorityLimitIncreased = FALSE;
PlanPriority newPriorityLimit =
otherCost.getPlanPriority() +
ancestorCost_->getPlanPriority() +
otherKinCost_->getPlanPriority();
if (newPriorityLimit > priorityLimit_ AND // we have higher priority option
CmpCommon::getDefault(COMP_BOOL_193) == DF_OFF)
{
priorityLimit_ = newPriorityLimit;
priorityLimitIncreased = TRUE;
}
//-------------------------------------------------------------------------
// Calculate new potential upper limit as the sum of the elapsed time of
// the cost of the new plan and the elapsed time of the combined costs of
// siblings and ancestors accumulated so far.
//-------------------------------------------------------------------------
double newLimitVal;
if (ActiveSchemaDB()->getDefaults().getAsLong(COMP_INT_95) == 0)
{
newLimitVal = otherCost.convertToElapsedTime(rpp).value()
+ ancestorCost_->convertToElapsedTime(rpp).value()
+ otherKinCost_->convertToElapsedTime(rpp).value();
}
else
{
newLimitVal = otherCost.convertToElapsedTime(rpp).value()
+ ancestorCost_->convertToElapsedTime(rpp).value()
+ otherKinCost_->convertToElapsedTime(rpp).value();
}
//------------------------------------------------------------------------
// If newly calculated limit is smaller than old upper limit, make it the
// official upper limit.
//------------------------------------------------------------------------
if (newLimitVal < upperLimit_.value() OR
priorityLimitIncreased) // need to compute upperLimit based on new winner
{
upperLimit_ = ElapsedTime(newLimitVal);
}
// invalidate cachedValue;
cachedValue_ = 0.0;
} //ScmElapsedTimeCostLimit::tryToReduce()
//============================================================================
// Unilaterally reduce upper limit by a specified elapsed time without going
// below zero.
//
// Input:
// timeReduction -- specified elapsed time reduction.
//
// Output:
// none
//
// Return:
// none
//
//============================================================================
void
ScmElapsedTimeCostLimit::unilaterallyReduce(const ElapsedTime & timeReduction)
{
// This method is used for changing elapsed time limit for child of blocking
// binary operators. It should not change priority limit
upperLimit_ -= timeReduction;
upperLimit_.minCsZero();
// invalidate cachedValue;
cachedValue_ = 0.0;
} //ElapsedTimeCostLimit::unilaterallyReduce()
// ---------------------------------------------------------------------------
// methods for class CostPrimitives
// ---------------------------------------------------------------------------
double CostPrimitives::cpuCostForCopySet(const ValueIdSet & vidset)
{
return
(getBasicCostFactor(CPUCOST_COPY_SIMPLE_DATA_TYPE) * vidset.entries());
}
double CostPrimitives::cpuCostForCopyRow(Lng32 byteCount)
{
return (getBasicCostFactor(CPUCOST_COPY_ROW_OVERHEAD) +
getBasicCostFactor(CPUCOST_COPY_ROW_PER_BYTE) * byteCount);
}
double CostPrimitives::cpuCostForCompare(const ValueIdSet & vidset)
{
const double entries = vidset.entries();
if (entries != 0.0 )
{
const double compareCost =
(getBasicCostFactor(CPUCOST_COMPARE_SIMPLE_DATA_TYPE) * entries);
const double length = (vidset.isEmpty() ? 0 : vidset.getRowLength());
const double extraLength = MAXOF(8.0, length / entries / 2.0);
return (compareCost + compareCost * ((extraLength - 8.0) / 8.0));
}
else
{
return 0.0;
}
}
double CostPrimitives::cpuCostForLikeCompare(const ValueId & vid)
{
CMPASSERT(vid.getItemExpr()->getOperatorType() == ITM_LIKE);
ItemExpr * child0 = vid.getItemExpr()->getChild(0)->castToItemExpr();
Lng32 child0length = child0->getValueId().getType().getNominalSize();
return (getBasicCostFactor(CPUCOST_LIKE_COMPARE_OVERHEAD) +
getBasicCostFactor(CPUCOST_LIKE_COMPARE_PER_BYTE) * child0length);
}
double CostPrimitives::cpuCostForEvalArithExpr(const ValueId & vid)
{
CMPASSERT(vid.getItemExpr() != NULL); // Just to suppress warnings.
return getBasicCostFactor(CPUCOST_EVAL_ARITH_OP);
}
double CostPrimitives::cpuCostForEvalFunc(const ValueId & vid)
{
return getBasicCostFactor(CPUCOST_EVAL_FUNC_DEFAULT);
}
//<pb>
double CostPrimitives::cpuCostForEvalPred(const ValueIdSet & vidset)
{
return
(getBasicCostFactor(CPUCOST_EVAL_SIMPLE_PREDICATE) * vidset.entries());
}
double CostPrimitives::cpuCostForEvalExpr(const ValueId & vid)
{
return getBasicCostFactor(CPUCOST_EVAL_ARITH_OP);
}
double CostPrimitives::cpuCostForEncode(const ValueIdSet & vidset)
{
return
(getBasicCostFactor(CPUCOST_ENCODE_PER_BYTE) * vidset.getRowLength());
}
double CostPrimitives::cpuCostForHash(const ValueIdSet & vidset)
{
return (getBasicCostFactor(CPUCOST_HASH_PER_KEY) * vidset.entries() +
getBasicCostFactor(CPUCOST_HASH_PER_BYTE) * vidset.getRowLength());
}
double CostPrimitives::cpuCostForAggrRow(const ValueIdSet & vidset)
{
return (getBasicCostFactor(CPUCOST_EVAL_ARITH_OP) * vidset.entries());
}
//<pb>
// The parameter mscf is one of the factors listed in the table
// defaultDefaults in sqlcomp/NADefaults.C; getBasicCostFactor returns
// the associated value listed in that table. To add a factor, include
// it in sqlcomp/DefaultConstants.h and include that file where the
// factor will be used. Also, list the factor and its value in the
// defaultDefaults table.
double CostPrimitives::getBasicCostFactor(Lng32 id)
{
if (id == MSCF_OV_MSG ||
id == MSCF_OV_IO)
return 1.0;
return CmpCommon::getDefaultNumeric((DefaultConstants)id);
}
// eof