YoushouldhavereceivedcopiesoftheGNUGeneralPublicLicenseandthe GNULesserGeneralPublicLicensealongwiththeGNUMPLibrary.Ifnot,
see https://www.gnu.org/licenses/. */
struct dat_t {
mp_size_t size; double d;
} *dat = NULL; int ndat = 0; int allocdat = 0;
/* This is not defined if mpn_sqr_basecase doesn't declare a limit. In that
case use zero here, which for params.max_size means no limit. */ #ifndef TUNE_SQR_TOOM2_MAX #define TUNE_SQR_TOOM2_MAX 0 #endif
struct param_t { constchar *name;
speed_function_t function;
speed_function_t function2; double step_factor; /* how much to step relatively */ int step; /* how much to step absolutely */ double function_fudge; /* multiplier for "function" speeds */ int stop_since_change; double stop_factor;
mp_size_t min_size; int min_is_always;
mp_size_t max_size;
mp_size_t check_size;
mp_size_t size_extra;
#define DATA_HIGH_LT_R 1 #define DATA_HIGH_GE_R 2 int data_high;
int noprint;
};
/* These are normally undefined when false, which suits "#if" fine.
But give them zero values so they can be used in plain C "if"s. */ #ifndef UDIV_PREINV_ALWAYS #define UDIV_PREINV_ALWAYS 0 #endif #ifndef HAVE_NATIVE_mpn_divexact_1 #define HAVE_NATIVE_mpn_divexact_1 0 #endif #ifndef HAVE_NATIVE_mpn_div_qr_1n_pi1 #define HAVE_NATIVE_mpn_div_qr_1n_pi1 0 #endif #ifndef HAVE_NATIVE_mpn_divrem_1 #define HAVE_NATIVE_mpn_divrem_1 0 #endif #ifndef HAVE_NATIVE_mpn_divrem_2 #define HAVE_NATIVE_mpn_divrem_2 0 #endif #ifndef HAVE_NATIVE_mpn_mod_1 #define HAVE_NATIVE_mpn_mod_1 0 #endif #ifndef HAVE_NATIVE_mpn_mod_1_1p #define HAVE_NATIVE_mpn_mod_1_1p 0 #endif #ifndef HAVE_NATIVE_mpn_modexact_1_odd #define HAVE_NATIVE_mpn_modexact_1_odd 0 #endif #ifndef HAVE_NATIVE_mpn_preinv_divrem_1 #define HAVE_NATIVE_mpn_preinv_divrem_1 0 #endif #ifndef HAVE_NATIVE_mpn_preinv_mod_1 #define HAVE_NATIVE_mpn_preinv_mod_1 0 #endif #ifndef HAVE_NATIVE_mpn_sqr_basecase #define HAVE_NATIVE_mpn_sqr_basecase 0 #endif
mp_limb_t
randlimb_half (void)
{
mp_limb_t n;
mpn_random (&n, 1);
n &= GMP_NUMB_HALFMASK;
n += (n==0); return n;
}
/* Add an entry to the end of the dat[] array, reallocing to make it bigger
if necessary. */ void
add_dat (mp_size_t size, double d)
{ #define ALLOCDAT_STEP 500
/* Return the threshold size based on the data accumulated. */
mp_size_t
analyze_dat (int final)
{ double x, min_x; int j, min_j;
/* If the threshold is set at dat[0].size, any positive values are bad. */
x = 0.0; for (j = 0; j < ndat; j++) if (dat[j].d > 0.0)
x += dat[j].d;
if (option_trace >= 2 && final)
{
printf ("\n");
printf ("x is the sum of the badness from setting thresh at given size\n");
printf (" (minimum x is sought)\n");
printf ("size=%ld first x=%.4f\n", (long) dat[j].size, x);
}
min_x = x;
min_j = 0;
/* When stepping to the next dat[j].size, positive values are no longer bad(sosubtracted),negativevaluesbecomebad(soaddtheabsolute
value, meaning subtract). */ for (j = 0; j < ndat; x -= dat[j].d, j++)
{ if (option_trace >= 2 && final)
printf ("size=%ld x=%.4f\n", (long) dat[j].size, x);
if (x < min_x)
{
min_x = x;
min_j = j;
}
}
return min_j;
}
/* Measuring for recompiled mpn/generic/div_qr_1.c,
* mpn/generic/divrem_1.c, mpn/generic/mod_1.c and mpz/fac_ui.c */
*threshold = s.size;
t2 = tuneup_measure (param->function2, param, &s); if (t1 == -1.0 || t2 == -1.0)
{
printf ("Oops, can't run both functions at size %ld\n",
(long) s.size);
abort ();
}
t1 *= param->function_fudge;
/* ask that t2 is at least 4% below t1 */ if (t1 < t2*1.04)
{ if (option_trace)
printf ("function2 never enough faster: t1=%.9f t2=%.9f\n", t1, t2);
*threshold = MP_SIZE_T_MAX; if (! param->noprint)
print_define (param->name, *threshold); return;
}
if (option_trace >= 2)
printf ("function2 enough faster at size=%ld: t1=%.9f t2=%.9f\n",
(long) s.size, t1, t2);
}
if (! param->noprint || option_trace)
print_define_start (param->name);
/* using method A at this size */
*threshold = s.size+1;
ti = tuneup_measure (param->function, param, &s); if (ti == -1.0)
abort ();
ti *= param->function_fudge;
/* using method B at this size */
*threshold = s.size;
tiplus1 = tuneup_measure (param->function2, param, &s); if (tiplus1 == -1.0)
abort ();
/* Calculate the fraction by which the one or the other routine is
slower. */ if (tiplus1 >= ti)
d = (tiplus1 - ti) / tiplus1; /* negative */ else
d = (tiplus1 - ti) / ti; /* positive */
/* Stop if the last time method i was faster was more than a
certain number of measurements ago. */ #define STOP_SINCE_POSITIVE 200 if (d >= 0)
since_positive = 0; else if (++since_positive > STOP_SINCE_POSITIVE)
{ if (option_trace >= 1)
printf ("stopped due to since_positive (%d)\n",
STOP_SINCE_POSITIVE); break;
}
/* Stop if method A has become slower by a certain factor. */ if (ti >= tiplus1 * param->stop_factor)
{ if (option_trace >= 1)
printf ("stopped due to ti >= tiplus1 * factor (%.1f)\n",
param->stop_factor); break;
}
/* Stop if the threshold implied hasn't changed in a certain numberofmeasurements.(It'sthisconditionthatusually
stops the loop.) */ if (thresh_idx != new_thresh_idx)
since_thresh_change = 0, thresh_idx = new_thresh_idx; else if (++since_thresh_change > param->stop_since_change)
{ if (option_trace >= 1)
printf ("stopped due to since_thresh_change (%d)\n",
param->stop_since_change); break;
}
/* Stop if the threshold implied is more than a certain number of
measurements ago. */ #define STOP_SINCE_AFTER 500 if (ndat - thresh_idx > STOP_SINCE_AFTER)
{ if (option_trace >= 1)
printf ("stopped due to ndat - thresh_idx > amount (%d)\n",
STOP_SINCE_AFTER); break;
}
/* Stop when the size limit is reached before the end of the crossover,butonlyshowthisasanerrorfor>=thedefaultmax size.FIXME:Maybeshouldmakeitaparamchoicewhetherthisis
an error. */ if (s.size >= param->max_size && param->max_size >= DEFAULT_MAX_SIZE)
{
fprintf (stderr, "%s\n", param->name);
fprintf (stderr, "sizes %ld to %ld total %d measurements\n",
(long) dat[0].size, (long) dat[ndat-1].size, ndat);
fprintf (stderr, " max size reached before end of crossover\n"); break;
}
}
if (option_trace >= 1)
printf ("sizes %ld to %ld total %d measurements\n",
(long) dat[0].size, (long) dat[ndat-1].size, ndat);
*threshold = dat[analyze_dat (1)].size;
if (param->min_is_always)
{ if (*threshold == param->min_size)
*threshold = 0;
}
if (! param->noprint || option_trace)
print_define_end (param->name, *threshold);
}
/* Time N different FUNCTIONS with the same parameters and size, to selectthefastest.Since*_METHODdefinesstartnumberingfrom one,iffunctions[i]isfastest,thevalueofthedefineisi+1. Alsooutputacommentwithspeedupcomparedtothenextfastest function.TheNAMEargumentisusedonlyfortraceoutput.
Returnstheindexofthefastestfunction.
*/ int
one_method (int n, speed_function_t *functions, constchar *name, constchar *define, conststruct param_t *param)
{ double *t; int i; int method; int method_runner_up;
TMP_DECL;
TMP_MARK;
t = (double*) TMP_ALLOC (n * sizeof (*t));
for (i = 0; i < n; i++)
{
t[i] = tuneup_measure (functions[i], param, &s); if (option_trace >= 1)
printf ("size=%ld, %s, method %d %.9f\n",
(long) s.size, name, i + 1, t[i]); if (t[i] == -1.0)
{
printf ("Oops, can't measure all %s methods\n", name);
abort ();
}
}
method = 0; for (i = 1; i < n; i++) if (t[i] < t[method])
method = i;
method_runner_up = (method == 0); for (i = 0; i < n; i++) if (i != method && t[i] < t[method_runner_up])
method_runner_up = i;
Thecurrentapproachistocompareroutinesatthemidpointofrelevant steps.Arguablyamoresophisticatedsystemofthresholddataiswanted
if this step effect remains. */
#if1
{ /* Use plain one() mechanism, for some reasonable initial values of k. The advantageisthatwedon'tdependonmpn_fft_table3,whichcantherefore
leave it completely uninitialized. */
staticstruct param_t param;
mp_size_t thres, best_thres; int best_k; char buf[20];
/* Compare mpn_mul_1 to whatever fast exact single-limb division we have. This iscurrentlympn_divexact_1,butwillbecomempn_bdiv_1_qr_pi2orsomesuch.
This is used in get_str and set_str. */ void
relspeed_div_1_vs_mul_1 (void)
{ const size_t max_opsize = 100;
mp_size_t n; long j;
mp_limb_t rp[max_opsize];
mp_limb_t ap[max_opsize]; double multime, divtime;
mpn_random (ap, max_opsize);
multime = 0; for (n = max_opsize; n > 1; n--)
{
mpn_mul_1 (rp, ap, n, MP_BASES_BIG_BASE_10);
speed_starttime (); for (j = speed_precision; j != 0 ; j--)
mpn_mul_1 (rp, ap, n, MP_BASES_BIG_BASE_10);
multime += speed_endtime () / n;
}
divtime = 0; for (n = max_opsize; n > 1; n--)
{ /* Make input divisible for good measure. */
ap[n - 1] = mpn_mul_1 (ap, ap, n - 1, MP_BASES_BIG_BASE_10);
/* Start karatsuba from 4, since the Cray t90 ieee code is much faster at 2,
giving wrong results. */ void
tune_mul_n (void)
{ staticstruct param_t param;
mp_size_t next_toom_start; int something_changed;
param.function = speed_mpn_mul_n;
param.name = "MUL_TOOM22_THRESHOLD";
param.min_size = MAX (4, MPN_TOOM22_MUL_MINSIZE);
param.max_size = MUL_TOOM22_THRESHOLD_LIMIT-1;
one (&mul_toom22_threshold, ¶m);
param.noprint = 1;
/* Threshold sequence loop. Disable functions that would be used in a very
narrow range, re-measuring things when that happens. */
something_changed = 1; while (something_changed)
{
something_changed = 0;
next_toom_start = mul_toom22_threshold;
if (mul_toom33_threshold != 0)
{
param.name = "MUL_TOOM33_THRESHOLD";
param.min_size = MAX (next_toom_start, MPN_TOOM33_MUL_MINSIZE);
param.max_size = MUL_TOOM33_THRESHOLD_LIMIT-1;
one (&mul_toom33_threshold, ¶m);
/* Use ratio 5/6 when measuring, the middle of the range 2/3 to 1. */
param.function = speed_mpn_toom43_for_toom54_mul;
param.function2 = speed_mpn_toom54_for_toom43_mul;
param.name = "MUL_TOOM43_TO_TOOM54_THRESHOLD";
param.min_size = MPN_TOOM54_MUL_MINSIZE * 6 / 5;
one (&thres, ¶m);
mul_toom43_to_toom54_threshold = thres * 5 / 6;
print_define ("MUL_TOOM43_TO_TOOM54_THRESHOLD", mul_toom43_to_toom54_threshold);
}
/* Start the basecase from 3, since 1 is a special case, and if mul_basecase isfasteronlyatsize==2thenwedon'twanttobotherwithextracode
just for that. Start karatsuba from 4 same as MUL above. */
/* Threshold sequence loop. Disable functions that would be used in a very
narrow range, re-measuring things when that happens. */
something_changed = 1; while (something_changed)
{
something_changed = 0;
next_toom_start = MAX (sqr_toom2_threshold, sqr_basecase_threshold);
sqr_toom3_threshold = SQR_TOOM3_THRESHOLD_LIMIT;
param.name = "SQR_TOOM3_THRESHOLD";
param.min_size = MAX (next_toom_start, MPN_TOOM3_SQR_MINSIZE);
param.max_size = SQR_TOOM3_THRESHOLD_LIMIT-1;
one (&sqr_toom3_threshold, ¶m);
next_toom_start = MAX (next_toom_start, sqr_toom3_threshold);
if (sqr_toom4_threshold != 0)
{
param.name = "SQR_TOOM4_THRESHOLD";
sqr_toom4_threshold = SQR_TOOM4_THRESHOLD_LIMIT;
param.min_size = MAX (next_toom_start, MPN_TOOM4_SQR_MINSIZE);
param.max_size = SQR_TOOM4_THRESHOLD_LIMIT-1;
one (&sqr_toom4_threshold, ¶m);
/* In tune_powm_sec we compute the table used by the win_size function. The cutoffpointsareinexponentbits,disregardingotheroperandsizes.Itis notpossibletousetheoneframeworksinceitcurrentlyusesagranularity offulllimbs.
*/
/* This win_size replaces the variant in the powm code, allowing us to
control k in the k-ary algorithms. */ int winsize; int
win_size (mp_bitcnt_t eb)
{ return winsize;
}
/* For nbits == 1, we should always use k == 1, so no need to tune that.Startingwithnbits==2alsoensurethatnbitsalwaysis
larger than the windowsize k+1. */ for (nbits = 2; nbits <= n_max * GMP_NUMB_BITS; )
{
n = (nbits - 1) / GMP_NUMB_BITS + 1;
/* Generate E such that sliding-window for k and k+1 works equally
well/poorly (but sliding is not used in powm_sec, of course). */ for (i = 0; i < n; i++)
ep[i] = ~CNST_LIMB(0);
winsize = k; for (i = 0; i < n_measurements; i++)
{
speed_starttime ();
mpn_sec_powm (rp, bp, n, ep, nbits, mp, n, tp);
ttab[i] = speed_endtime ();
}
tk = median (ttab, n_measurements);
winsize = k + 1;
speed_starttime (); for (i = 0; i < n_measurements; i++)
{
speed_starttime ();
mpn_sec_powm (rp, bp, n, ep, nbits, mp, n, tp);
ttab[i] = speed_endtime ();
}
tkp1 = median (ttab, n_measurements); /* printf("testing:%ld,%d",nbits,k,ep[n-1]); printf("%10.5f%10.5f\n",tk,tkp1);
*/ if (tkp1 < tk)
{ if (possible_nbits_cutoff)
{ /* Two consecutive sizes indicate k increase, obey. */
/* Must always have x[k] >= k */
ASSERT_ALWAYS (possible_nbits_cutoff >= k);
if (k > 1)
printf (",");
printf ("%ld", (long) possible_nbits_cutoff);
k++;
possible_nbits_cutoff = 0;
} else
{ /* One measurement indicate k increase, save nbits for further
consideration. */ /* The new larger k gets used for sizes > the cutoff value,hencethecutoffshouldbeonelessthanthe
smallest size where it gives a speedup. */
possible_nbits_cutoff = nbits - 1;
}
} else
possible_nbits_cutoff = 0;
/* size_extra==1 reflects the fact that with high<divisor one division is alwaysskipped.Forcinghigh<divisorwhiletestingensuresconsistency whilesteppingthroughsizes,ie.thatsize-1divideswillbedoneeach time.
min_size==2andmin_is_alwaysareusedsothatifplaindivisionisonly betteratsize==1thendon'tbotherincludingthatcodejustforthat
case, instead go with preinv always and get a size saving. */
void
tune_divrem_1 (void)
{ /* plain version by default */
tuned_speed_mpn_divrem_1 = speed_mpn_divrem_1;
/* No support for tuning native assembler code, do that by hand and put theresultsinthe.asmfile,there'snoneedforsuchthresholdsto
appear in gmp-mparam.h. */ if (HAVE_NATIVE_mpn_divrem_1) return;
if (GMP_NAIL_BITS != 0)
{
print_define_remark ("DIVREM_1_NORM_THRESHOLD", MP_SIZE_T_MAX, "no preinv with nails");
print_define_remark ("DIVREM_1_UNNORM_THRESHOLD", MP_SIZE_T_MAX, "no preinv with nails"); return;
}
/* Tune for the integer part of mpn_divrem_1. This will very possibly be abitoutforthefractionalpart,butthat'stoobad,theintegerpart
is more important. */
{ staticstruct param_t param;
param.name = "DIVREM_1_NORM_THRESHOLD";
DIV_1_PARAMS;
s.r = randlimb_norm ();
param.function = speed_mpn_divrem_1_tune;
one (&divrem_1_norm_threshold, ¶m);
}
{ staticstruct param_t param;
param.name = "DIVREM_1_UNNORM_THRESHOLD";
DIV_1_PARAMS;
s.r = randlimb_half ();
param.function = speed_mpn_divrem_1_tune;
one (&divrem_1_unnorm_threshold, ¶m);
}
}
void
tune_mod_1 (void)
{ /* No support for tuning native assembler code, do that by hand and put theresultsinthe.asmfile,there'snoneedforsuchthresholdsto
appear in gmp-mparam.h. */ if (HAVE_NATIVE_mpn_mod_1) return;
if (GMP_NAIL_BITS != 0)
{
print_define_remark ("MOD_1_NORM_THRESHOLD", MP_SIZE_T_MAX, "no preinv with nails");
print_define_remark ("MOD_1_UNNORM_THRESHOLD", MP_SIZE_T_MAX, "no preinv with nails"); return;
}
if (GMP_NAIL_BITS != 0)
{
print_define_remark ("USE_PREINV_DIVREM_1", 0, "no preinv with nails"); return;
}
/* Any native version of mpn_preinv_divrem_1 is assumed to exist because
it's faster than mpn_divrem_1. */ if (HAVE_NATIVE_mpn_preinv_divrem_1)
{
print_define_remark ("USE_PREINV_DIVREM_1", 1, "native"); return;
}
/* If udiv_qrnnd_preinv is the only division method then of course
mpn_preinv_divrem_1 should be used. */ if (UDIV_PREINV_ALWAYS)
{
print_define_remark ("USE_PREINV_DIVREM_1", 1, "preinv always"); return;
}
/* If we've got an assembler version of mpn_divrem_1, then compare against
that, not the mpn_divrem_1_div generic C. */ if (HAVE_NATIVE_mpn_divrem_1)
{
divrem_1 = speed_mpn_divrem_1;
divrem_1_name = "mpn_divrem_1";
} else
{
divrem_1 = speed_mpn_divrem_1_div;
divrem_1_name = "mpn_divrem_1_div";
}
param.data_high = DATA_HIGH_LT_R; /* allow skip one division */
s.size = 200; /* generous but not too big */ /* Divisor, nonzero. Unnormalized so as to exercise the shift!=0 case, sinceingeneralthat'sprobablymostcommon,thoughinfactfora
64-bit limb mp_bases[10].big_base is normalized. */
s.r = urandom() & (GMP_NUMB_MASK >> 4); if (s.r == 0) s.r = 123;
/* No support for tuning native assembler code, do that by hand and put theresultsinthe.asmfile,andthere'snoneedforsuchthresholds
to appear in gmp-mparam.h. */ if (HAVE_NATIVE_mpn_divrem_2) return;
if (GMP_NAIL_BITS != 0)
{
print_define_remark ("DIVREM_2_THRESHOLD", MP_SIZE_T_MAX, "no preinv with nails"); return;
}
if (UDIV_PREINV_ALWAYS)
{
print_define_remark ("DIVREM_2_THRESHOLD", 0L, "preinv always"); return;
}
/* Tune for the integer part of mpn_divrem_2. This will very possibly be abitoutforthefractionalpart,butthat'stoobad,theintegerpart ismoreimportant.
min_sizemustbe>=2sincensize>=2isrequired,butissetto4tosave
code space if plain division is better only at size==2 or size==3. */
param.name = "DIVREM_2_THRESHOLD";
param.check_size = 256;
param.min_size = 4;
param.min_is_always = 1;
param.size_extra = 2; /* does qsize==nsize-2 divisions */
param.stop_factor = 2.0;
/* mpn_divexact_1 is vaguely expected to be used on smallish divisors, so tuneforthat.Itsspeedcandifferonoddorevendivisor,sotakean averagethresholdforthetwo.
mpn_divrem_1canvarywithhigh<divisorornot,whereasmpn_divexact_1 mightnotvarythatway,butdon'ttestthissincehigh<divisorisn't
expected to occur often with small divisors. */
/* Any native mpn_divexact_1 is assumed to incorporate all the speed of a
full mpn_divrem_1. */ if (HAVE_NATIVE_mpn_divexact_1)
{
print_define_remark ("DIVEXACT_1_THRESHOLD", 0, "always (native)"); return;
}
one (&thresh[low], ¶m); if (option_trace)
printf ("low=%d thresh %ld\n", low, (long) thresh[low]);
if (thresh[low] == MP_SIZE_T_MAX)
{
average = MP_SIZE_T_MAX; goto divexact_1_done;
}
}
if (option_trace)
{
printf ("average of:"); for (i = 0; i < numberof(thresh); i++)
printf (" %ld", (long) thresh[i]);
printf ("\n");
}
average = 0; for (i = 0; i < numberof(thresh); i++)
average += thresh[i];
average /= numberof(thresh);
/* If divexact turns out to be better as early as 3 limbs, then use it
always, so as to reduce code size and conditional jumps. */ if (average <= 3)
average = 0;
/* The generic mpn_modexact_1_odd skips a divide step if high<divisor, the sameasmpn_mod_1,butthismightnotbetrueofanassembler implementation.Thethresholdusedisanaveragebasedondatawherea dividecanbeskippedandwhereitcan't.
Ifmodexactturnsouttobebetterasearlyas3limbs,thenuseit
always, so as to reduce code size and conditional jumps. */
#if0 /* Any native mpn_modexact_1_odd is assumed to incorporate all the speed
of a full mpn_mod_1. */ if (HAVE_NATIVE_mpn_modexact_1_odd)
{
print_define_remark ("BMOD_1_TO_MOD_1_THRESHOLD", MP_SIZE_T_MAX, "always bmod_1"); return;
} #endif
time (&end_time);
printf ("/* Tuneup completed successfully, took %ld seconds */\n",
(long) (end_time - start_time));
TMP_FREE;
}
int
main (int argc, char *argv[])
{ int opt;
/* Unbuffered so if output is redirected to a file it isn't lost if the
program is killed part way through. */
setbuf (stdout, NULL);
setbuf (stderr, NULL);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.