Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0aa38db

Browse files
committed
Optimise numeric division for 3 and 4 base-NBASE digit divisors.
On platforms with 128-bit integer support, introduce a new functiondiv_var_int64(), along the same lines as div_var_int() added ind1b307e for divisors with 1 or 2 base-NBASE digits, and use it tospeed up div_var() and div_var_fast() in a similar way when thedivisor has 3 or 4 base-NBASE digits.This gives significant performance gains for divisors with 9-16decimal digits.Joel Jacobson.Discussion:https://postgr.es/m/b7a5893d-af18-4c0b-8918-96de5f1bbf39%40app.fastmail.comhttps://postgr.es/m/CAEZATCXGm%3DDyTq%3DFrcOqC0gPMVveKUYTaD5KRRoajrUTiWxVMw%40mail.gmail.com
1 parent009dbde commit0aa38db

File tree

1 file changed

+167
-0
lines changed

1 file changed

+167
-0
lines changed

‎src/backend/utils/adt/numeric.c

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,10 @@ static void div_var_fast(const NumericVar *var1, const NumericVar *var2,
554554
NumericVar*result,intrscale,boolround);
555555
staticvoiddiv_var_int(constNumericVar*var,intival,intival_weight,
556556
NumericVar*result,intrscale,boolround);
557+
#ifdefHAVE_INT128
558+
staticvoiddiv_var_int64(constNumericVar*var,int64ival,intival_weight,
559+
NumericVar*result,intrscale,boolround);
560+
#endif
557561
staticintselect_div_scale(constNumericVar*var1,constNumericVar*var2);
558562
staticvoidmod_var(constNumericVar*var1,constNumericVar*var2,
559563
NumericVar*result);
@@ -8484,6 +8488,9 @@ div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
84848488
/*
84858489
* If the divisor has just one or two digits, delegate to div_var_int(),
84868490
* which uses fast short division.
8491+
*
8492+
* Similarly, on platforms with 128-bit integer support, delegate to
8493+
* div_var_int64() for divisors with three or four digits.
84878494
*/
84888495
if (var2ndigits <=2)
84898496
{
@@ -8503,6 +8510,26 @@ div_var(const NumericVar *var1, const NumericVar *var2, NumericVar *result,
85038510
div_var_int(var1,idivisor,idivisor_weight,result,rscale,round);
85048511
return;
85058512
}
8513+
#ifdefHAVE_INT128
8514+
if (var2ndigits <=4)
8515+
{
8516+
int64idivisor;
8517+
intidivisor_weight;
8518+
8519+
idivisor=var2->digits[0];
8520+
idivisor_weight=var2->weight;
8521+
for (i=1;i<var2ndigits;i++)
8522+
{
8523+
idivisor=idivisor*NBASE+var2->digits[i];
8524+
idivisor_weight--;
8525+
}
8526+
if (var2->sign==NUMERIC_NEG)
8527+
idivisor=-idivisor;
8528+
8529+
div_var_int64(var1,idivisor,idivisor_weight,result,rscale,round);
8530+
return;
8531+
}
8532+
#endif
85068533

85078534
/*
85088535
* Otherwise, perform full long division.
@@ -8774,6 +8801,9 @@ div_var_fast(const NumericVar *var1, const NumericVar *var2,
87748801
/*
87758802
* If the divisor has just one or two digits, delegate to div_var_int(),
87768803
* which uses fast short division.
8804+
*
8805+
* Similarly, on platforms with 128-bit integer support, delegate to
8806+
* div_var_int64() for divisors with three or four digits.
87778807
*/
87788808
if (var2ndigits <=2)
87798809
{
@@ -8793,6 +8823,26 @@ div_var_fast(const NumericVar *var1, const NumericVar *var2,
87938823
div_var_int(var1,idivisor,idivisor_weight,result,rscale,round);
87948824
return;
87958825
}
8826+
#ifdefHAVE_INT128
8827+
if (var2ndigits <=4)
8828+
{
8829+
int64idivisor;
8830+
intidivisor_weight;
8831+
8832+
idivisor=var2->digits[0];
8833+
idivisor_weight=var2->weight;
8834+
for (i=1;i<var2ndigits;i++)
8835+
{
8836+
idivisor=idivisor*NBASE+var2->digits[i];
8837+
idivisor_weight--;
8838+
}
8839+
if (var2->sign==NUMERIC_NEG)
8840+
idivisor=-idivisor;
8841+
8842+
div_var_int64(var1,idivisor,idivisor_weight,result,rscale,round);
8843+
return;
8844+
}
8845+
#endif
87968846

87978847
/*
87988848
* Otherwise, perform full long division.
@@ -9182,6 +9232,123 @@ div_var_int(const NumericVar *var, int ival, int ival_weight,
91829232
}
91839233

91849234

9235+
#ifdefHAVE_INT128
9236+
/*
9237+
* div_var_int64() -
9238+
*
9239+
*Divide a numeric variable by a 64-bit integer with the specified weight.
9240+
*The quotient var / (ival * NBASE^ival_weight) is stored in result.
9241+
*
9242+
*This duplicates the logic in div_var_int(), so any changes made there
9243+
*should be made here too.
9244+
*/
9245+
staticvoid
9246+
div_var_int64(constNumericVar*var,int64ival,intival_weight,
9247+
NumericVar*result,intrscale,boolround)
9248+
{
9249+
NumericDigit*var_digits=var->digits;
9250+
intvar_ndigits=var->ndigits;
9251+
intres_sign;
9252+
intres_weight;
9253+
intres_ndigits;
9254+
NumericDigit*res_buf;
9255+
NumericDigit*res_digits;
9256+
uint64divisor;
9257+
inti;
9258+
9259+
/* Guard against division by zero */
9260+
if (ival==0)
9261+
ereport(ERROR,
9262+
errcode(ERRCODE_DIVISION_BY_ZERO),
9263+
errmsg("division by zero"));
9264+
9265+
/* Result zero check */
9266+
if (var_ndigits==0)
9267+
{
9268+
zero_var(result);
9269+
result->dscale=rscale;
9270+
return;
9271+
}
9272+
9273+
/*
9274+
* Determine the result sign, weight and number of digits to calculate.
9275+
* The weight figured here is correct if the emitted quotient has no
9276+
* leading zero digits; otherwise strip_var() will fix things up.
9277+
*/
9278+
if (var->sign==NUMERIC_POS)
9279+
res_sign=ival>0 ?NUMERIC_POS :NUMERIC_NEG;
9280+
else
9281+
res_sign=ival>0 ?NUMERIC_NEG :NUMERIC_POS;
9282+
res_weight=var->weight-ival_weight;
9283+
/* The number of accurate result digits we need to produce: */
9284+
res_ndigits=res_weight+1+ (rscale+DEC_DIGITS-1) /DEC_DIGITS;
9285+
/* ... but always at least 1 */
9286+
res_ndigits=Max(res_ndigits,1);
9287+
/* If rounding needed, figure one more digit to ensure correct result */
9288+
if (round)
9289+
res_ndigits++;
9290+
9291+
res_buf=digitbuf_alloc(res_ndigits+1);
9292+
res_buf[0]=0;/* spare digit for later rounding */
9293+
res_digits=res_buf+1;
9294+
9295+
/*
9296+
* Now compute the quotient digits. This is the short division algorithm
9297+
* described in Knuth volume 2, section 4.3.1 exercise 16, except that we
9298+
* allow the divisor to exceed the internal base.
9299+
*
9300+
* In this algorithm, the carry from one digit to the next is at most
9301+
* divisor - 1. Therefore, while processing the next digit, carry may
9302+
* become as large as divisor * NBASE - 1, and so it requires a 128-bit
9303+
* integer if this exceeds PG_UINT64_MAX.
9304+
*/
9305+
divisor=i64abs(ival);
9306+
9307+
if (divisor <=PG_UINT64_MAX /NBASE)
9308+
{
9309+
/* carry cannot overflow 64 bits */
9310+
uint64carry=0;
9311+
9312+
for (i=0;i<res_ndigits;i++)
9313+
{
9314+
carry=carry*NBASE+ (i<var_ndigits ?var_digits[i] :0);
9315+
res_digits[i]= (NumericDigit) (carry /divisor);
9316+
carry=carry %divisor;
9317+
}
9318+
}
9319+
else
9320+
{
9321+
/* carry may exceed 64 bits */
9322+
uint128carry=0;
9323+
9324+
for (i=0;i<res_ndigits;i++)
9325+
{
9326+
carry=carry*NBASE+ (i<var_ndigits ?var_digits[i] :0);
9327+
res_digits[i]= (NumericDigit) (carry /divisor);
9328+
carry=carry %divisor;
9329+
}
9330+
}
9331+
9332+
/* Store the quotient in result */
9333+
digitbuf_free(result->buf);
9334+
result->ndigits=res_ndigits;
9335+
result->buf=res_buf;
9336+
result->digits=res_digits;
9337+
result->weight=res_weight;
9338+
result->sign=res_sign;
9339+
9340+
/* Round or truncate to target rscale (and set result->dscale) */
9341+
if (round)
9342+
round_var(result,rscale);
9343+
else
9344+
trunc_var(result,rscale);
9345+
9346+
/* Strip leading/trailing zeroes */
9347+
strip_var(result);
9348+
}
9349+
#endif
9350+
9351+
91859352
/*
91869353
* Default scale selection for division
91879354
*

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp