1 /* tune-gcd-p
2
3 Tune the choice for splitting p in divide-and-conquer gcd.
4
5 Copyright 2008, 2010, 2011 Free Software Foundation, Inc.
6
7 This file is part of the GNU MP Library.
8
9 The GNU MP Library is free software; you can redistribute it and/or modify
10 it under the terms of either:
11
12 * the GNU Lesser General Public License as published by the Free
13 Software Foundation; either version 3 of the License, or (at your
14 option) any later version.
15
16 or
17
18 * the GNU General Public License as published by the Free Software
19 Foundation; either version 2 of the License, or (at your option) any
20 later version.
21
22 or both in parallel, as here.
23
24 The GNU MP Library is distributed in the hope that it will be useful, but
25 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
26 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
27 for more details.
28
29 You should have received copies of the GNU General Public License and the
30 GNU Lesser General Public License along with the GNU MP Library. If not,
31 see https://www.gnu.org/licenses/. */
32
33 #define TUNE_GCD_P 1
34
35 #include "../mpn/gcd.c"
36
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <time.h>
41
42 #include "speed.h"
43
44 /* Search for minimum over a range. FIXME: Implement golden-section /
45 fibonacci search*/
46 static int
search(double * minp,double (* f)(void *,int),void * ctx,int start,int end)47 search (double *minp, double (*f)(void *, int), void *ctx, int start, int end)
48 {
49 int x[4];
50 double y[4];
51
52 int best_i;
53
54 x[0] = start;
55 x[3] = end;
56
57 y[0] = f(ctx, x[0]);
58 y[3] = f(ctx, x[3]);
59
60 for (;;)
61 {
62 int i;
63 int length = x[3] - x[0];
64
65 x[1] = x[0] + length/3;
66 x[2] = x[0] + 2*length/3;
67
68 y[1] = f(ctx, x[1]);
69 y[2] = f(ctx, x[2]);
70
71 #if 0
72 printf("%d: %f, %d: %f, %d:, %f %d: %f\n",
73 x[0], y[0], x[1], y[1], x[2], y[2], x[3], y[3]);
74 #endif
75 for (best_i = 0, i = 1; i < 4; i++)
76 if (y[i] < y[best_i])
77 best_i = i;
78
79 if (length <= 4)
80 break;
81
82 if (best_i >= 2)
83 {
84 x[0] = x[1];
85 y[0] = y[1];
86 }
87 else
88 {
89 x[3] = x[2];
90 y[3] = y[2];
91 }
92 }
93 *minp = y[best_i];
94 return x[best_i];
95 }
96
97 static int
compare_double(const void * ap,const void * bp)98 compare_double(const void *ap, const void *bp)
99 {
100 double a = * (const double *) ap;
101 double b = * (const double *) bp;
102
103 if (a < b)
104 return -1;
105 else if (a > b)
106 return 1;
107 else
108 return 0;
109 }
110
111 static double
median(double * v,size_t n)112 median (double *v, size_t n)
113 {
114 qsort(v, n, sizeof(*v), compare_double);
115
116 return v[n/2];
117 }
118
119 #define TIME(res, code) do { \
120 double time_measurement[5]; \
121 unsigned time_i; \
122 \
123 for (time_i = 0; time_i < 5; time_i++) \
124 { \
125 speed_starttime(); \
126 code; \
127 time_measurement[time_i] = speed_endtime(); \
128 } \
129 res = median(time_measurement, 5); \
130 } while (0)
131
132 struct bench_data
133 {
134 mp_size_t n;
135 mp_ptr ap;
136 mp_ptr bp;
137 mp_ptr up;
138 mp_ptr vp;
139 mp_ptr gp;
140 };
141
142 static double
bench_gcd(void * ctx,int p)143 bench_gcd (void *ctx, int p)
144 {
145 struct bench_data *data = (struct bench_data *) ctx;
146 double t;
147
148 p_table[data->n] = p;
149 TIME(t, {
150 MPN_COPY (data->up, data->ap, data->n);
151 MPN_COPY (data->vp, data->bp, data->n);
152 mpn_gcd (data->gp, data->up, data->n, data->vp, data->n);
153 });
154
155 return t;
156 }
157
158 int
main(int argc,char ** argv)159 main(int argc, char **argv)
160 {
161 gmp_randstate_t rands; struct bench_data data;
162 mp_size_t n;
163
164 TMP_DECL;
165
166 /* Unbuffered so if output is redirected to a file it isn't lost if the
167 program is killed part way through. */
168 setbuf (stdout, NULL);
169 setbuf (stderr, NULL);
170
171 gmp_randinit_default (rands);
172
173 TMP_MARK;
174
175 data.ap = TMP_ALLOC_LIMBS (P_TABLE_SIZE);
176 data.bp = TMP_ALLOC_LIMBS (P_TABLE_SIZE);
177 data.up = TMP_ALLOC_LIMBS (P_TABLE_SIZE);
178 data.vp = TMP_ALLOC_LIMBS (P_TABLE_SIZE);
179 data.gp = TMP_ALLOC_LIMBS (P_TABLE_SIZE);
180
181 mpn_random (data.ap, P_TABLE_SIZE);
182 mpn_random (data.bp, P_TABLE_SIZE);
183
184 memset (p_table, 0, sizeof(p_table));
185
186 for (n = 100; n < P_TABLE_SIZE; n++)
187 {
188 mp_size_t p;
189 mp_size_t best_p;
190 double best_time;
191 double lehmer_time;
192
193 if (data.ap[n-1] == 0)
194 data.ap[n-1] = 1;
195
196 if (data.bp[n-1] == 0)
197 data.bp[n-1] = 1;
198
199 data.n = n;
200
201 lehmer_time = bench_gcd (&data, 0);
202
203 best_p = search (&best_time, bench_gcd, &data, n/5, 4*n/5);
204 if (best_time > lehmer_time)
205 best_p = 0;
206
207 printf("%6lu %6lu %5.3g", (unsigned long) n, (unsigned long) best_p,
208 (double) best_p / n);
209 if (best_p > 0)
210 {
211 double speedup = 100 * (lehmer_time - best_time) / lehmer_time;
212 printf(" %5.3g%%", speedup);
213 if (speedup < 1.0)
214 {
215 printf(" (ignored)");
216 best_p = 0;
217 }
218 }
219 printf("\n");
220
221 p_table[n] = best_p;
222 }
223 TMP_FREE;
224 gmp_randclear(rands);
225 return 0;
226 }
227