+
+ memset(&instr, 0, sizeof(instr));
+ /*
+ * We know that samples will actually be taken up to SAMPLE_THRESHOLD,
+ * so that's as far as we can test.
+ */
+ for (j=0; j < SAMPLE_THRESHOLD; j++)
{
- InstrStartNode( &instr );
- InstrStopNode( &instr, 1 );
+ InstrStartNode(&instr);
+ InstrStopNode(&instr, 1);
}
overhead = INSTR_TIME_GET_DOUBLE(instr.counter) / instr.samplecount;
- if( overhead < SampleOverhead )
+ if (overhead < SampleOverhead)
SampleOverhead = overhead;
}
-
+
SampleOverheadCalculated = true;
}
+
/* Allocate new instrumentation structure(s) */
Instrumentation *
InstrAlloc(int n)
{
- Instrumentation *instr = palloc0(n * sizeof(Instrumentation));
+ Instrumentation *instr;
- /* we don't need to do any initialization except zero 'em */
-
- /* Calculate overhead, if not done yet */
- if( !SampleOverheadCalculated )
+ /* Calculate sampling overhead, if not done yet in this backend */
+ if (!SampleOverheadCalculated)
CalculateSampleOverhead();
+
+ instr = palloc0(n * sizeof(Instrumentation));
+
+ /* we don't need to do any initialization except zero 'em */
+
return instr;
}
{
if (INSTR_TIME_IS_ZERO(instr->starttime))
{
- /* We always sample the first SAMPLE_THRESHOLD tuples, so small nodes are always accurate */
- if (instr->tuplecount < SAMPLE_THRESHOLD)
+ /*
+ * Always sample if not yet up to threshold, else check whether
+ * next threshold has been reached
+ */
+ if (instr->itercount < SAMPLE_THRESHOLD)
instr->sampling = true;
- else
+ else if (instr->itercount >= instr->nextsample)
{
- /* Otherwise we go to sampling, see the comments on SampleFunc at the top of the file */
- if( instr->tuplecount > instr->nextsample )
- {
- instr->sampling = true;
- /* The doubling is so the random will average 1 over time */
- instr->nextsample += 2.0 * SampleFunc(instr->tuplecount) * (double)rand() / (double)RAND_MAX;
- }
+ instr->sampling = true;
+ instr->nextsample =
+ instr->itercount + SampleInterval(instr->itercount);
}
if (instr->sampling)
INSTR_TIME_SET_CURRENT(instr->starttime);
void
InstrStopNode(Instrumentation *instr, double nTuples)
{
- instr_time endtime;
-
- /* count the returned tuples */
+ /* count the returned tuples and iterations */
instr->tuplecount += nTuples;
+ instr->itercount += 1;
+ /* measure runtime if appropriate */
if (instr->sampling)
{
+ instr_time endtime;
+
if (INSTR_TIME_IS_ZERO(instr->starttime))
{
elog(DEBUG2, "InstrStopNode called without start");
#endif
INSTR_TIME_SET_ZERO(instr->starttime);
- instr->samplecount += nTuples;
+
+ instr->samplecount += 1;
instr->sampling = false;
}
if (!INSTR_TIME_IS_ZERO(instr->starttime))
elog(DEBUG2, "InstrEndLoop called on running node");
- /* Accumulate per-cycle statistics into totals */
+ /* Compute time spent in node */
totaltime = INSTR_TIME_GET_DOUBLE(instr->counter);
- instr->startup += instr->firsttuple;
-
- /* Here we take into account sampling effects. Doing it naively ends
- * up assuming the sampling overhead applies to all tuples, even the
- * ones we didn't measure. We've calculated an overhead, so we
- * subtract that for all samples we didn't measure. The first tuple
- * is also special cased, because it usually takes longer. */
-
- if( instr->samplecount < instr->tuplecount )
+ /*
+ * If we didn't measure runtime on every iteration, then we must increase
+ * the measured total to account for the other iterations. Naively
+ * multiplying totaltime by itercount/samplecount would be wrong because
+ * it effectively assumes the sampling overhead applies to all iterations,
+ * even the ones we didn't measure. (Note that what we are trying to
+ * estimate here is the actual time spent in the node, including the
+ * actual measurement overhead; not the time exclusive of measurement
+ * overhead.) We exclude the first iteration from the correction basis,
+ * because it often takes longer than others.
+ */
+ if (instr->itercount > instr->samplecount)
{
- double pertuple = (totaltime - instr->firsttuple) / (instr->samplecount - 1);
- instr->total += instr->firsttuple + (pertuple * (instr->samplecount - 1))
- + ((pertuple - SampleOverhead) * (instr->tuplecount - instr->samplecount));
+ double per_iter;
+
+ per_iter = (totaltime - instr->firsttuple) / (instr->samplecount - 1)
+ - SampleOverhead;
+ if (per_iter > 0) /* sanity check */
+ totaltime += per_iter * (instr->itercount - instr->samplecount);
}
- else
- instr->total += totaltime;
-
+
+ /* Accumulate per-cycle statistics into totals */
+ instr->startup += instr->firsttuple;
+ instr->total += totaltime;
instr->ntuples += instr->tuplecount;
- instr->nsamples += instr->samplecount;
instr->nloops += 1;
/* Reset for next cycle (if any) */
instr->running = false;
+ instr->sampling = false;
INSTR_TIME_SET_ZERO(instr->starttime);
INSTR_TIME_SET_ZERO(instr->counter);
instr->firsttuple = 0;
- instr->samplecount = 0;
instr->tuplecount = 0;
+ instr->itercount = 0;
+ instr->samplecount = 0;
+ instr->nextsample = 0;
}