1 /* Very efficient antebuffer to preempt the file(s) load.
2 Syntax: antebuffer X file [file] | YourCommand
3 with buffer size = 2^X.
4 The files are written on stdout.
5 Best size for the buffer is about 4MB (local disk) to
6 16MB (NFS), eventually until 128 MB.
7 Limitation: X <= 31; more than 2GB antebuffer has no sense anyway.
8 */
9
10 /* To avoid the warning: implicit declaration of nanosleep for c99 compliant */
11 #include "cado.h" // IWYU pragma: keep
12 #ifdef _POSIX_C_SOURCE
13 #undef _POSIX_C_SOURCE
14 #endif
15 #define _POSIX_C_SOURCE 200809L
16
17 #include <stdio.h>
18 #include <string.h>
19 #include <stdlib.h>
20 #include <unistd.h>
21 #include <fcntl.h>
22 #include <stdint.h>
23 #include <pthread.h>
24 #include <errno.h>
25 #include <time.h>
26 #include "macros.h" // LIKELY UNLIKELY
27 #include "timing.h"
28 #include "portability.h" // sleep // IWYU pragma: keep
29
30 #ifdef HAVE_MINGW
31 int _CRT_fmode = _O_BINARY; /* Binary open for stdin/out/err */
32 #endif
33
34 #ifndef HAVE_NANOSLEEP
nanosleep(const struct timespec * req,struct timespec * rem)35 int nanosleep(const struct timespec *req, struct timespec *rem) {
36 if (rem == NULL) {
37 /* Dummy to shut up the warning */
38 }
39 #ifdef HAVE_USLEEP
40 unsigned long usec = req->tv_sec * 1000000UL + req->tv_nsec / 1000UL;
41 usleep(usec);
42 #else
43 sleep(req->tv_sec);
44 #endif
45 return 0;
46 }
47 #endif
48
49 /* These variables are used by pthread and main */
50 static volatile uintptr_t ab_cptp = 0, ab_cptc = 0; /* Main counters */
51 static volatile int ab_end = 0; /* 1 if end */
52 static char *ab_buf; /* Main buffer */
53 static const struct timespec waiting = { 0, 1<<13 }; /* About 8 to 20 microseconds */
54 static int ab_in; /* fd for loading */
55 static size_t ab_size, ab_sizeio; /* size for buffer & in */
56
57 /* The 3 shared variables must be considered with non atomical access.
58 So, dedicated mutex protection for all.
59 NB: spinlocks are not mandatory in POSIX and don't exist in macos.
60 But it's so uncommon a mutex is blocking here than the mutex version
61 has the same speed than the spin locks version, and both are faster
62 than the gcc __builtin_fetch_and_add version.
63 */
64 static pthread_mutex_t mutex_ab_cptp = PTHREAD_MUTEX_INITIALIZER;
65 static pthread_mutex_t mutex_ab_cptc = PTHREAD_MUTEX_INITIALIZER;
66 static pthread_mutex_t mutex_ab_end = PTHREAD_MUTEX_INITIALIZER;
67
ab_cons()68 static void ab_cons () {
69 uintptr_t cpy_ab_cptc = 0;
70 size_t c, t = 0;
71 ssize_t w;
72
73 pthread_setcanceltype (PTHREAD_CANCEL_DEFERRED, NULL);
74 for ( ; ; ) {
75 for ( ; ; ) {
76 uintptr_t mut_ab_cptp;
77 int mut_ab_end;
78 pthread_mutex_lock (&mutex_ab_cptp); mut_ab_cptp = ab_cptp; pthread_mutex_unlock (&mutex_ab_cptp);
79 c = (mut_ab_cptp - cpy_ab_cptc);
80 if (LIKELY (c)) break;
81 pthread_mutex_lock (&mutex_ab_end); mut_ab_end = ab_end; pthread_mutex_unlock (&mutex_ab_end);
82 if (UNLIKELY (mut_ab_end)) {
83 pthread_mutex_lock (&mutex_ab_cptp); mut_ab_cptp = ab_cptp; pthread_mutex_unlock (&mutex_ab_cptp);
84 c = (int) (mut_ab_cptp - cpy_ab_cptc);
85 if (LIKELY (c)) break;
86 pthread_exit(NULL);
87 }
88 nanosleep (&waiting, NULL);
89 }
90 if (c > ab_sizeio) c = ab_sizeio;
91 if (c > ab_size - t) c = ab_size - t;
92 while ((w = write(1, &(ab_buf[t]), c)) <= 0) nanosleep (&waiting, NULL);
93 cpy_ab_cptc += w;
94 t = (t + w) & (ab_size - 1);
95 pthread_mutex_lock (&mutex_ab_cptc); ab_cptc = cpy_ab_cptc; pthread_mutex_unlock (&mutex_ab_cptc);
96 }
97 }
98
main(int argc,char ** argv)99 int main(int argc, char **argv) {
100 pthread_t ab_tc;
101 pthread_attr_t ab_attr;
102 uintptr_t cpy_ab_cptp = 0;
103 size_t c, t = 0;
104 ssize_t r;
105 unsigned int p;
106 char *real_malloc;
107
108 #ifdef HAVE_MINGW
109 _fmode = _O_BINARY; /* Binary open for all others files */
110 #endif
111
112 if (argc < 3) {
113 error:
114 fprintf (stderr, "%s syntax: %s SIZE file [file] or %s SIZE -\n"
115 "This command is an ante buffer which loads the files or stdin in a\n"
116 "buffer and write them on stdout. The size of this buffer is 2^SIZE.\n"
117 "16<=SIZE<=31, best for 20 to 24.\n", argv[0], argv[0], argv[0]);
118 exit (1);
119 }
120 if ((sscanf(argv[1], "%zu", &ab_size)) != 1) goto error;
121 if (ab_size < 16 || ab_size > 31) goto error;
122 ab_size = (uintptr_t) 1 << ab_size;
123 ab_sizeio = ab_size >> 4;
124 if (!(real_malloc = malloc (ab_size + 0xFFF))) {
125 fprintf (stderr, "%s: malloc error: %s\n", argv[0], strerror(errno));
126 exit(1);
127 }
128 ab_buf = (char *) (((uintptr_t) real_malloc + 0xFFF) & ~ (uintptr_t) 0xFFF);
129 pthread_attr_init(&ab_attr);
130 pthread_attr_setstacksize(&ab_attr, 1<<12);
131 pthread_attr_setdetachstate(&ab_attr, PTHREAD_CREATE_JOINABLE);
132 if (pthread_create(&ab_tc, &ab_attr, (void *) ab_cons, NULL)) {
133 fprintf (stderr, "%s: pthread_create error: %s\n", argv[0], strerror(errno));
134 exit(1);
135 }
136 if (strcmp(argv[2], "-")) {
137 p = 3;
138 if ((ab_in = open(argv[2], O_RDONLY)) == -1) {
139 fprintf (stderr, "%s: open or load error in file %s: %s\n", argv[0], argv[2], strerror(errno));
140 exit (1);
141 }
142 }
143 else {
144 p = 0;
145 ab_in = 0;
146 }
147 for ( ; ; ) {
148 for ( ; ; ) {
149 uintptr_t mut_ab_cptc;
150 pthread_mutex_lock (&mutex_ab_cptc); mut_ab_cptc = ab_cptc; pthread_mutex_unlock (&mutex_ab_cptc);
151 c = ((mut_ab_cptc + ab_size) - cpy_ab_cptp);
152 if (LIKELY (c)) break;
153 nanosleep (&waiting, NULL);
154 }
155 if (c > ab_sizeio) c = ab_sizeio;
156 if (c > ab_size - t) c = ab_size - t;
157 r = read(ab_in, &(ab_buf[t]), c);
158 if (r > 0) {
159 cpy_ab_cptp += r;
160 t = (t + r) & (ab_size - 1);
161 pthread_mutex_lock (&mutex_ab_cptp); ab_cptp = cpy_ab_cptp; pthread_mutex_unlock (&mutex_ab_cptp);
162 }
163 else
164 if (!r) {
165 if (!p || p == (unsigned int) argc) {
166 pthread_mutex_lock (&mutex_ab_end); ab_end = 1; pthread_mutex_unlock (&mutex_ab_end);
167 break;
168 }
169 close(ab_in);
170 if ((ab_in = open(argv[p++], O_RDONLY)) == -1) {
171 fprintf (stderr, "%s: open or load error in file %s: %s\n", argv[0], argv[p-1], strerror(errno));
172 exit (1);
173 }
174 }
175 else {
176 fprintf (stderr, "%s: read error in file %s: %s\n", argv[0], argv[p-1], strerror(errno));
177 exit (1);
178 }
179 }
180 if (p) close(ab_in);
181 pthread_join(ab_tc, NULL);
182 pthread_mutex_destroy (&mutex_ab_end);
183 pthread_mutex_destroy (&mutex_ab_cptc);
184 pthread_mutex_destroy (&mutex_ab_cptp);
185 free (real_malloc);
186 double tt[2];
187 seconds_user_sys(tt);
188 /*
189 fprintf(stderr, "antebuffer exits after having spent %.2fs+%.2fs on cpu\n",
190 tt[0], tt[1]);
191 */
192 exit (0);
193 }
194