-
Notifications
You must be signed in to change notification settings - Fork 10
/
commonb.c
13105 lines (10708 loc) · 506 KB
/
commonb.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*----------------------------------------------------------------------
| Copyright 1995-2024 Mersenne Research, Inc. All rights reserved
|
| This file contains routines and global variables that are common for
| all operating systems the program has been ported to. It is included
| in one of the source code files of each port.
|
| Commona contains information used only during setup
| Commonb contains information used only during execution
| Commonc contains information used during setup and execution
+---------------------------------------------------------------------*/
#include "ecm.h"
#include "exponentiate.h"
#include "polymult.h"
/* Globals for error messages */
static const char ERRMSG0[] = "Iteration: %ld/%ld, %s";
static const char ERRMSG1A[] = "ERROR: ILLEGAL SUMOUT\n";
static const char ERRMSG1C[] = "Possible error: round off (%.10g) > %.5g\n";
static const char ERRMSG1D[] = "ERROR: Shift counter corrupt.\n";
static const char ERRMSG1E[] = "ERROR: Illegal double encountered.\n";
static const char ERRMSG1F[] = "ERROR: FFT data has been zeroed!\n";
static const char ERRMSG1G[] = "ERROR: Jacobi error check failed!\n";
static const char ERRMSG2[] = "Possible hardware failure, consult readme.txt file.\n";
static const char ERRMSG3[] = "Continuing from last save file.\n";
static const char ERRMSG4[] = "Waiting five minutes before restarting.\n";
static const char ERRMSG5[] = "For added safety, redoing iteration using a slower, more reliable method.\n";
static const char ERRMSG6[] = "ERROR: Comparing PRP double-check values failed. Rolling back to iteration %lu.\n";
static const char ERRMSG7[] = "ERROR: Comparing Gerbicz checksum values failed. Rolling back to iteration %lu.\n";
static const char ERRMSG8[] = "ERROR: Invalid FFT data. Restarting from last save file.\n";
static const char ERRMSG9[] = "ERROR: Invalid PRP state. Restarting from last save file.\n";
static const char ERROK[] = "Disregard last error. Result is reproducible and thus not a hardware problem.\n";
static const char READFILEERR[] = "Error reading intermediate file: %s\n";
static const char WRITEFILEERR[] = "Error writing intermediate file: %s\n";
static const char ALTSAVE_MSG[] = "Trying backup intermediate file: %s\n";
const char ALLSAVEBAD_MSG[] = "All intermediate files bad. Temporarily abandoning work unit.\n";
/* PauseWhileRunning globals */
struct pause_info {
int thread_num; /* Worker to pause */
int low_mem; /* Flag set for LowMemWhileRunning entries */
int workers_affected; /* Number of workers affected */
char *program_name; /* Pause if running this program */
char matching_program[80]; /* Running program that matched this entry */
struct pause_info *next; /* Next in linked list of program names */
};
int PAUSE_MUTEX_INITIALIZED = 0;
gwmutex PAUSE_MUTEX; /* Lock for accessing pause globals */
struct pause_info *PAUSE_DATA = NULL;
int PAUSE_WHILE_RUNNING_FREQ = 10;
int PAUSEABLE_WORKERS_RUNNING = FALSE;
/* Globals for stopping and starting workers */
/* Note that we have one flag byte for each worker. We could use one bit per worker, but then we need to have locks around */
/* updates so that 2 workers don't interleave a read-modify-write operation. */
int STOP_FOR_RESTART = FALSE;/* Flag indicating we should stop and restart all workers because an important option changed in the GUI. */
/* One example is changing the priority for workers. */
int STOP_FOR_REREAD_INI = FALSE;/* Flag indicating all workers must */
/* stop because a during/else time period */
/* has ended and INI file must be reread. */
char STOP_FOR_MEM_CHANGED[MAX_NUM_WORKERS] = {0};
/* Flags indicating it is time to stop */
/* workers due to day/night memory change. */
int STOP_FOR_BATTERY = FALSE;/* Flag indicating it is time to stop */
/* workers due to running on battery. */
int STOP_FOR_AUTOBENCH = FALSE;/* Flag indicating we chould temporarily */
/* stop workers to run an auto-benchmark. */
char STOP_FOR_PRIORITY_WORK[MAX_NUM_WORKERS] = {0};
/* Flags indicating it is time to switch */
/* a worker to high priority work. */
struct pause_info *STOP_FOR_PAUSE[MAX_NUM_WORKERS] = {NULL};
/* Flags saying worker should pause while another program runs */
struct pause_info *STOP_FOR_LOW_MEMORY = NULL; /* Set when LowMemWhileRunning active */
/* Workers using lots of memory will be stopped */
int STOP_FOR_LOADAVG = 0; /* Count of workers to pause due */
/* to a period of high system load. */
char STOP_FOR_THROTTLE[MAX_NUM_WORKERS] = {0};
/* Flags indicating it is time to pause */
/* a worker for throttling. */
char STOP_FOR_ABORT[MAX_NUM_WORKERS] = {0};
/* Abort work unit due to unreserve, factor */
/* found in a different thread, server */
/* request, or any other reason. */
char ACTIVE_WORKERS[MAX_NUM_WORKERS] = {0};
/* Flags indicating which workers are active. */
char WRITE_SAVE_FILES[MAX_NUM_WORKERS] = {0};
/* Flags indicating it is time to write */
/* a save file. */
char JACOBI_ERROR_CHECK[MAX_NUM_WORKERS] = {0};
/* Flags indicating it is time to execute */
/* a Jacobi error check. */
char WORK_AVAILABLE_OR_STOP_INITIALIZED[MAX_NUM_WORKERS] = {0};
gwevent WORK_AVAILABLE_OR_STOP[MAX_NUM_WORKERS] = {0};
/* Signal for telling primeContinue that */
/* work is now available or all threads */
/* are stopping */
char USER_START_OR_STOP_INITIALIZED[MAX_NUM_WORKERS] = {0};
gwevent USER_START_OR_STOP[MAX_NUM_WORKERS] = {0};
/* Signal for telling implement_stop_one_worker */
/* that the user wants this worker to start or */
/* all threads are stopping */
char END_PAUSE_OR_STOP_INITIALIZED[MAX_NUM_WORKERS] = {0};
gwevent END_PAUSE_OR_STOP[MAX_NUM_WORKERS] = {0};
/* Signal for telling implement_pause */
/* that the pause has ended or */
/* all threads are stopping */
char END_LOADAVG_OR_STOP_INITIALIZED[MAX_NUM_WORKERS] = {0};
gwevent END_LOADAVG_OR_STOP[MAX_NUM_WORKERS] = {0};
/* Signal for telling implement_loadavg */
/* that the load average condition has ended */
/* or all threads are stopping */
char OFF_BATTERY_OR_STOP_INITIALIZED[MAX_NUM_WORKERS] = {0};
gwevent OFF_BATTERY_OR_STOP[MAX_NUM_WORKERS] = {0};
/* Signal for telling implement_stop_battery */
/* that AC power has been restored or */
/* all threads are stopping */
char MEM_WAIT_OR_STOP_INITIALIZED[MAX_NUM_WORKERS] = {0};
gwevent MEM_WAIT_OR_STOP[MAX_NUM_WORKERS] = {0};
/* Signal for telling avail_mem that */
/* it can now determine the available memory */
/* Globals for memory manager */
#define DEFAULT_MEM_USAGE 48 /* 48MB default */
unsigned long AVAIL_MEM = 0; /* Memory available now */
unsigned long MAX_MEM = 0; /* Max memory available */
unsigned long AVAIL_MEM_PER_WORKER[MAX_NUM_WORKERS] = {0}; /* Maximum memory each worker can use */
unsigned long MAX_HIGH_MEM_WORKERS = 0; /* Maximum number of workers allowed to use lots of memory */
char MEM_FLAGS[MAX_NUM_WORKERS] = {0}; /* Flags indicating which threads will be affected by a change in memory settings. */
unsigned int MEM_IN_USE[MAX_NUM_WORKERS] = {0}; /* Array containing memory in use by each worker */
#define MEM_RESTART_LOWMEM_ENDS 0x1 /* Worker needs to restart when the LowMemWhileRunning program ends. */
#define MEM_RESTART_MAX_MEM_AVAILABLE 0x2 /* Worker needs to restart when available memory equals maximum memory. This happens when */
/* stage 2 is delayed until max memory is available. */
#define MEM_RESTART_MAX_MEM_CHANGE 0x4 /* Current work unit needs to restart if max mem changes. P-1 may choose different bounds because of the change */
#define MEM_RESTART_TOO_MANY_HIGHMEM 0x8 /* Worker needs to restart because MAX_HIGH_MEM_WORKERS exceeded. */
#define MEM_RESTART_MORE_AVAIL 0x10 /* One of the worker's work units did not have enough memory to run. If memory becomes available restart the worker. */
#define MEM_RESTART_IF_MORE 0x20 /* The current work unit could use more memory and should be restarted if more becomes available. */
char MEM_RESTART_FLAGS[MAX_NUM_WORKERS] = {0};
unsigned int MEM_RESTART_MIN_AMOUNT[MAX_NUM_WORKERS] = {0};
unsigned int MEM_RESTART_DESIRED_AMOUNT[MAX_NUM_WORKERS] = {0}; /* Only restart if this amount of memory is available */
unsigned int MEM_RESTART_IF_MORE_AMOUNT[MAX_NUM_WORKERS] = {0};
int MEM_MUTEX_INITIALIZED = FALSE;
gwmutex MEM_MUTEX; /* Lock for accessing mem globals */
/*************************************/
/* Routines used to time code chunks */
/*************************************/
void clear_timers (
double *timers,
int num_timers)
{
int i;
for (i = 0; i < num_timers; i++) timers[i] = 0.0;
}
void clear_timer (
double *timers,
int i)
{
timers[i] = 0.0;
}
void start_timer (
double *timers,
int i)
{
if (RDTSC_TIMING < 10) {
timers[i] -= getHighResTimer ();
} else if (RDTSC_TIMING > 10 && (CPU_FLAGS & CPU_RDTSC)) {
uint32_t hi, lo;
rdtsc (&hi, &lo);
timers[i] -= (double) hi * 4294967296.0 + lo;
} else {
struct timeval start_time;
gettimeofday (&start_time, NULL);
timers[i] -= (double) start_time.tv_sec * 1000000.0 + start_time.tv_usec;
}
}
void start_timer_from_zero (
double *timers,
int i)
{
clear_timer (timers, i);
start_timer (timers, i);
}
void end_timer (
double *timers,
int i)
{
if (RDTSC_TIMING < 10) {
timers[i] += getHighResTimer ();
} else if (RDTSC_TIMING > 10 && (CPU_FLAGS & CPU_RDTSC)) {
uint32_t hi, lo;
rdtsc (&hi, &lo);
timers[i] += (double) hi * 4294967296.0 + lo;
} else {
struct timeval end_time;
gettimeofday (&end_time, NULL);
timers[i] += (double) end_time.tv_sec * 1000000.0 + end_time.tv_usec;
}
}
void divide_timer (
double *timers,
int i,
int j)
{
timers[i] = timers[i] / (double) j;
}
double timer_value (
double *timers,
int i)
{
if (RDTSC_TIMING < 10)
return (timers[i] / getHighResTimerFrequency ());
else if (RDTSC_TIMING > 10 && (CPU_FLAGS & CPU_RDTSC))
return (timers[i] / CPU_SPEED / 1000000.0);
else
return (timers[i] / 1000000.0);
}
void print_timer (
double *timers,
int i,
char *buf,
int flags)
{
double t;
/* The timer could be less than zero if the computer went into hibernation. */
/* Hibernation is where the memory image is saved to disk and the computer */
/* shut off. Upon power up the memory image is restored but the RDTSC */
/* timestamp counter has been reset to zero. */
buf += strlen (buf);
t = timer_value (timers, i);
if (t < 0.0) {
strcpy (buf, "Unknown");
timers[i] = 0.0;
}
/* Format the timer value in one of several styles */
else {
int style;
style = IniGetInt (INI_FILE, "TimingOutput", 0);
if (style == 0) {
if (flags & TIMER_MS) style = 4;
else style = 1;
}
if (style == 1)
sprintf (buf, "%.3f sec.", t);
else if (style == 2)
sprintf (buf, "%.1f ms.", t * 1000.0);
else if (style == 3)
sprintf (buf, "%.2f ms.", t * 1000.0);
else
sprintf (buf, "%.3f ms.", t * 1000.0);
if (RDTSC_TIMING == 12 && (CPU_FLAGS & CPU_RDTSC))
sprintf (buf+strlen(buf), " (%.0f clocks)", timers[i]);
else if (RDTSC_TIMING == 2)
sprintf (buf+strlen(buf), " (%.0f clocks)", t * CPU_SPEED * 1000000.0);
}
/* Append optional newline */
if (flags & TIMER_NL) strcat (buf, "\n");
/* Clear the timer */
if (flags & TIMER_CLR) timers[i] = 0.0;
if ((flags & TIMER_OPT_CLR) && !CUMULATIVE_TIMING) timers[i] = 0.0;
}
/**************************************************************/
/* Routines dealing with thread priority and affinity */
/**************************************************************/
/* The hwloc library numbers cores from 0 to HW_NUM_CORES-1. But we do not necessarily assign cores in that order. */
/* With the introduction of Alder Lake, we first assign compute/performance cores. Then assign efficiency cores. */
/* This routine maps "prime95 core numbers" into "hwloc core numbers", returning the index into the HW_CORES array (this array index */
/* is the same as the hwloc library's core number). This routine allows us to apply different ranking criteria for future architectures. */
uint32_t get_ranked_core ( /* Return hwloc core number (index into HW_CORES array) */
uint32_t core_num) /* A "prime95 core number". Zero for the first core, one for the second core, ... */
{
uint32_t i, num_performance_cores, num_efficiency_cores;
core_num++; // Need a one-based core_num in this routine
ASSERTG (core_num <= HW_NUM_CORES);
num_performance_cores = 0;
num_efficiency_cores = 0;
for (i = 0; i < HW_NUM_CORES; i++) {
if (HW_CORES[i].ranking >= 1) num_performance_cores++; else num_efficiency_cores++;
if (core_num <= HW_NUM_COMPUTE_CORES && core_num == num_performance_cores) return (i);
if (core_num > HW_NUM_COMPUTE_CORES && core_num - HW_NUM_COMPUTE_CORES == num_efficiency_cores) return (i);
}
ASSERTG (0);
return (0);
}
/* Return the number of threads gwnum will need to use to when running on several possibly hyperthreaded cores */
uint32_t get_ranked_num_threads (
uint32_t base_core_num, /* A "prime95 core number". Zero for the first core, one for the second core, ... */
uint32_t num_cores, /* Number of cores the gwnum FFT should use */
bool hyperthreading) /* TRUE if the gwnum FFT should use hyperthreading if available on each core */
{
if (!hyperthreading) return (num_cores);
uint32_t num_threads = 0;
for (uint32_t core = base_core_num; core < (int) HW_NUM_CORES && core < base_core_num + num_cores; core++)
num_threads += HW_CORES[get_ranked_core(core)].num_threads;
return (num_threads);
}
/* Return the number of threads gwnum will need to use when a worker is running on several possibly hyperthreaded cores */
uint32_t get_worker_num_threads (
uint32_t worker_num, /* Worker number (zero based) */
bool hyperthreading) /* TRUE if the gwnum FFT should use hyperthreading if available on each core */
{
// No hyperthreading is the easy case - one thread per cores to use.
if (!hyperthreading) return (CORES_PER_TEST[worker_num]);
// Mimic the code in SetPriority for SET_PRIORITY_NORMAL_WORK. We want to total up the hyperthreads available on the
// core(s) this worker will be assigned to.
// The first case is the user defined affinity assignments which we hope no one is using. This is a comma separated
// list of affinity settings for each thread.
char section_name[32];
const char *p;
sprintf (section_name, "Worker #%d", worker_num + 1);
p = IniSectionGetStringRaw (INI_FILE, section_name, "Affinity");
if (p != NULL) return (countCommas (p) + 1);
// Second case is the special SET_PRIORITY_NORMAL_WORK code where num workers = num cores
if (NUM_WORKERS == HW_NUM_COMPUTE_CORES || NUM_WORKERS == HW_NUM_CORES) return (get_ranked_num_threads (worker_num, 1, hyperthreading));
// Third case is to duplicate the SET_PRIORITY_NORMAL_WORK code to get the prime95 base core number using the total number of cores to use
uint32_t worker_core_count, cores_used_by_lower_workers, base_core_num;
worker_core_count = cores_used_by_lower_workers = 0;
for (uint32_t i = 0; i < NUM_WORKERS; i++) {
worker_core_count += CORES_PER_TEST[i];
if (i < worker_num) cores_used_by_lower_workers += CORES_PER_TEST[i];
}
if (worker_core_count < HW_NUM_COMPUTE_CORES && HW_NUM_COMPUTE_CORES == HW_NUM_CORES) base_core_num = cores_used_by_lower_workers + 1;
else if (worker_core_count <= HW_NUM_CORES) base_core_num = cores_used_by_lower_workers;
else base_core_num = cores_used_by_lower_workers % HW_NUM_CORES;
// Now return the sum of threads available in these core(s)
return (get_ranked_num_threads (base_core_num, CORES_PER_TEST[worker_num], hyperthreading));
}
/* Return the number of cores in the specified threading node */
uint32_t get_cores_in_threading_node (
uint32_t node_num) /* Threading node number (zero-based) */
{
//NOTE: This needs beefing up to handle new asymmetric architectures
if (node_num < HW_NUM_COMPUTE_THREADING_NODES) return (HW_NUM_COMPUTE_CORES / HW_NUM_COMPUTE_THREADING_NODES);
return ((HW_NUM_CORES - HW_NUM_COMPUTE_CORES) / (HW_NUM_THREADING_NODES - HW_NUM_COMPUTE_THREADING_NODES));
}
/* Internal routine to map base prime95 core number + aux_thread_num into the hwloc core number to set affinity to */
uint32_t map_aux_to_core (
uint32_t base_core_num, /* A "prime95 core number". Zero for the first core, one for the second core, ... */
uint32_t aux_thread_num, /* Zero for main thread, one or more for helper threads */
bool hyperthreading) /* TRUE if the gwnum FFT is using hyperthreading */
{
ASSERTG (base_core_num < (int) HW_NUM_CORES);
// Without hyperthreading. Aux-thread-num is same as core # since we are running one thread per core. Map to hwloc core numbering.
// Do a modulo in case user has oversubscribed cores.
if (!hyperthreading) return (get_ranked_core ((base_core_num + aux_thread_num) % HW_NUM_CORES));
// With hyperthreading. Use every thread on each core. Map to hwloc core numbering.
uint32_t total_threads = 0;
uint32_t hwloc_core = 0;
for (int core = base_core_num; ; core++) {
if (core == (int) HW_NUM_CORES) core = 0; // Shouldn't happen unless user has oversubscribed the CPU's core count. Wrap around.
hwloc_core = get_ranked_core (core);
total_threads += HW_CORES[hwloc_core].num_threads;
if (aux_thread_num < total_threads) break;
}
return (hwloc_core);
}
/* Set thread priority and affinity correctly. Most screen savers run at priority 4. */
/* Most Windows application's run at priority 9 when in foreground, 7 when in background. */
/* In selecting the proper thread priority I've assumed the program usually runs in the background. */
void SetPriority (
struct PriorityInfo *info)
{
int bind_type, core;
#ifdef BIND_TYPE_1_USED
int logical_CPU;
#endif
char logical_CPU_string[255];
char logical_CPU_substring[255];
char buf[255];
/* Call OS-specific routine to set the priority */
if (IniGetInt (INI_FILE, "EnableSetPriority", 1))
setOsThreadPriority (PRIORITY);
/* Skip setting affinity if requested by user. There is no known reason to do this at present. */
if (! IniGetInt (INI_FILE, "EnableSetAffinity", 1)) return;
/* Skip setting affinity if OS does not support it. At present time, that is Apple. */
if (!OS_CAN_SET_AFFINITY) return;
/* Pick from one of several methodologies to determine affinity setting */
switch (info->type) {
/* QA affinity. Let threads run on any CPU. */
case SET_PRIORITY_QA:
return;
/* Advanced/Time affinity. Set affinity to appropriate core. */
case SET_PRIORITY_TIME:
bind_type = 0; // Set affinity to one specific core
// Timing without hyperthreading. Aux-thread-num is same as core # since we are running one thread per core
// Timing with hyperthreading. Use every thread on each core.
core = map_aux_to_core (0, info->aux_thread_num, info->time_hyperthreading);
break;
/* Benchmarking -- similar to Advanced/Time. Set affinity to appropriate core. */
case SET_PRIORITY_BENCHMARKING:
bind_type = 0; // Set affinity to one specific core
// Benchmarking without hyperthreading. Aux-thread-num is same as core # since we are running one thread per core
// Benchmarking with hyperthreading. Use every thread on each core.
core = map_aux_to_core (info->bench_base_core_num, info->aux_thread_num, info->bench_hyperthreading);
break;
/* Busy loop on specified CPU core. */
case SET_PRIORITY_BUSY_LOOP:
bind_type = 0; // Set affinity to one specific core
core = get_ranked_core (info->busy_loop_core);
break;
/* Torture test affinity. Set affinity (including all auxiliary threads) to the core being tortured. */
case SET_PRIORITY_TORTURE:
bind_type = 0; // Set affinity to one specific core
core = get_ranked_core (info->torture_core_num);
break;
/* If user has given an explicit list of logical CPU cores to set affinity to, then use that list. */
/* Hopefully, there is no real need to ever use this feature. */
case SET_PRIORITY_NORMAL_WORK:
{
char section_name[32];
const char *p;
sprintf (section_name, "Worker #%d", info->worker_num+1);
p = IniSectionGetStringRaw (INI_FILE, section_name, "Affinity");
if (p != NULL) {
bind_type = 2; // Set affinity to a set of logical CPUs
truncated_strcpy (logical_CPU_string, sizeof (logical_CPU_string), p);
break;
}
}
/* If number of workers equals number of physical cores then run each worker on its own physical core. Run auxiliary threads on the same */
/* physical core. This might be advantageous on hyperthreaded CPUs. User should be careful to not run more auxiliary threads than available */
/* logical cores created by hyperthreading. In essence, we are overriding the settings in CORES_TO_USE and using just one core. This is not */
/* intuitive to the user and perhaps we should delete this code! */
if (NUM_WORKERS == HW_NUM_COMPUTE_CORES || NUM_WORKERS == HW_NUM_CORES) {
bind_type = 0; // Set affinity to a specific physical CPU core
core = get_ranked_core (info->worker_num);
break;
}
/* Set P-1 and ECM Stage2ExtraThreads to run on any performance core */
if (info->aux_polymult && info->aux_thread_num >= CORES_PER_TEST[info->worker_num] * (info->normal_work_hyperthreading ? 2 : 1)) {
bind_type = 3;
break;
}
/* Calculate the total num worker cores to be used. We will base our affinity decisions on this value. */
uint32_t worker_core_count, cores_used_by_lower_workers;
worker_core_count = cores_used_by_lower_workers = 0;
for (uint32_t i = 0; i < NUM_WORKERS; i++) {
worker_core_count += CORES_PER_TEST[i];
if (i < info->worker_num) cores_used_by_lower_workers += CORES_PER_TEST[i];
}
/* If total num worker cores < num compute cores we will give each worker its own CPU. There is some weak anecdotal */
/* evidence that CPU 0 is reserved for interrupt processing on some OSes and architectures without efficiency cores, */
/* so we leave CPU #0 unused (hoping hwloc assigns CPU numbers the same way the OS does). */
/* This post, https://www.mersenneforum.org/showpost.php?p=599873&postcount=387, reports downsides to reserving core 0 for OS use. */
/* Basically, reserving core 0 might cause a worker to be spread across multiple L2 caches rather than a single shared L2 cache. */
/* In version 30.8 build 11, I've switched to making reserving core 0 optional rather than the default. */
if (worker_core_count < HW_NUM_COMPUTE_CORES && HW_NUM_COMPUTE_CORES == HW_NUM_CORES && IniGetInt (INI_FILE, "ReserveCore0", 0)) {
bind_type = 0; // Set affinity to a specific physical CPU core
// Map prime95 core number and auxiliary thread number to hwloc core number.
core = map_aux_to_core (cores_used_by_lower_workers + 1, info->aux_thread_num, info->normal_work_hyperthreading);
break;
}
/* If total num worker cores <= num cores, then there is an easy path forward. Assign cores in ascending order. This will assign efficiency cores last. */
if (worker_core_count <= HW_NUM_CORES) {
bind_type = 0; // Set affinity to a specific physical CPU core
// Map prime95 core number and auxiliary thread number to hwloc core number.
core = map_aux_to_core (cores_used_by_lower_workers, info->aux_thread_num, info->normal_work_hyperthreading);
break;
}
/* Total num worker cores is greater than num cores. What to do? */
/* Perhaps our default policy should be to throw our hands up in despair and simply run on any CPU core. */
/* Instead, we "wrap around" and start assigning to compute cores again. This lets the user create one worker per thread -- assuming the */
/* compute cores rather than the efficiency cores are the ones likely to support hyperthreading. */
bind_type = 0; // Set affinity to a specific physical CPU core
core = map_aux_to_core (cores_used_by_lower_workers % HW_NUM_CORES, info->aux_thread_num, info->normal_work_hyperthreading);
break;
}
/* Parse affinity settings specified in the INI file. */
/* We accept several syntaxes in an INI file for a zero-based list of logical CPUs: */
/* 3,6,9 Run main worker on logical CPU #3, run two aux threads on logical CPUs #6 & #9 */
/* 3-4,5-6 Run main worker on logical CPUs #3 & #4, run aux thread on logical CPUs #5 & #6 */
/* {3,5,7},{4,6} Run main worker on logical CPUs #3, #5, & #7, run aux thread on logical CPUs #4 & #6 */
/* (3,5,7),(4,6) Run main worker on logical CPUs #3, #5, & #7, run aux thread on logical CPUs #4 & #6 */
/* [3,5-7],(4,6) Run main worker on logical CPUs #3, #5, #6, & #7, run aux thread on logical CPUs #4 & #6 */
if (bind_type == 2) { // Find the subset of the logical CPU string for this auxilary thread
uint32_t i;
const char *p;
for (i = 0, p = logical_CPU_string; i <= info->aux_thread_num && *p; i++) {
while (isspace (*p)) p++;
const char *start = p;
char end_char = 0;
if (*p == '{') end_char = '}';
if (*p == '(') end_char = ')';
if (*p == '[') end_char = ']';
if (end_char) {
p = strchr (start, end_char);
if (p == NULL) {
sprintf (buf, "Error parsing affinity string: %s\n", logical_CPU_string);
OutputStr (info->worker_num, buf);
return;
}
truncated_strcpy_with_len (logical_CPU_substring, sizeof (logical_CPU_substring), start + 1, (int) (p - start - 1));
if (strchr (p, ',') != NULL) p = strchr (p, ',') + 1;
else p = p + strlen(p);
} else {
p = strchr (start, ',');
if (p != NULL) {
truncated_strcpy_with_len (logical_CPU_substring, sizeof (logical_CPU_substring), start, (int) (p - start));
p++;
} else {
truncated_strcpy (logical_CPU_substring, sizeof (logical_CPU_substring), start);
p = start + strlen (start);
}
}
}
}
/* Output an informative message */
if (HW_NUM_CORES > 1 && info->verbosity >= 1) {
if (info->aux_hyperthread) sprintf (buf, "Setting affinity to run prefetching hyperthread on ");
else if (info->aux_polymult) sprintf (buf, "Setting affinity to run polymult helper thread on ");
else if (info->aux_thread_num == 0) strcpy (buf, "Setting affinity to run worker on ");
else sprintf (buf, "Setting affinity to run helper thread %d on ", info->aux_thread_num);
if (bind_type == 0) sprintf (buf+strlen(buf), "CPU core #%d\n", core+1);
#ifdef BIND_TYPE_1_USED
else if (bind_type == 1) sprintf (buf+strlen(buf), "logical CPU #%d (zero-based)\n", logical_CPU);
#endif
else if (bind_type == 3) sprintf (buf+strlen(buf), "any performance core\n");
else sprintf (buf+strlen(buf), "logical CPUs %s (zero-based)\n", logical_CPU_substring);
OutputStr (info->worker_num, buf);
}
/* Set affinity for this thread to a specific CPU core */
if (bind_type == 0) {
int num_cores;
hwloc_obj_t obj;
num_cores = hwloc_get_nbobjs_by_type (hwloc_topology, HWLOC_OBJ_CORE);
if (num_cores < 1) num_cores = hwloc_get_nbobjs_by_type (hwloc_topology, HWLOC_OBJ_PU);
if (num_cores < 1) num_cores = 1; // This shouldn't happen
if (core >= num_cores) { // This shouldn't happen
sprintf (buf, "Error setting affinity to core #%d. There are %d cores.\n", core+1, num_cores);
OutputStr (info->worker_num, buf);
core %= num_cores;
}
obj = hwloc_get_obj_by_type (hwloc_topology, HWLOC_OBJ_CORE, core); /* Get proper core */
if (obj == NULL) obj = hwloc_get_obj_by_type (hwloc_topology, HWLOC_OBJ_PU, core); /* Get proper core */
if (obj != NULL) {
if (hwloc_set_cpubind (hwloc_topology, obj->cpuset, HWLOC_CPUBIND_THREAD)) { /* Bind thread to all logical CPUs in the core */
char str[80];
int error = errno;
hwloc_bitmap_snprintf (str, sizeof (str), obj->cpuset);
sprintf (buf, "Error setting affinity to cpuset %s: %s\n", str, strerror (error));
OutputStr (info->worker_num, buf);
}
else if (info->verbosity >= 2) {
char str[80];
hwloc_bitmap_snprintf (str, sizeof (str), obj->cpuset);
sprintf (buf, "Affinity set to cpuset %s\n", str);
OutputStr (info->worker_num, buf);
}
}
else { // This shouldn't happen
sprintf (buf, "Error getting hwloc object for core #%d. Affinity not set.\n", core+1);
OutputStr (info->worker_num, buf);
}
}
/* Set affinity for this thread to a specific logical CPU */
#ifdef BIND_TYPE_1_USED
else if (bind_type == 1) {
int num_logical_CPUs;
hwloc_obj_t obj;
num_logical_CPUs = hwloc_get_nbobjs_by_type (hwloc_topology, HWLOC_OBJ_PU);
if (logical_CPU >= num_logical_CPUs) { // This shouldn't happen
sprintf (buf, "Error setting affinity to logical CPU #%d (zero-based). There are %d logical CPUs.\n", logical_CPU, num_logical_CPUs);
OutputStr (info->worker_num, buf);
logical_CPU %= num_logical_CPUs;
}
obj = hwloc_get_obj_by_type (hwloc_topology, HWLOC_OBJ_PU, logical_CPU); /* Get proper logical CPU */
if (obj) {
if (hwloc_set_cpubind (hwloc_topology, obj->cpuset, HWLOC_CPUBIND_THREAD)) { /* Bind thread to one logical CPU */
char str[80];
int error = errno;
hwloc_bitmap_snprintf (str, sizeof (str), obj->cpuset);
sprintf (buf, "Error setting affinity to cpuset %s: %s\n", str, strerror (error));
OutputStr (info->worker_num, buf);
}
else if (info->verbosity >= 2) {
char str[80];
hwloc_bitmap_snprintf (str, sizeof (str), obj->cpuset);
sprintf (buf, "Affinity set to cpuset %s\n", str);
OutputStr (info->worker_num, buf);
}
}
}
#endif
/* Set affinity for this thread to any performance thread */
else if (bind_type == 3) {
hwloc_bitmap_t cpuset = hwloc_bitmap_alloc ();
for (int core = 0; core < (int) HW_NUM_CORES; core++) {
if (HW_CORES[core].ranking != 1) continue; // Only look at performance cores
hwloc_obj_t obj = hwloc_get_obj_by_type (hwloc_topology, HWLOC_OBJ_CORE, core); /* Get core obj */
if (obj == NULL) obj = hwloc_get_obj_by_type (hwloc_topology, HWLOC_OBJ_PU, core); /* The above failed for someone use plan B */
if (obj == NULL) continue; // Can't happen?
hwloc_bitmap_or (cpuset, cpuset, obj->cpuset);
}
if (hwloc_set_cpubind (hwloc_topology, cpuset, HWLOC_CPUBIND_THREAD)) {
char str[80];
int error = errno;
hwloc_bitmap_snprintf (str, sizeof (str), cpuset);
sprintf (buf, "Error setting affinity to cpuset %s: %s\n", str, strerror (error));
OutputStr (info->worker_num, buf);
}
else if (info->verbosity >= 2) {
char str[80];
hwloc_bitmap_snprintf (str, sizeof (str), cpuset);
sprintf (buf, "Affinity set to cpuset %s\n", str);
OutputStr (info->worker_num, buf);
}
hwloc_bitmap_free (cpuset);
}
/* Set affinity for this thread to one or more logical CPUs as specified in the INI file. */
else {
char *p, *dash, *comma;
hwloc_obj_t obj;
hwloc_bitmap_t cpuset;
int start, end;
cpuset = hwloc_bitmap_alloc ();
for (p = logical_CPU_substring; ; p = comma+1) { // Loop through comma-separated list in the logical_CPU_substring
start = atoi (p);
dash = strchr (p, '-');
comma = strchr (p, ',');
if (dash != NULL && (comma == NULL || dash < comma)) end = atoi (dash+1);
else end = start;
while (start <= end) {
obj = hwloc_get_obj_by_type (hwloc_topology, HWLOC_OBJ_PU, start++);
if (obj) hwloc_bitmap_or (cpuset, cpuset, obj->cpuset);
}
if (comma == NULL) break;
}
if (hwloc_set_cpubind (hwloc_topology, cpuset, HWLOC_CPUBIND_THREAD)) { /* Set affinity to specified logical CPUs */
char str[80];
int error = errno;
hwloc_bitmap_snprintf (str, sizeof (str), cpuset);
sprintf (buf, "Error setting affinity to cpuset %s: %s\n", str, strerror (error));
OutputStr (info->worker_num, buf);
}
else if (info->verbosity >= 2) {
char str[80];
hwloc_bitmap_snprintf (str, sizeof (str), cpuset);
sprintf (buf, "Affinity set to cpuset %s\n", str);
OutputStr (info->worker_num, buf);
}
hwloc_bitmap_free (cpuset);
}
}
/* Gwnum thread callback routine */
void SetAuxThreadPriority (int aux_thread_num, int action, void *data)
{
/* Handle thread start and hyperthread start action. Set the thread priority. */
if (action == 0 || action == 10 || action == 20) {
struct PriorityInfo sp_info;
memcpy (&sp_info, data, sizeof (struct PriorityInfo));
sp_info.aux_thread_num = aux_thread_num;
sp_info.aux_hyperthread = (action == 10);
sp_info.aux_polymult = (action == 20);
SetPriority (&sp_info);
}
/* Handle thread terminate and hyperthread terminate action. Remove thread handle from list of active workers. */
if (action == 1 || action == 11 || action == 21) {
registerThreadTermination ();
}
}
/**************************************************************/
/* Routines and globals dealing with stop codes */
/* and the write save files timer */
/**************************************************************/
/* This routine checks if the worker needs to be stopped for any reason whatsoever. If the worker should stop, a stop reason is returned. */
/* The routine is declared EXTERNC because it can be called by the C code in giants that does GCD. */
EXTERNC int stopCheck (
int thread_num) /* Worker number */
{
/* Call an OS-specific callback routine. This gives OSes that poll for */
/* the ESC key a hook. They can perform any necessary miscellaneous */
/* functions and check for the ESC key to call stop_workers_for_escape. */
stopCheckCallback (thread_num);
/* If the ESC key was hit, stop processing. This takes precedence over */
/* all other stop reasons. This also happens when the program is exiting. */
if (WORKERS_STOPPING) return (STOP_ESCAPE);
/* If this request is coming from one of the "special" thread_nums, then */
/* do not check for any more stop codes. The only time I know this can happen */
/* is when we are running auto-benchmarks using MAIN_THREAD_NUM */
if (thread_num < 0) return (0);
/* If an important option changed in the GUI, restart all workers. For example, the user changes the priority of all workers. */
if (STOP_FOR_RESTART) return (STOP_RESTART);
/* If the during/else time period has ended, stop processing all workers so prime.txt and local.txt can be reprocessed. */
if (STOP_FOR_REREAD_INI) return (STOP_REREAD_INI);
/* If the memory settings have changed, stop processing affected workers so they can allocate more or less memory. */
if (STOP_FOR_MEM_CHANGED[thread_num]) {
STOP_FOR_MEM_CHANGED[thread_num] = 0;
return (STOP_MEM_CHANGED);
}
/* If we are on battery power, stop processing all worker */
/* threads until we cease running on the battery. */
if (STOP_FOR_BATTERY) return (STOP_BATTERY);
/* Check if we need to do an auto-benchmark */
if (STOP_FOR_AUTOBENCH) return (STOP_AUTOBENCH);
/* If the thread needs to go do some higher priority work, then stop */
/* processing this work_unit and reprocess the worktodo file. */
if (STOP_FOR_PRIORITY_WORK[thread_num]) {
STOP_FOR_PRIORITY_WORK[thread_num] = 0;
return (STOP_PRIORITY_WORK);
}
/* If the thread needs to abort the current work unit, then return */
/* that stop code. */
if (STOP_FOR_ABORT[thread_num]) {
STOP_FOR_ABORT[thread_num] = 0;
return (STOP_ABORT);
}
/* If the thread needs to stop because the user has explicitly stopped (or never */
/* started) this worker, then return the proper stop code. */
if (!ACTIVE_WORKERS[thread_num]) return (STOP_WORKER);
/* Check if thread should pause because another process is running. */
/* When pause completes, check stop codes again. We may have been paused */
/* a long time during which other stop timers may have fired. */
if (STOP_FOR_PAUSE[thread_num] != NULL) {
return (STOP_PAUSE);
// Do we want to offer INI option to do an immediate pause (next 2 lines) instead???
// implement_pause (thread_num);
// return (stopCheck (thread_num));
}
/* Check if thread should pause because system load is high. */
/* When pause completes, check stop codes again. We may have been paused */
/* a long time during which other stop timers may have fired. */
if (STOP_FOR_LOADAVG) {
STOP_FOR_LOADAVG--;
implement_loadavg (thread_num);
return (stopCheck (thread_num));
}
/* If the thread needs to pause because of the throttle option, then */
/* do so now. */
if (STOP_FOR_THROTTLE[thread_num]) {
STOP_FOR_THROTTLE[thread_num] = 0;
implementThrottle (thread_num);
}
/* No need to stop */
return (0);
}
/* Clear flags controlling the stopping of workers */
int stop_events_initialized = FALSE;
void init_stop_code (void)
{
STOP_FOR_RESTART = FALSE;
STOP_FOR_REREAD_INI = FALSE;
STOP_FOR_BATTERY = FALSE;
STOP_FOR_AUTOBENCH = FALSE;
STOP_FOR_LOADAVG = 0;
memset (STOP_FOR_MEM_CHANGED, 0, sizeof (STOP_FOR_MEM_CHANGED));
memset (STOP_FOR_PRIORITY_WORK, 0, sizeof (STOP_FOR_PRIORITY_WORK));
memset (STOP_FOR_PAUSE, 0, sizeof (STOP_FOR_PAUSE));
memset (STOP_FOR_THROTTLE, 0, sizeof (STOP_FOR_THROTTLE));
memset (STOP_FOR_ABORT, 0, sizeof (STOP_FOR_ABORT));
memset (WRITE_SAVE_FILES, 0, sizeof (WRITE_SAVE_FILES));
memset (JACOBI_ERROR_CHECK, 0, sizeof (JACOBI_ERROR_CHECK));
}
/* Signal threads waiting for work to do */
void restart_waiting_workers (
int restart_flags)
{
int thread_num;
for (thread_num = 0; thread_num < MAX_NUM_WORKERS; thread_num++)
restart_one_waiting_worker (thread_num, restart_flags);
}
void restart_one_waiting_worker (
int thread_num,
int restart_flags)
{
if (restart_flags & RESTART_USER_START &&
USER_START_OR_STOP_INITIALIZED[thread_num]) {
gwevent_signal (&USER_START_OR_STOP[thread_num]);
}
if (restart_flags & RESTART_WORK_AVAILABLE &&
WORK_AVAILABLE_OR_STOP_INITIALIZED[thread_num]) {
gwevent_signal (&WORK_AVAILABLE_OR_STOP[thread_num]);
}
if (restart_flags & RESTART_END_PAUSE &&
END_PAUSE_OR_STOP_INITIALIZED[thread_num]) {
gwevent_signal (&END_PAUSE_OR_STOP[thread_num]);
}
if (restart_flags & RESTART_LOADAVG &&
END_LOADAVG_OR_STOP_INITIALIZED[thread_num]) {
gwevent_signal (&END_LOADAVG_OR_STOP[thread_num]);
}
if (restart_flags & RESTART_MEM_WAIT &&
MEM_WAIT_OR_STOP_INITIALIZED[thread_num]) {
gwevent_signal (&MEM_WAIT_OR_STOP[thread_num]);
}
if (restart_flags & RESTART_BATTERY &&
OFF_BATTERY_OR_STOP_INITIALIZED[thread_num]) {
gwevent_signal (&OFF_BATTERY_OR_STOP[thread_num]);
}
}
/* Set flags so that workers will stop due to ESC key being pressed */
void stop_workers_for_escape (void)
{
if (WORKERS_ACTIVE) {
OutputStr (MAIN_THREAD_NUM, "Stopping all worker windows.\n");
WORKERS_STOPPING = TRUE;
restart_waiting_workers (RESTART_ALL);
raiseAllWorkersPriority ();
}
}
/* Set flag so that all workers stop and restart because an important INI option changed (like thread priority). */
/* This routine only restarts "genuine" workers - not benchmarking and torture test workers. */
void stop_workers_for_restart (void)
{
if (WORKERS_ACTIVE &&
LAUNCH_TYPE == LD_CONTINUE &&
! STOP_FOR_RESTART) {
OutputStr (MAIN_THREAD_NUM, "Restarting all worker windows.\n");
STOP_FOR_RESTART = TRUE;
restart_waiting_workers (RESTART_ALL);
if (NUM_WORKERS > WORKERS_ACTIVE)
create_worker_windows (NUM_WORKERS);
}
}
/* Set flag so that all workers stop and restart because an important INI option changed (like thread priority). */
void stop_workers_for_add_files (void)
{
if (WORKERS_ACTIVE && ! STOP_FOR_RESTART) {
OutputStr (MAIN_THREAD_NUM, "Restarting all worker windows to process .add file.\n");
STOP_FOR_RESTART = TRUE;
restart_waiting_workers (RESTART_ALL);
}
}
/* Set flag so that workers will stop due to Time= end time being reached. We need to stop all workers, reprocess prime.ini, and restart the workers. */
void stop_workers_for_reread_ini (void)
{
if (WORKERS_ACTIVE && ! STOP_FOR_REREAD_INI) {
OutputStr (MAIN_THREAD_NUM, "Restarting all worker windows with new timed INI settings.\n");
STOP_FOR_REREAD_INI = TRUE;
restart_waiting_workers (RESTART_ALL);
}
}
/* Set flags so that workers will stop due to day/night memory changeover. */
void stop_worker_for_mem_changed (
int thread_num)
{
if (WORKERS_ACTIVE && ! STOP_FOR_MEM_CHANGED[thread_num]) {
OutputStr (thread_num, "Restarting worker with new memory settings.\n");
MEM_FLAGS[thread_num] |= MEM_RESTARTING;
STOP_FOR_MEM_CHANGED[thread_num] = 1;
restart_one_waiting_worker (thread_num, RESTART_ALL);
}
}
/* Set flag so that worker will stop to do priority work. */
void stop_worker_for_advanced_test (
int thread_num)
{
if (WORKERS_ACTIVE && ! STOP_FOR_PRIORITY_WORK[thread_num]) {
OutputStr (thread_num, "Restarting worker to do LL test.\n");
STOP_FOR_PRIORITY_WORK[thread_num] = 1;
}
}
/* Set flag so that worker will stop to do priority work. */
void stop_worker_for_priority_work (
int thread_num)
{