/* tune-gcd-p Tune the choice for splitting p in divide-and-conquer gcd. Copyright 2008, 2010, 2011 Free Software Foundation, Inc. This file is part of the GNU MP Library. The GNU MP Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. The GNU MP Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU MP Library. If not, see http://www.gnu.org/licenses/. */ #define TUNE_GCD_P 1 #include "../mpn/gcd.c" #include #include #include #include #include "speed.h" /* Search for minimum over a range. FIXME: Implement golden-section / fibonacci search*/ static int search (double *minp, double (*f)(void *, int), void *ctx, int start, int end) { int x[4]; double y[4]; int best_i; x[0] = start; x[3] = end; y[0] = f(ctx, x[0]); y[3] = f(ctx, x[3]); for (;;) { int i; int length = x[3] - x[0]; x[1] = x[0] + length/3; x[2] = x[0] + 2*length/3; y[1] = f(ctx, x[1]); y[2] = f(ctx, x[2]); #if 0 printf("%d: %f, %d: %f, %d:, %f %d: %f\n", x[0], y[0], x[1], y[1], x[2], y[2], x[3], y[3]); #endif for (best_i = 0, i = 1; i < 4; i++) if (y[i] < y[best_i]) best_i = i; if (length <= 4) break; if (best_i >= 2) { x[0] = x[1]; y[0] = y[1]; } else { x[3] = x[2]; y[3] = y[2]; } } *minp = y[best_i]; return x[best_i]; } static int compare_double(const void *ap, const void *bp) { double a = * (const double *) ap; double b = * (const double *) bp; if (a < b) return -1; else if (a > b) return 1; else return 0; } static double median (double *v, size_t n) { qsort(v, n, sizeof(*v), compare_double); return v[n/2]; } #define TIME(res, code) do { \ double time_measurement[5]; \ unsigned time_i; \ \ for (time_i = 0; time_i < 5; time_i++) \ { \ speed_starttime(); \ code; \ time_measurement[time_i] = speed_endtime(); \ } \ res = median(time_measurement, 5); \ } while (0) struct bench_data { mp_size_t n; mp_ptr ap; mp_ptr bp; mp_ptr up; mp_ptr vp; mp_ptr gp; }; static double bench_gcd (void *ctx, int p) { struct bench_data *data = ctx; double t; p_table[data->n] = p; TIME(t, { MPN_COPY (data->up, data->ap, data->n); MPN_COPY (data->vp, data->bp, data->n); mpn_gcd (data->gp, data->up, data->n, data->vp, data->n); }); return t; } int main(int argc, char **argv) { gmp_randstate_t rands; struct bench_data data; mp_size_t n; TMP_DECL; /* 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); gmp_randinit_default (rands); TMP_MARK; data.ap = TMP_ALLOC_LIMBS (P_TABLE_SIZE); data.bp = TMP_ALLOC_LIMBS (P_TABLE_SIZE); data.up = TMP_ALLOC_LIMBS (P_TABLE_SIZE); data.vp = TMP_ALLOC_LIMBS (P_TABLE_SIZE); data.gp = TMP_ALLOC_LIMBS (P_TABLE_SIZE); mpn_random (data.ap, P_TABLE_SIZE); mpn_random (data.bp, P_TABLE_SIZE); memset (p_table, 0, sizeof(p_table)); for (n = 100; n < P_TABLE_SIZE; n++) { mp_size_t p; mp_size_t best_p; double best_time; double lehmer_time; if (data.ap[n-1] == 0) data.ap[n-1] = 1; if (data.bp[n-1] == 0) data.bp[n-1] = 1; data.n = n; lehmer_time = bench_gcd (&data, 0); best_p = search (&best_time, bench_gcd, &data, n/5, 4*n/5); if (best_time > lehmer_time) best_p = 0; printf("%6d %6d %5.3g", n, best_p, (double) best_p / n); if (best_p > 0) { double speedup = 100 * (lehmer_time - best_time) / lehmer_time; printf(" %5.3g%%", speedup); if (speedup < 1.0) { printf(" (ignored)"); best_p = 0; } } printf("\n"); p_table[n] = best_p; } TMP_FREE; gmp_randclear(rands); return 0; }