-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcommonc.c
More file actions
7376 lines (6040 loc) · 256 KB
/
commonc.c
File metadata and controls
7376 lines (6040 loc) · 256 KB
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-2025 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. See common.h for the
| common #defines and common routine definitions.
|
| Commona contains information used only during setup
| Commonb contains information used only during execution
| Commonc contains information used during setup and execution
+---------------------------------------------------------------------*/
static const char JUNK[]="Copyright 1996-2025 Mersenne Research, Inc. All rights reserved";
char INI_FILE[260] = {0};
char WORKTODO_FILE[260] = {0};
char RESFILE[260] = {0};
char RESFILEBENCH[260] = {0};
char RESFILEJSON[260] = {0};
char SPOOL_FILE[260] = {0};
char LOGFILE[260] = {0};
char SCREENLOGFILE[260] = {0};
char *RESFILES[3] = {RESFILE, RESFILEBENCH, RESFILEJSON};
int NO_GUI = 1;
char USERID[21] = {0};
char COMPID[21] = {0};
char COMPUTER_GUID[33] = {0};
char HARDWARE_GUID[33] = {0};
char WINDOWS_GUID[33] = {0};
int USE_PRIMENET = 0;
int DIAL_UP = 0;
unsigned int DAYS_OF_WORK = 5;
int STRESS_TESTER = 0;
int volatile ERRCHK = 0;
unsigned int PRIORITY = 1;
unsigned int NUM_WORKERS = 1; /* Number of workers to launch */
unsigned int WORK_PREFERENCE[MAX_NUM_WORKERS] = {0};
unsigned int CORES_PER_WORKER[MAX_NUM_WORKERS] = {1}; /* Number of cores gwnum can use in computations */
int HYPERTHREAD_TF = 1; /* TRUE if trial factoring should use hyperthreads */
int HYPERTHREAD_LL = 0; /* TRUE if FFTs (LL, P-1, ECM, PRP) should use hyperthreads */
int MANUAL_COMM = 0;
int this_is_a_manual_comm = FALSE; /* Ugly hack to let comm thread know it was triggered by a user's manual comm */
float volatile CPU_WORKER_DISK_SPACE = 6.0;
unsigned int volatile CPU_HOURS = 0;
int CLASSIC_OUTPUT = 0;
int OUTPUT_ROUNDOFF = 0;
unsigned long volatile ITER_OUTPUT = 0;
unsigned long volatile ITER_OUTPUT_RES = 999999999;
unsigned long volatile DISK_WRITE_TIME = 30;
unsigned long volatile JACOBI_TIME = 12; /* Run a Jacobi test every 12 hours */
unsigned int MODEM_RETRY_TIME = 2;
unsigned int NETWORK_RETRY_TIME = 70;
float DAYS_BETWEEN_CHECKINS = 1.0;
int NUM_BACKUP_FILES = 3;
int NUM_JACOBI_BACKUP_FILES = 2;
int SILENT_VICTORY = 0;
int SILENT_VICTORY_PRP = 1;
int RUN_ON_BATTERY = 1;
int BATTERY_PERCENT = 0;
int DEFEAT_POWER_SAVE = 1;
int TRAY_ICON = TRUE;
int HIDE_ICON = FALSE;
int MERGE_WINDOWS = 0; /* Flags indicating which MDI windows to merge together */
double UNOFFICIAL_CPU_SPEED = 0.0;
unsigned int ROLLING_AVERAGE = 0;
unsigned int PRECISION = 2;
int RDTSC_TIMING = 1;
int TIMESTAMPING = 1;
int TIMESTAMPING_UTC = 0;
int LOGTIMESTAMPING = 1;
int LOGTIMESTAMPING_UTC = 0;
int SCREENLOG = 0;
int CUMULATIVE_TIMING = 0;
int CUMULATIVE_ROUNDOFF = 1;
int SEQUENTIAL_WORK = -1;
int WELL_BEHAVED_WORK = 0;
unsigned long INTERIM_FILES = 0;
unsigned long INTERIM_RESIDUES = 0;
unsigned long HYPERTHREADING_BACKOFF = 0;
int THROTTLE_PCT = 0;
int STARTUP_IN_PROGRESS = 0; /* True if displaying startup dialog boxes */
gwmutex OUTPUT_MUTEX; /* Lock for screen and results file access */
gwmutex LOG_MUTEX; /* Lock for prime.log access */
gwmutex WORKTODO_MUTEX; /* Lock for accessing worktodo structure */
gwevent AUTOBENCH_EVENT; /* Event to wake up workers after an auto-benchmark */
gwevent PROOF_UPLOAD_EVENT; /* Event to wake up proof uploader */
int LAUNCH_TYPE = 0; /* Type of workers launched */
unsigned int WORKERS_ACTIVE = 0; /* Number of workers running */
int WORKERS_STOPPING = 0; /* TRUE iff workers are stopping */
struct work_unit_array WORK_UNITS[MAX_NUM_WORKERS] = {0}; /* An array of work units for each worker */
unsigned int WORKTODO_COUNT = 0; /* Count of valid work lines */
unsigned int WORKTODO_IN_USE_COUNT = 0; /* Count of work units in use */
int WORKTODO_CHANGED = 0; /* Flag indicating worktodo file needs writing */
hwloc_topology_t hwloc_topology; /* Hardware topology */
uint32_t CPU_TOTAL_L1_CACHE_SIZE = 0; /* Sum of all the L1 caches in KB as determined by hwloc */
uint32_t CPU_TOTAL_L2_CACHE_SIZE = 0; /* Sum of all the L2 caches in KB as determined by hwloc */
uint32_t CPU_TOTAL_L3_CACHE_SIZE = 0; /* Sum of all the L3 caches in KB as determined by hwloc */
uint32_t CPU_TOTAL_L4_CACHE_SIZE = 0; /* Sum of all the L4 caches in KB as determined by hwloc */
uint32_t CPU_NUM_L1_CACHES = 0; /* Number of L1 caches as determined by hwloc */
uint32_t CPU_NUM_L2_CACHES = 0; /* Number of L2 caches as determined by hwloc */
uint32_t CPU_NUM_L3_CACHES = 0; /* Number of L3 caches as determined by hwloc */
uint32_t CPU_NUM_L4_CACHES = 0; /* Number of L4 caches as determined by hwloc */
int CPU_L2_CACHE_INCLUSIVE = -1; /* 1 if inclusive, 0 if exclusive, -1 if not known */
int CPU_L3_CACHE_INCLUSIVE = -1; /* 1 if inclusive, 0 if exclusive, -1 if not known */
int CPU_L4_CACHE_INCLUSIVE = -1; /* 1 if inclusive, 0 if exclusive, -1 if not known */
int OS_CAN_SET_AFFINITY = 1; /* hwloc supports setting CPU affinity (known exception is Apple) */
/* New in 30.7, HW_ globals to describe the underlying hardware. Earlier versions of prime95 did not deal well with asymmetric cores/threads/caches. */
uint32_t HW_NUM_CORES; /* Total number of cores (physical processors) available */
uint32_t HW_NUM_THREADS; /* Total number of threads (logical processors) available */
uint32_t HW_NUM_COMPUTE_CORES; /* Number of cores that a program like ours should use (i.e. not Alder Lake efficiency cores) */
uint32_t HW_NUM_THREADING_NODES; /* Total number of nodes where it should be beneficial to assign a worker's cores within the same node */
uint32_t HW_NUM_COMPUTE_THREADING_NODES;/* Same as HW_NUM_THREADING_NODES but only counting nodes governing compute cores */
uint32_t HW_NUM_NUMA_NODES; /* Total number of NUMA nodes in the computer */
struct hw_core_info *HW_CORES = NULL; /* Information on every core */
/* INI section and settings strings */
const char KEY_NumWorkers[] = "NumWorkers";
const char KEY_QuitGIMPS[] = "QuitGIMPS";
const char SEC_Internals[] = "Internals";
const char KEY_V30OptionsConverted[] = "V30OptionsConverted";
const char KEY_OldCpuSpeed[] = "OldCpuSpeed";
const char KEY_NewCpuSpeed[] = "NewCpuSpeed";
const char KEY_NewCpuSpeedCount[] = "NewCpuSpeedCount";
const char KEY_RollingAverage[] = "RollingAverage";
const char KEY_RollingHash[] = "RollingHash";
const char KEY_RollingStartTime[] = "RollingStartTime";
const char KEY_RollingCompleteTime[] = "RollingCompleteTime";
const char KEY_CertDailyMBRemaining[] = "CertDailyMBRemaining";
const char KEY_CertDailyCPURemaining[] = "CertDailyCPURemaining";
const char KEY_CertDailyRemainingLastUpdate[] = "CertDailyRemainingLastUpdate";
const char KEY_Pid[] = "Pid";
const char KEY_SrvrUID[] = "SrvrUID";
const char KEY_SrvrComputerName[] = "SrvrComputerName";
const char KEY_SrvrP00[] = "SrvrP00";
const char KEY_SrvrPO1[] = "SrvrPO1";
const char KEY_SrvrPO2[] = "SrvrPO2";
const char KEY_SrvrPO3[] = "SrvrPO3";
const char KEY_SrvrPO4[] = "SrvrPO4";
const char KEY_SrvrPO5[] = "SrvrPO5";
const char KEY_SrvrPO6[] = "SrvrPO6";
const char KEY_SrvrPO7[] = "SrvrPO7";
const char KEY_SrvrPO8[] = "SrvrPO8";
const char KEY_SrvrPO9[] = "SrvrPO9";
const char KEY_LastEndDatesSent[] = "LastEndDatesSent";
const char KEY_WGUID_version[] = "WGUID_version";
const char KEY_CertErrorCount[] = "CertErrorCount";
const char SEC_PrimeNet[] = "PrimeNet";
const char KEY_DialUp[] = "DialUp";
const char KEY_ProxyHost[] = "ProxyHost";
const char KEY_ProxyUser[] = "ProxyUser";
const char KEY_ProxyPass[] = "ProxyPass";
const char KEY_ProxyMask[] = "ProxyMask";
const char KEY_Debug[] = "Debug";
const char KEY_UploadRateLimit[] = "UploadRateLimit";
const char KEY_UploadStartTime[] = "UploadStartTime";
const char KEY_UploadEndTime[] = "UploadEndTime";
const char KEY_DownloadDailyLimit[] = "DownloadDailyLimit";
const char KEY_DownloadRateLimit[] = "DownloadRateLimit";
const char KEY_ProofUploads[] = "ProofUploads";
const char KEY_ProofHashLength[] = "ProofHashLength";
const char KEY_ProofPower[] = "ProofPower";
const char KEY_ProofPowerMult[] = "ProofPowerMult";
const char KEY_UploadChunkSize[] = "UploadChunkSize";
const char SEC_Windows[] = "Windows";
const char KEY_MergeWindows[] = "MergeWindows";
const char KEY_TrayIcon[] = "TrayIcon";
const char KEY_HideIcon[] = "HideIcon";
const char KEY_Left[] = "Left";
const char KEY_Right[] = "Right";
const char KEY_Top[] = "Top";
const char KEY_Bottom[] = "Bottom";
const char KEY_ExitOnX[] = "ExitOnX";
/* Forward declarations */
int parseWorkToDoLine (char *line, struct work_unit *w);
/* Generate the application string. This is sent to the server in a */
/* UC (Update Computer info) call. It is also displayed in the Help/About dialog box. */
void generate_application_string (
char *app_string)
{
#ifdef SECURITY_MODULES_PRESENT
sprintf (app_string, "%s,Prime95,v%s,build %s",
#else
sprintf (app_string, "%s,Untrusted Prime95,v%s,build %s",
#endif
PORT == 1 ? "Windows" :
PORT == 2 ? "Linux" :
PORT == 4 ? "Windows64" :
PORT == 5 ? "WindowsService" :
PORT == 6 ? "FreeBSD" :
PORT == 7 ? "OS/2" :
PORT == 8 ? "Linux64" :
PORT == 9 ? "Mac OS X" :
PORT == 10 ? "Mac OS X 64-bit" :
PORT == 11 ? "Haiku" :
PORT == 12 ? "FreeBSD 64-bit" : "Unknown",
VERSION, BUILD_NUM);
}
/* Calculate the 32-byte hex string for the hardware GUID. We use the */
/* output of the CPUID function to generate this. We don't include the */
/* cache size information because when a new processor comes out the CPUID */
/* does not recognize the cache size. When a new version of prime95 is */
/* released that does recognize the cache size a different hardware GUID */
/* would be generated. */
void calc_hardware_guid (void)
{
char buf[500];
sprintf (buf, "%s%d", CPU_BRAND, CPU_SIGNATURE);
md5_hexdigest_string (HARDWARE_GUID, buf);
/* Sometimes a user might want to run the program on several machines. */
/* Typically this is done by carrying the program and files around on a */
/* portable media such as a USB memory stick. In this case, */
/* we need to defeat the code that automatically detects hardware changes. */
/* The FixedHardwareUID INI option tells us to get the Windows and */
/* hardware hashes from the INI file rather than calculating them. */
if (IniGetInt (INI_FILE, "FixedHardwareUID", 0)) {
IniGetString (INI_FILE, "HardwareGUID", HARDWARE_GUID, sizeof (HARDWARE_GUID), HARDWARE_GUID);
IniWriteString (INI_FILE, "HardwareGUID", HARDWARE_GUID);
}
}
/* Calculate the 32-byte hex string for the Windows-only GUID. For */
/* non-Windows systems, we set WINDOWS_GUID to "". */
/* NOTE: In version 25.7 and earlier we used our first attempt at */
/* generating a unique ID. In version 25.8 and later we use a more */
/* robust method of getting the serial number and SID. We call the */
/* older code for all computer GUIDs that were generated by 25.7 */
void calc_windows_guid (void)
{
#ifdef _WINDOWS_
int algorithm_version;
char buf[500];
algorithm_version = IniSectionGetInt (INI_FILE, SEC_Internals, KEY_WGUID_version, 1);
if (algorithm_version == 1) {
getWindowsSerialNumber (buf);
getWindowsSID (buf + strlen (buf));
} else {
getWindowsSerialNumber_2 (buf);
getWindowsSID_2 (buf + strlen (buf));
}
md5_hexdigest_string (WINDOWS_GUID, buf);
#else
WINDOWS_GUID[0] = 0;
#endif
/* Sometimes a user might want to run the program on several machines. */
/* Typically this is done by carrying the program and files around on a */
/* portable media such as a USB memory stick. In this case, */
/* we need to defeat the code that automatically detects hardware changes. */
/* The FixedHardwareUID INI option tells us to get the Windows and */
/* hardware hashes from the INI file rather than calculating them. */
/* NOTE: In a dual boot situation where Linux has already written out */
/* an empty WindowsGUID, then this code will write out the non-empty */
/* WindowsGUID when run on a Windows machine. The server must not */
/* generate a CPU_IDENTITY_MISMATCH error in this case. */
if (IniGetInt (INI_FILE, "FixedHardwareUID", 0)) {
IniGetString (INI_FILE, "WindowsGUID", WINDOWS_GUID, sizeof (WINDOWS_GUID), WINDOWS_GUID);
IniWriteString (INI_FILE, "WindowsGUID", WINDOWS_GUID);
}
}
/* Clear cached program options. This is done when the server requests */
/* all program options to be resent. This is a fail-safe that lets the */
/* client and server resync if the server detects an inconsistency (like */
/* getting an assignment for worker #2 with num_workers = 1 */
void clearCachedProgramOptions (void)
{
int tnum;
char section_name[32];
IniSectionWriteString (INI_FILE, SEC_Internals, KEY_SrvrPO1, NULL);
for (tnum = 0; tnum < MAX_NUM_WORKERS; tnum++) {
sprintf (section_name, "Worker #%d", tnum+1);
IniSectionWriteString (INI_FILE, section_name, KEY_SrvrPO1, NULL);
}
IniSectionWriteString (INI_FILE, SEC_Internals, KEY_SrvrPO2, NULL);
IniSectionWriteString (INI_FILE, SEC_Internals, KEY_SrvrPO3, NULL);
IniSectionWriteString (INI_FILE, SEC_Internals, KEY_SrvrPO4, NULL);
IniSectionWriteString (INI_FILE, SEC_Internals, KEY_SrvrPO5, NULL);
IniSectionWriteString (INI_FILE, SEC_Internals, KEY_SrvrPO6, NULL);
IniSectionWriteString (INI_FILE, SEC_Internals, KEY_SrvrPO7, NULL);
IniSectionWriteString (INI_FILE, SEC_Internals, KEY_SrvrPO8, NULL);
IniSectionWriteString (INI_FILE, SEC_Internals, KEY_SrvrPO9, NULL);
}
/* Generate a globally unique ID for this computer. All Primenet */
/* communications are based on this value. */
void generate_computer_guid (void)
{
char buf[500];
time_t current_time;
time (¤t_time);
sprintf (buf, "%s%d%f%d", CPU_BRAND, CPU_SIGNATURE, CPU_SPEED, (int) current_time);
md5_hexdigest_string (COMPUTER_GUID, buf);
IniWriteString (INI_FILE, "ComputerGUID", COMPUTER_GUID);
/* Clear out local copies of what we think the server knows about this computer */
/* The server now knows nothing about this computer because of the newly generated computer ID */
IniSectionWriteString (INI_FILE, SEC_Internals, KEY_SrvrUID, NULL);
IniSectionWriteString (INI_FILE, SEC_Internals, KEY_SrvrComputerName, NULL);
clearCachedProgramOptions ();
/* Since we're generating a new computer GUID, we can use the latest, */
/* most robust version of calculating the Windows GUID. */
IniSectionWriteInt (INI_FILE, SEC_Internals, KEY_WGUID_version, 2);
calc_windows_guid ();
}
/* Determine the CPU speed either empirically or by user overrides. */
/* getCpuType must be called prior to calling this routine. */
void getCpuSpeed (void)
{
int temp, old_cpu_speed, report_new_cpu_speed;
/* Guess the CPU speed using the RDTSC instruction */
guessCpuSpeed ();
/* Now let the user override the cpu speed from the prime.txt file */
temp = IniGetInt (INI_FILE, "CpuSpeed", 99);
if (temp != 99) CPU_SPEED = temp;
/* Make sure the cpu speed is reasonable */
if (CPU_SPEED > 50000) CPU_SPEED = 50000;
if (CPU_SPEED < 25) CPU_SPEED = 25;
/* Set the unofficial CPU speed. The unofficial CPU speed is the */
/* last CPU speed measurement. The official CPU speed is the one */
/* reported to the server. */
UNOFFICIAL_CPU_SPEED = CPU_SPEED;
/* If CPU speed is much less than the official CPU speed, then set a new */
/* official CPU speed only after several slower measurements. */
/* The reason for this is that erroneously (due to one aberrant CPU speed */
/* calculation) reducing the speed we report to the server may result */
/* in erroneously unreserving exponents. */
report_new_cpu_speed = FALSE;
old_cpu_speed = IniSectionGetInt (INI_FILE, SEC_Internals, KEY_OldCpuSpeed, 0);
if (CPU_SPEED < (double) old_cpu_speed * 0.97) {
if (IniSectionGetInt (INI_FILE, SEC_Internals, KEY_NewCpuSpeedCount, 0) <= 5) {
if (CPU_SPEED > (double) IniSectionGetInt (INI_FILE, SEC_Internals, KEY_NewCpuSpeed, 0))
IniSectionWriteInt (INI_FILE, SEC_Internals, KEY_NewCpuSpeed, (int) (CPU_SPEED + 0.5));
IniSectionWriteInt (INI_FILE, SEC_Internals, KEY_NewCpuSpeedCount, IniSectionGetInt (INI_FILE, SEC_Internals, KEY_NewCpuSpeedCount, 0) + 1);
CPU_SPEED = old_cpu_speed;
} else {
if (CPU_SPEED < (double) IniSectionGetInt (INI_FILE, SEC_Internals, KEY_NewCpuSpeed, 0))
CPU_SPEED = (double) IniSectionGetInt (INI_FILE, SEC_Internals, KEY_NewCpuSpeed, 0);
report_new_cpu_speed = TRUE;
}
}
/* If CPU speed is close to last reported CPU speed, then use it. */
/* tell the server, recalculate new completion dates, and reset the */
/* rolling average. Don't do this on the first run (before the Welcome */
/* dialog has been displayed). */
else if (CPU_SPEED < (double) old_cpu_speed * 1.03) {
IniSectionWriteInt (INI_FILE, SEC_Internals, KEY_NewCpuSpeedCount, 0);
IniSectionWriteInt (INI_FILE, SEC_Internals, KEY_NewCpuSpeed, 0);
}
/* If CPU speed is much larger than the speed reported to the server, then */
/* use this new speed and tell the server. */
else {
report_new_cpu_speed = TRUE;
}
/* Report a new CPU speed. Remember the new CPU speed, tell the server, */
/* recalculate new completion dates, and reset the rolling average in */
/* such a way as to reduce the chance of spurious unreserves. Don't */
/* do this on the first run (before the Welcome dialog has been displayed). */
if (report_new_cpu_speed) {
IniSectionWriteInt (INI_FILE, SEC_Internals, KEY_OldCpuSpeed, (int) (CPU_SPEED + 0.5));
IniSectionWriteInt (INI_FILE, SEC_Internals, KEY_NewCpuSpeedCount, 0);
IniSectionWriteInt (INI_FILE, SEC_Internals, KEY_NewCpuSpeed, 0);
if (old_cpu_speed) {
if (WORKTODO_COUNT) {
ROLLING_AVERAGE = (int) (ROLLING_AVERAGE * old_cpu_speed / CPU_SPEED);
if (ROLLING_AVERAGE < 1000) ROLLING_AVERAGE = 1000;
}
else
ROLLING_AVERAGE = 1000;
IniSectionWriteInt (INI_FILE, SEC_Internals, KEY_RollingAverage, ROLLING_AVERAGE);
IniSectionWriteInt (INI_FILE, SEC_Internals, KEY_RollingStartTime, 0);
spoolMessage (PRIMENET_UPDATE_COMPUTER_INFO, NULL);
UpdateEndDates ();
}
}
}
/* Internal routine to calculate the number of "threading nodes" in a cpu set. A "threading node" represents a group of cores that we prefer to */
/* assign to a single worker. For example, there may well be a performance penalty if a worker's threads are on two different physical CPUS or */
/* access data from two different L3 caches. */
// NOTE: We should beef up prime95's understanding of threading nodes. There really should be a heirarchy of threading nodes.
// Consider an 8/8 Alder Lake CPU. The 8 performance cores share form a threading node, the 8 efficiency cores form another threading
// node that is comprised of 2 subnodes -- there are two L2 caches serving 4 cores each.
uint32_t calc_threading_nodes_for_cpuset (hwloc_bitmap_t cpuset)
{
int num_cores;
int num_objs;
int num_threading_nodes = 1;
num_cores = hwloc_get_nbobjs_inside_cpuset_by_type (hwloc_topology, cpuset, HWLOC_OBJ_CORE);
num_objs = hwloc_get_nbobjs_inside_cpuset_by_type (hwloc_topology, cpuset, HWLOC_OBJ_PACKAGE);
if (num_objs < num_cores && num_objs > num_threading_nodes) num_threading_nodes = num_objs;
num_objs = hwloc_get_nbobjs_inside_cpuset_by_type (hwloc_topology, cpuset, HWLOC_OBJ_NUMANODE);
if (num_objs < num_cores && num_objs > num_threading_nodes) num_threading_nodes = num_objs;
num_objs = hwloc_get_nbobjs_inside_cpuset_by_type (hwloc_topology, cpuset, HWLOC_OBJ_L4CACHE);
if (num_objs < num_cores && num_objs > num_threading_nodes) num_threading_nodes = num_objs;
num_objs = hwloc_get_nbobjs_inside_cpuset_by_type (hwloc_topology, cpuset, HWLOC_OBJ_L3CACHE);
if (num_objs < num_cores && num_objs > num_threading_nodes) num_threading_nodes = num_objs;
num_objs = hwloc_get_nbobjs_inside_cpuset_by_type (hwloc_topology, cpuset, HWLOC_OBJ_L2CACHE);
if (num_objs < num_cores && num_objs > num_threading_nodes) num_threading_nodes = num_objs;
return (num_threading_nodes);
}
/* Set the CPU flags based on the CPUID instruction. Also, the advanced */
/* user can override our guesses. */
void getCpuInfo (void)
{
int depth, i, temp;
uint32_t core;
/* Get the CPU info using CPUID instruction */
guessCpuType ();
/* New in version 29! Use hwloc info to determine HW_NUM_CORES and HW_NUM_THREADS. Also get number of NUMA nodes */
/* which we may use later on to allocate memory from the proper NUMA node. */
/* We still allow overriding these settings using the INI file. */
HW_NUM_CORES = hwloc_get_nbobjs_by_type (hwloc_topology, HWLOC_OBJ_CORE);
if (HW_NUM_CORES < 1) HW_NUM_CORES = hwloc_get_nbobjs_by_type (hwloc_topology, HWLOC_OBJ_PU);
if (HW_NUM_CORES < 1) HW_NUM_CORES = 1; // Shouldn't happen
HW_NUM_THREADS = hwloc_get_nbobjs_by_type (hwloc_topology, HWLOC_OBJ_PU);
if (HW_NUM_THREADS < 1) HW_NUM_THREADS = 1; // Shouldn't happen
HW_NUM_NUMA_NODES = hwloc_get_nbobjs_by_type (hwloc_topology, HWLOC_OBJ_NUMANODE);
if (HW_NUM_NUMA_NODES < 1) HW_NUM_NUMA_NODES = 1;
/* Allow overriding the hwloc generated values for number of physical processors and NUMA nodes. */
HW_NUM_CORES = IniGetInt (INI_FILE, "NumCPUs", HW_NUM_CORES);
HW_NUM_CORES = IniGetInt (INI_FILE, "NumCores", HW_NUM_CORES);
if (HW_NUM_CORES < 1) HW_NUM_CORES = 1;
HW_NUM_THREADS = IniGetInt (INI_FILE, "NumThreads", HW_NUM_THREADS);
if (HW_NUM_THREADS < 1) HW_NUM_THREADS = 1;
HW_NUM_NUMA_NODES = IniGetInt (INI_FILE, "NumNUMANodes", HW_NUM_NUMA_NODES);
if (HW_NUM_NUMA_NODES == 0) HW_NUM_NUMA_NODES = 1;
/* Create a structure to describe each physical core. Initialize as performance cores with the appropriate number of hyperthreads. */
free (HW_CORES);
HW_CORES = (struct hw_core_info *) malloc (HW_NUM_CORES * sizeof (struct hw_core_info));
for (core = 0; core < HW_NUM_CORES; core++) {
HW_CORES[core].ranking = 1; // Mark as a performance core
// Calculate number of threads
int num_threads = 1; // Default to one thread (no hyperthreading)
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) num_threads = hwloc_get_nbobjs_inside_cpuset_by_type (hwloc_topology, obj->cpuset, HWLOC_OBJ_PU);
// Let user override calculated number of threads
char key[50];
sprintf (key, "Core%" PRIu32 "NumThreads", core);
num_threads = IniGetInt (INI_FILE, key, num_threads);
if (num_threads < 1) num_threads = 1;
HW_CORES[core].num_threads = (uint16_t) num_threads;
}
/* New in version 29.5, get L1/L2/L3/L4 total cache size for use in determining torture test FFT sizes. */
/* Overwrite cpuid's linesize and associativity with hwloc's */
CPU_TOTAL_L1_CACHE_SIZE = CPU_NUM_L1_CACHES = 0;
CPU_TOTAL_L2_CACHE_SIZE = CPU_NUM_L2_CACHES = 0;
CPU_TOTAL_L3_CACHE_SIZE = CPU_NUM_L3_CACHES = 0;
CPU_TOTAL_L4_CACHE_SIZE = CPU_NUM_L4_CACHES = 0;
CPU_L2_CACHE_INCLUSIVE = -1;
CPU_L3_CACHE_INCLUSIVE = -1;
CPU_L4_CACHE_INCLUSIVE = -1;
for (depth = 0; depth < hwloc_topology_get_depth (hwloc_topology); depth++) {
for (i = 0; i < (int) hwloc_get_nbobjs_by_depth (hwloc_topology, depth); i++) {
hwloc_obj_t obj;
const char *inclusive;
obj = hwloc_get_obj_by_depth (hwloc_topology, depth, i);
if (obj == NULL || obj->attr == NULL) break; // can't happen
if (obj->type == HWLOC_OBJ_L1CACHE) {
CPU_TOTAL_L1_CACHE_SIZE += (uint32_t) (obj->attr->cache.size >> 10);
CPU_NUM_L1_CACHES++;
if (obj->attr->cache.linesize > 0) CPU_L1_CACHE_LINE_SIZE = obj->attr->cache.linesize;
if (obj->attr->cache.associativity > 0) CPU_L1_SET_ASSOCIATIVE = obj->attr->cache.associativity;
}
else if (obj->type == HWLOC_OBJ_L2CACHE) {
CPU_TOTAL_L2_CACHE_SIZE += (uint32_t) (obj->attr->cache.size >> 10);
CPU_NUM_L2_CACHES++;
if (obj->attr->cache.linesize > 0) CPU_L2_CACHE_LINE_SIZE = obj->attr->cache.linesize;
if (obj->attr->cache.associativity > 0) CPU_L2_SET_ASSOCIATIVE = obj->attr->cache.associativity;
inclusive = hwloc_obj_get_info_by_name (obj, "Inclusive");
if (inclusive != NULL) CPU_L2_CACHE_INCLUSIVE = atoi (inclusive);
}
else if (obj->type == HWLOC_OBJ_L3CACHE) {
CPU_TOTAL_L3_CACHE_SIZE += (uint32_t) (obj->attr->cache.size >> 10);
CPU_NUM_L3_CACHES++;
if (obj->attr->cache.linesize > 0) CPU_L3_CACHE_LINE_SIZE = obj->attr->cache.linesize;
if (obj->attr->cache.associativity > 0) CPU_L3_SET_ASSOCIATIVE = obj->attr->cache.associativity;
inclusive = hwloc_obj_get_info_by_name (obj, "Inclusive");
if (inclusive != NULL) CPU_L3_CACHE_INCLUSIVE = atoi (inclusive);
}
else if (obj->type == HWLOC_OBJ_L4CACHE) {
CPU_TOTAL_L4_CACHE_SIZE += (uint32_t) (obj->attr->cache.size >> 10);
CPU_NUM_L4_CACHES++;
inclusive = hwloc_obj_get_info_by_name (obj, "Inclusive");
if (inclusive != NULL) CPU_L4_CACHE_INCLUSIVE = atoi (inclusive);
}
}
}
/* Overwrite the cache info calculated via CPUID as hwloc's info is more detailed and I believe more reliable. */
/* We are transitioning away from using the cache size global variables computed by the CPUID code. */
if (CPU_NUM_L1_CACHES) CPU_L1_CACHE_SIZE = CPU_TOTAL_L1_CACHE_SIZE / CPU_NUM_L1_CACHES;
if (CPU_NUM_L2_CACHES) CPU_L2_CACHE_SIZE = CPU_TOTAL_L2_CACHE_SIZE / CPU_NUM_L2_CACHES;
if (CPU_NUM_L3_CACHES) CPU_L3_CACHE_SIZE = CPU_TOTAL_L3_CACHE_SIZE / CPU_NUM_L3_CACHES;
/* If hwloc could not figure out the cache sizes, use the cache sizes as determined by CPUID. */
/* Note that the CPUID code in gwnum is not good at determining the number of L2 and L3 caches. */
/* Fortunately, it should be rare that we rely on the CPUID code. */
if (CPU_NUM_L1_CACHES == 0 && CPU_L1_CACHE_SIZE > 0) CPU_TOTAL_L1_CACHE_SIZE = CPU_L1_CACHE_SIZE * HW_NUM_CORES, CPU_NUM_L1_CACHES = HW_NUM_CORES;
if (CPU_NUM_L2_CACHES == 0 && CPU_L2_CACHE_SIZE > 0) CPU_TOTAL_L2_CACHE_SIZE = CPU_L2_CACHE_SIZE, CPU_NUM_L2_CACHES = 1;
if (CPU_NUM_L3_CACHES == 0 && CPU_L3_CACHE_SIZE > 0) CPU_TOTAL_L3_CACHE_SIZE = CPU_L3_CACHE_SIZE, CPU_NUM_L3_CACHES = 1;
/* New in version 30.7. Get the number of CPU kinds, figure out which are the efficiency cores. */
int kind_verbosity = IniGetInt (INI_FILE, "KindVerbosity", 0);
int cpu_kinds = hwloc_cpukinds_get_nr (hwloc_topology, 0);
if (kind_verbosity) {
char buf[200];
sprintf (buf, "Hwloc %d kinds\n", cpu_kinds);
writeResultsBench (buf);
}
HW_NUM_THREADING_NODES = 0;
HW_NUM_COMPUTE_THREADING_NODES = 0;
hwloc_bitmap_t cpuset = hwloc_bitmap_alloc ();
for (int i = 0; i < cpu_kinds; i++) {
int efficiency;
unsigned num_infos;
struct hwloc_info_s *infos;
uint32_t num_threading_nodes;
// Get info on this cpu kind as well as which cpu cores are of this cpu kind
if (hwloc_cpukinds_get_info (hwloc_topology, i, cpuset, &efficiency, &num_infos, &infos, 0) < 0) continue;
if (kind_verbosity) {
char buf[200], str[80];
hwloc_bitmap_snprintf (str, sizeof (str), cpuset);
sprintf (buf, "Hwloc kind %d: Efficiency: %d, Cpuset: %s\n", i, efficiency, str);
writeResultsBench (buf);
}
// Try and compute the number of "threading nodes" for this cpu kind.
num_threading_nodes = calc_threading_nodes_for_cpuset (cpuset);
HW_NUM_THREADING_NODES += num_threading_nodes;
// Efficiency values are not set (-1), (0) most power efficient, or (1+) performance
// We earlier marked all cores as performance cores. We only need to change things if there are multiple cpu_kinds
// and we encounter efficiency == 0.
if (cpu_kinds == 1 || efficiency != 0) {
HW_NUM_COMPUTE_THREADING_NODES += num_threading_nodes;
continue;
}
// Look at all HW_CORE structures to see which need changing
for (core = 0; core < HW_NUM_CORES; core++) {
hwloc_obj_t obj;
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 && hwloc_bitmap_isincluded (obj->cpuset, cpuset))
HW_CORES[core].ranking = 0; // Mark this core as an efficiency core
}
}
hwloc_bitmap_free (cpuset);
// Let user override efficiency rankings
for (core = 0; core < HW_NUM_CORES; core++) {
char key[50];
sprintf (key, "Core%" PRIu32 "Ranking", core);
HW_CORES[core].ranking = (uint16_t) IniGetInt (INI_FILE, key, HW_CORES[core].ranking);
}
/* Calculate the number of compute cores (as opposed to efficiency cores) */
HW_NUM_COMPUTE_CORES = 0;
for (core = 0; core < HW_NUM_CORES; core++) if (HW_CORES[core].ranking >= 1) HW_NUM_COMPUTE_CORES++;
ASSERTG (HW_NUM_COMPUTE_CORES > 0);
/* Calculate hardware GUID (global unique identifier) using the CPUID info. */
/* Well, it isn't unique but it is about as good as we can do and still have */
/* portable code. Do this calculation before user overrides values */
/* derived from CPUID results. */
calc_hardware_guid ();
/* Let the user override the cpu flags from the prime.txt file */
temp = IniGetInt (INI_FILE, "CpuSupportsRDTSC", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_RDTSC;
if (temp == 1) CPU_FLAGS |= CPU_RDTSC;
temp = IniGetInt (INI_FILE, "CpuSupportsCMOV", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_CMOV;
if (temp == 1) CPU_FLAGS |= CPU_CMOV;
temp = IniGetInt (INI_FILE, "CpuSupportsPrefetch", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_PREFETCH;
if (temp == 1) CPU_FLAGS |= CPU_PREFETCH;
temp = IniGetInt (INI_FILE, "CpuSupportsPrefetchw", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_PREFETCHW;
if (temp == 1) CPU_FLAGS |= CPU_PREFETCHW;
temp = IniGetInt (INI_FILE, "CpuSupportsPrefetchwt1", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_PREFETCHWT1;
if (temp == 1) CPU_FLAGS |= CPU_PREFETCHWT1;
temp = IniGetInt (INI_FILE, "CpuSupportsSSE", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_SSE;
if (temp == 1) CPU_FLAGS |= CPU_SSE;
temp = IniGetInt (INI_FILE, "CpuSupportsSSE2", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_SSE2;
if (temp == 1) CPU_FLAGS |= CPU_SSE2;
temp = IniGetInt (INI_FILE, "CpuSupportsSSE4", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_SSE41;
if (temp == 1) CPU_FLAGS |= CPU_SSE41;
temp = IniGetInt (INI_FILE, "CpuSupports3DNow", 99);
if (temp == 0) CPU_FLAGS &= ~(CPU_3DNOW + CPU_3DNOW_PREFETCH);
if (temp == 1) CPU_FLAGS |= (CPU_3DNOW + CPU_3DNOW_PREFETCH);
temp = IniGetInt (INI_FILE, "CpuSupportsAVX", 99);
if (temp == 0) CPU_FLAGS &= ~(CPU_AVX | CPU_FMA3);
if (temp == 1) CPU_FLAGS |= CPU_AVX;
temp = IniGetInt (INI_FILE, "CpuSupportsFMA3", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_FMA3;
if (temp == 1) CPU_FLAGS |= (CPU_AVX | CPU_FMA3);
temp = IniGetInt (INI_FILE, "CpuSupportsFMA4", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_FMA4;
if (temp == 1) CPU_FLAGS |= CPU_FMA4;
temp = IniGetInt (INI_FILE, "CpuSupportsAVX2", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_AVX2;
if (temp == 1) CPU_FLAGS |= CPU_AVX2;
temp = IniGetInt (INI_FILE, "CpuSupportsAVX512F", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_AVX512F;
if (temp == 1) CPU_FLAGS |= CPU_AVX512F;
temp = IniGetInt (INI_FILE, "CpuSupportsAVX512DQ", 99);
if (temp == 0) CPU_FLAGS &= ~CPU_AVX512DQ;
if (temp == 1) CPU_FLAGS |= CPU_AVX512DQ;
/* Let the user override the L1/L2/L3/L4 cache size in prime.txt file */
CPU_TOTAL_L1_CACHE_SIZE = IniGetInt (INI_FILE, "CpuL1TotalCacheSize", CPU_TOTAL_L1_CACHE_SIZE);
CPU_NUM_L1_CACHES = IniGetInt (INI_FILE, "CpuL1NumCaches", CPU_NUM_L1_CACHES);
CPU_TOTAL_L2_CACHE_SIZE = IniGetInt (INI_FILE, "CpuL2TotalCacheSize", CPU_TOTAL_L2_CACHE_SIZE);
CPU_NUM_L2_CACHES = IniGetInt (INI_FILE, "CpuL2NumCaches", CPU_NUM_L2_CACHES);
CPU_L2_CACHE_SIZE = IniGetInt (INI_FILE, "CpuL2CacheSize", CPU_L2_CACHE_SIZE);
CPU_L2_CACHE_LINE_SIZE = IniGetInt (INI_FILE, "CpuL2CacheLineSize", CPU_L2_CACHE_LINE_SIZE);
CPU_L2_SET_ASSOCIATIVE = IniGetInt (INI_FILE, "CpuL2SetAssociative", CPU_L2_SET_ASSOCIATIVE);
CPU_L2_CACHE_INCLUSIVE = IniGetInt (INI_FILE, "CpuL2CacheInclusive", CPU_L2_CACHE_INCLUSIVE);
CPU_TOTAL_L3_CACHE_SIZE = IniGetInt (INI_FILE, "CpuL3TotalCacheSize", CPU_TOTAL_L3_CACHE_SIZE);
CPU_NUM_L3_CACHES = IniGetInt (INI_FILE, "CpuL3NumCaches", CPU_NUM_L3_CACHES);
CPU_L3_CACHE_SIZE = IniGetInt (INI_FILE, "CpuL3CacheSize", CPU_L3_CACHE_SIZE);
CPU_L3_CACHE_LINE_SIZE = IniGetInt (INI_FILE, "CpuL3CacheLineSize", CPU_L3_CACHE_LINE_SIZE);
CPU_L3_SET_ASSOCIATIVE = IniGetInt (INI_FILE, "CpuL3SetAssociative", CPU_L3_SET_ASSOCIATIVE);
CPU_L3_CACHE_INCLUSIVE = IniGetInt (INI_FILE, "CpuL3CacheInclusive", CPU_L3_CACHE_INCLUSIVE);
CPU_TOTAL_L4_CACHE_SIZE = IniGetInt (INI_FILE, "CpuL4TotalCacheSize", CPU_TOTAL_L4_CACHE_SIZE);
CPU_NUM_L4_CACHES = IniGetInt (INI_FILE, "CpuL4NumCaches", CPU_NUM_L4_CACHES);
CPU_L4_CACHE_INCLUSIVE = IniGetInt (INI_FILE, "CpuL4CacheInclusive", CPU_L4_CACHE_INCLUSIVE);
/* Let the user override the CPUID brand string. It should never be necessary. */
/* However, one Athlon owner's brand string became corrupted with illegal characters. */
IniGetString (INI_FILE, "CpuBrand", CPU_BRAND, sizeof(CPU_BRAND), CPU_BRAND);
/* Apply sane corrections if HW_NUM_COREs was reduced significantly) */
if (CPU_NUM_L1_CACHES > HW_NUM_CORES) CPU_NUM_L1_CACHES = HW_NUM_CORES;
if (CPU_NUM_L2_CACHES > HW_NUM_CORES) CPU_NUM_L2_CACHES = HW_NUM_CORES;
if (CPU_NUM_L3_CACHES > HW_NUM_CORES) CPU_NUM_L3_CACHES = HW_NUM_CORES;
if (CPU_NUM_L4_CACHES > HW_NUM_CORES) CPU_NUM_L4_CACHES = HW_NUM_CORES;
if (HW_NUM_NUMA_NODES > HW_NUM_CORES) HW_NUM_NUMA_NODES = HW_NUM_CORES;
/* Let user override the CPU architecture */
CPU_ARCHITECTURE = IniGetInt (INI_FILE, "CpuArchitecture", CPU_ARCHITECTURE);
/* Allow overriding the calculated number of threading nodes */
HW_NUM_THREADING_NODES = IniGetInt (INI_FILE, "NumThreadingNodes", HW_NUM_THREADING_NODES);
if (HW_NUM_THREADING_NODES < 1) HW_NUM_THREADING_NODES = 1;
HW_NUM_COMPUTE_THREADING_NODES = IniGetInt (INI_FILE, "NumComputeThreadingNodes", HW_NUM_COMPUTE_THREADING_NODES);
if (HW_NUM_COMPUTE_THREADING_NODES < 1) HW_NUM_COMPUTE_THREADING_NODES = 1;
/* Now get the CPU speed */
getCpuSpeed ();
}
/* Format a long or very long textual cpu description */
void getCpuDescription (
char *buf, /* A 512 byte buffer */
int long_desc) /* True for a very long description */
{
/* Recalculate the CPU speed in case speed step has changed the original settings. */
getCpuSpeed ();
/* Now format a pretty CPU description */
sprintf (buf, "%s\nCPU speed: %.2f MHz", CPU_BRAND, UNOFFICIAL_CPU_SPEED);
if (HW_NUM_CORES != HW_NUM_COMPUTE_CORES)
sprintf (buf + strlen (buf), ", %" PRIu32 "/%" PRIu32 " performance/efficiency cores", HW_NUM_COMPUTE_CORES, HW_NUM_CORES - HW_NUM_COMPUTE_CORES);
else if (HW_NUM_CORES > 1 && HW_NUM_CORES != HW_NUM_THREADS)
sprintf (buf + strlen (buf), ", %" PRIu32 " hyperthreaded cores", HW_NUM_CORES);
else if (HW_NUM_CORES > 1)
sprintf (buf + strlen (buf), ", %" PRIu32 " cores", HW_NUM_CORES);
else if (HW_NUM_CORES != HW_NUM_THREADS)
sprintf (buf + strlen (buf), ", with hyperthreading");
strcat (buf, "\n");
if (CPU_FLAGS) {
strcat (buf, "CPU features: ");
// if (CPU_FLAGS & CPU_RDTSC) strcat (buf, "RDTSC, ");
// if (CPU_FLAGS & CPU_CMOV) strcat (buf, "CMOV, ");
// if (CPU_FLAGS & CPU_MMX) strcat (buf, "MMX, ");
if (CPU_FLAGS & CPU_3DNOW) strcat (buf, "3DNow!, ");
else if (CPU_FLAGS & CPU_3DNOW_PREFETCH) strcat (buf, "3DNow! Prefetch, ");
else if (CPU_FLAGS & CPU_PREFETCHW) strcat (buf, "Prefetchw, ");
else if (CPU_FLAGS & CPU_PREFETCH) strcat (buf, "Prefetch, ");
if (CPU_FLAGS & CPU_SSE) strcat (buf, "SSE, ");
if (CPU_FLAGS & CPU_SSE2) strcat (buf, "SSE2, ");
if (CPU_FLAGS & CPU_SSE41) strcat (buf, "SSE4, ");
if (CPU_FLAGS & CPU_AVX) strcat (buf, "AVX, ");
if (CPU_FLAGS & CPU_AVX2) strcat (buf, "AVX2, ");
if (CPU_FLAGS & (CPU_FMA3 | CPU_FMA4)) strcat (buf, "FMA, ");
if (CPU_FLAGS & CPU_AVX512F) strcat (buf, "AVX512F, ");
strcpy (buf + strlen (buf) - 2, "\n");
}
strcat (buf, "L1 cache size: ");
if (CPU_NUM_L1_CACHES <= 0) strcat (buf, "unknown");
if (CPU_NUM_L1_CACHES > 1) sprintf (buf + strlen (buf), "%dx", CPU_NUM_L1_CACHES);
if (CPU_NUM_L1_CACHES >= 1) sprintf (buf + strlen (buf), "%d KB", CPU_TOTAL_L1_CACHE_SIZE / CPU_NUM_L1_CACHES);
strcat (buf, ", L2 cache size: ");
if (CPU_NUM_L2_CACHES <= 0) strcat (buf, "unknown");
if (CPU_NUM_L2_CACHES > 1) sprintf (buf + strlen (buf), "%dx", CPU_NUM_L2_CACHES);
if (CPU_NUM_L2_CACHES >= 1) {
if ((CPU_TOTAL_L2_CACHE_SIZE / CPU_NUM_L2_CACHES) & 0x3FF)
sprintf (buf + strlen (buf), "%d KB", CPU_TOTAL_L2_CACHE_SIZE / CPU_NUM_L2_CACHES);
else
sprintf (buf + strlen (buf), "%d MB", CPU_TOTAL_L2_CACHE_SIZE / CPU_NUM_L2_CACHES / 1024);
}
if (CPU_NUM_L3_CACHES > 0) {
strcat (buf, CPU_NUM_L4_CACHES > 0 ? "\n" : ", ");
strcat (buf, "L3 cache size: ");
if (CPU_NUM_L3_CACHES > 1) sprintf (buf + strlen (buf), "%dx", CPU_NUM_L3_CACHES);
if ((CPU_TOTAL_L3_CACHE_SIZE / CPU_NUM_L3_CACHES) & 0x3FF)
sprintf (buf + strlen (buf), "%d KB", CPU_TOTAL_L3_CACHE_SIZE / CPU_NUM_L3_CACHES);
else
sprintf (buf + strlen (buf), "%d MB", CPU_TOTAL_L3_CACHE_SIZE / CPU_NUM_L3_CACHES / 1024);
}
if (CPU_NUM_L4_CACHES > 0) {
strcat (buf, ", L4 cache size: ");
if (CPU_NUM_L4_CACHES > 1) sprintf (buf + strlen (buf), "%dx", CPU_NUM_L4_CACHES);
if ((CPU_TOTAL_L4_CACHE_SIZE / CPU_NUM_L4_CACHES) & 0x3FF)
sprintf (buf + strlen (buf), "%d KB", CPU_TOTAL_L4_CACHE_SIZE / CPU_NUM_L4_CACHES);
else
sprintf (buf + strlen (buf), "%d MB", CPU_TOTAL_L4_CACHE_SIZE / CPU_NUM_L4_CACHES / 1024);
}
if (! long_desc) return;
strcat (buf, "\nL1 cache line size: ");
if (CPU_L1_CACHE_LINE_SIZE < 0) strcat (buf, "unknown");
else sprintf (buf+strlen(buf), "%d bytes", CPU_L1_CACHE_LINE_SIZE);
strcat (buf, ", L2 cache line size: ");
if (CPU_L2_CACHE_LINE_SIZE < 0) strcat (buf, "unknown");
else sprintf (buf+strlen(buf), "%d bytes", CPU_L2_CACHE_LINE_SIZE);
strcat (buf, "\n");
}
/* Print the machine topology as discovered by hwloc library */
void topology_print_children (
hwloc_obj_t obj,
int depth)
{
char type[32], attr[1024], cpuset[256], buf[1500];
unsigned int i;
if (obj == NULL) return; // Shouldn't happen
hwloc_obj_type_snprintf (type, sizeof(type), obj, 0);
sprintf (buf, "%*s%s", 2*depth, " ", type);
if (obj->os_index != (unsigned) -1)
sprintf (buf+strlen(buf), "#%u", obj->os_index);
hwloc_obj_attr_snprintf (attr, sizeof(attr), obj, ", ", 1 /* verbose */);
if (obj->type == HWLOC_OBJ_CORE || obj->type == HWLOC_OBJ_PU)
hwloc_bitmap_snprintf (cpuset, sizeof(cpuset), obj->cpuset);
else
cpuset[0] = 0;
if (attr[0] && cpuset[0]) sprintf (buf+strlen(buf), " (%s, cpuset: %s)", attr, cpuset);
else if (attr[0]) sprintf (buf+strlen(buf), " (%s)", attr);
else if (cpuset[0]) sprintf (buf+strlen(buf), " (cpuset: %s)", cpuset);
strcat (buf, "\n");
writeResultsBench (buf);
for (i = 0; i < obj->arity; i++) {
topology_print_children (obj->children[i], depth + 1);
}
}
/* Determine if a number is prime */
int isPrime (
unsigned long p)
{
unsigned long i;
for (i = 2; i < 0xFFFF && i * i <= p; i = (i + 1) | 1)
if (p % i == 0) return (FALSE);
return (TRUE);
}
/* Routines that use a simple sieve to find "may be prime" numbers. That is, numbers without any small factors. */
/* This is used by ECM, P-1, and P+1. Also, used by 64-bit trial factoring setup code. */
typedef struct {
uint32_t *primes; // Sieving primes and the bit-to-clear
uint64_t first_number;
unsigned int bit_number;
unsigned int num_primes;
unsigned int num_elimination_primes;
unsigned int max_elimination;
uint64_t start;
char array[4096];
} sieve_info;
/* Internal routine to fill up the sieve array */
void fill_sieve (
sieve_info *si)
{
unsigned int i;
uint32_t fmax;
/* Determine the first sieve bit to clear for each small prime */
fmax = (uint32_t) sqrt ((double) (si->first_number + sizeof (si->array) * 8 * 2));
for (i = si->num_primes; i < si->num_elimination_primes * 2; i += 2) {
uint32_t f, r, bit;
f = si->primes[i];
if (f > fmax) break;
if (si->first_number == 3) {
bit = (f * f - 3) >> 1;
} else {
r = (uint32_t) (si->first_number % f);
if (r == 0) bit = 0;
else if (r & 1) bit = (f - r) / 2;
else bit = (f + f - r) / 2;
if (f == si->first_number + 2 * bit) bit += f;
}
si->primes[i+1] = bit;
}
si->num_primes = i;
/* Fill the sieve with ones, then zero out the composites */
memset (si->array, 0xFF, sizeof (si->array));
for (i = 0; i < si->num_primes; i += 2) {
uint32_t f, bit;
f = si->primes[i];
for (bit = si->primes[i+1]; bit < sizeof (si->array) * 8; bit += f) bitclr (si->array, bit);
si->primes[i+1] = bit - sizeof (si->array) * 8;
}
si->bit_number = 0;
}
/* Either: 1) Recycle a sieve_info structure using the same number of small primes, OR 2) Allocate a new sieve_info structure. */
/* In both cases, reset the sieve to start returning primes at the specified point. */
int start_sieve (
int thread_num,
uint64_t start,
void **si_to_recycle_or_returned_new_si) /* Recycled or returned sieving structure */
{
// Default sieve eliminates numbers with factors < 64K
return (start_sieve_with_limit (thread_num, start, 65536, si_to_recycle_or_returned_new_si));
}
int start_sieve_with_limit (
int thread_num,
uint64_t start, /* Starting point for the sieve */
uint32_t max_elimination_factor, /* Sieve eliminates composites with any factors less than this number */
void **si_to_recycle_or_returned_new_si) /* Returned sieving structure */
{
sieve_info *si;
unsigned int i;
/* Re-use or allocate the sieve structure */
if (*si_to_recycle_or_returned_new_si != NULL)
si = (sieve_info *) *si_to_recycle_or_returned_new_si;
else {
si = (sieve_info *) malloc (sizeof (sieve_info));
if (si == NULL) goto oom;
*si_to_recycle_or_returned_new_si = si;
memset (si, 0, sizeof (sieve_info));
}
/* Remember starting point (in case its 2) and make real start odd */
if (start < 2) start = 2;
si->start = start;
start |= 1;
/* Delete old small primes array if it is not big enough */
if (si->primes != NULL && max_elimination_factor > si->max_elimination) {
free (si->primes);
si->primes = NULL;
si->first_number = 0;
}
/* See if we can just reuse the existing sieve */
if (si->first_number && start >= si->first_number && start < si->first_number + sizeof (si->array) * 8 * 2) {
si->bit_number = (unsigned int) (start - si->first_number) / 2;
return (0);
}
/* Initialize sieving primes */
if (si->primes == NULL) {
uint32_t f;
unsigned int estimated_num_primes;
estimated_num_primes = (unsigned int) ((double) max_elimination_factor / (log ((double) max_elimination_factor) - 1.0) * 1.01);
si->primes = (uint32_t *) malloc (estimated_num_primes * 2 * sizeof (uint32_t));
if (si->primes == NULL) goto oom;
for (i = 0, f = 3; f <= max_elimination_factor && i < estimated_num_primes; f += 2)
if (isPrime (f)) si->primes[i*2] = f, i++;
si->num_elimination_primes = i;
si->max_elimination = max_elimination_factor;
}
si->first_number = start;
si->num_primes = 0;
fill_sieve (si);
return (0);
/* Out of memory exit path */