99
1010#include " ../../tests/test_helper/test_helper.hpp"
1111
12- #include < fcntl.h> // _O_WRONLY
13- #include < io.h> // _open, _dup, _dup2, _close
14-
15- #define WIN32_LEAN_AND_MEAN
16- #ifndef _WIN32_WINNT
17- #define _WIN32_WINNT 0x0A00 // Windows 10+
18- #endif
19- #include < algorithm>
20- #include < mutex>
21- #include < thread>
22- #include < unordered_set>
23- #include < windows.h>
24-
2512#include < cassert>
2613#include < fstream>
2714#include < stdexcept>
@@ -225,75 +212,40 @@ inline World get_kitchen_open_doors() {
225212 return world;
226213}
227214
228- // ------------------------------ Helper functions ------------------------------
229- struct IOSilencer {
230- int saved_stdout_fd = -1 ;
231- int null_fd = -1 ;
232- std::streambuf* old_cout = nullptr ;
233- std::streambuf* old_cerr = nullptr ;
234- std::ofstream null_stream;
235-
236- IOSilencer () {
237- // Flush buffers
238- std::cout.flush ();
239- std::cerr.flush ();
240- fflush (stdout);
241-
242- // Redirect std::cout and std::cerr to /dev/null
243- null_stream.open (" NUL" ); // Windows null device
244- old_cout = std::cout.rdbuf ();
245- old_cerr = std::cerr.rdbuf ();
246- std::cout.rdbuf (null_stream.rdbuf ());
247- std::cerr.rdbuf (null_stream.rdbuf ());
248-
249- // Redirect printf (C stdout)
250- saved_stdout_fd = _dup (_fileno (stdout));
251- null_fd = _open (" NUL" , _O_WRONLY);
252- _dup2 (null_fd, _fileno (stdout));
253- }
254-
255- ~IOSilencer () {
256- std::cout.rdbuf (old_cout);
257- std::cerr.rdbuf (old_cerr);
258- fflush (stdout);
259-
260- _dup2 (saved_stdout_fd, _fileno (stdout));
261- _close (null_fd);
262- _close (saved_stdout_fd);
263- }
264- };
265-
266- struct CpuRow {
267- ULONG id; // CpuSet.Id (stable handle for SetThreadSelectedCpuSets)
268- WORD group; // processor group
269- ULONG lpi; // LogicalProcessorIndex (info only)
270- ULONG core; // CoreIndex (same for SMT siblings)
271- BYTE eff; // EfficiencyClass (0 = most performant if hybrid)
272- bool ok; // Allocated & !Parked
273- };
215+ // ============================================================================
216+ // Cross-platform performance/thread helpers
217+ // ============================================================================
218+ #ifdef _WIN32
219+ #define WIN32_LEAN_AND_MEAN
220+ #ifndef _WIN32_WINNT
221+ #define _WIN32_WINNT 0x0A00
222+ #endif
223+ #include < algorithm>
224+ #include < iostream>
225+ #include < unordered_set>
226+ #include < vector>
227+ #include < windows.h>
274228
275229static std::vector<GROUP_AFFINITY > pick_physical_cores (size_t N) {
276230 std::vector<GROUP_AFFINITY > picks;
277231
278- // --- Preferred: CPU set API ---
279232 DWORD len = 0 ;
280- GetSystemCpuSetInformation (nullptr , 0 , &len, nullptr , 0 ); // note: nullptr process
233+ GetSystemCpuSetInformation (nullptr , 0 , &len, nullptr , 0 );
281234 if (GetLastError () == ERROR_INSUFFICIENT_BUFFER && len > 0 ) {
282235 std::vector<unsigned char > buf (len);
283236 if (GetSystemCpuSetInformation (reinterpret_cast <SYSTEM_CPU_SET_INFORMATION *>(buf.data ()),
284237 (ULONG ) buf.size (), &len, nullptr , 0 )) {
285238 std::unordered_set<ULONG > seen_core;
286239 BYTE min_eff = 255 , max_eff = 0 ;
287-
288- BYTE * p = buf.data ();
289- BYTE * end = p + len;
290240 struct Row {
291241 GROUP_AFFINITY ga;
292242 ULONG core;
293243 BYTE eff;
294244 };
295245 std::vector<Row> rows;
296246
247+ BYTE * p = buf.data ();
248+ BYTE * end = p + len;
297249 while (p < end) {
298250 auto * info = reinterpret_cast <SYSTEM_CPU_SET_INFORMATION *>(p);
299251 if (info->Type == CpuSetInformation) {
@@ -305,21 +257,17 @@ static std::vector<GROUP_AFFINITY> pick_physical_cores(size_t N) {
305257 r.core = c.CoreIndex ;
306258 r.eff = c.EfficiencyClass ;
307259 rows.push_back (r);
308- if (r.eff < min_eff)
309- min_eff = r.eff ;
310- if (r.eff > max_eff)
311- max_eff = r.eff ;
260+ min_eff = std::min (min_eff, r.eff );
261+ max_eff = std::max (max_eff, r.eff );
312262 }
313263 }
314264 p += info->Size ;
315265 }
316266
317267 if (!rows.empty ()) {
318268 bool hybrid = (min_eff != max_eff);
319- std::sort (rows.begin (), rows.end (),
320- [](const Row& a, const Row& b) { return a.core < b.core ; });
321-
322- for (const auto & r: rows) {
269+ std::sort (rows.begin (), rows.end (), [](auto & a, auto & b) { return a.core < b.core ; });
270+ for (auto & r: rows) {
323271 if (hybrid && r.eff != min_eff)
324272 continue ; // prefer P-cores
325273 if (seen_core.insert (r.core ).second ) {
@@ -332,53 +280,97 @@ static std::vector<GROUP_AFFINITY> pick_physical_cores(size_t N) {
332280 }
333281 }
334282
335- if (!picks.empty ())
336- return picks;
337-
338- // --- Fallback: LogicalProcessorInformationEx ---
339- DWORD bytes = 0 ;
340- GetLogicalProcessorInformationEx (RelationProcessorCore, nullptr , &bytes);
341- std::vector<unsigned char > buf2 (bytes);
342- if (GetLogicalProcessorInformationEx (RelationProcessorCore,
343- reinterpret_cast <PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX >(buf2.data ()), &bytes)) {
344-
345- BYTE * p2 = buf2.data ();
346- BYTE * end2 = p2 + bytes;
347- while (p2 < end2) {
348- auto * ex = reinterpret_cast <PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX >(p2);
349- if (ex->Relationship == RelationProcessorCore) {
350- const GROUP_AFFINITY & g = ex->Processor .GroupMask [0 ];
351- if (g.Mask ) {
352- KAFFINITY first = g.Mask & (~g.Mask + 1 );
353- GROUP_AFFINITY ga{};
354- ga.Group = g.Group ;
355- ga.Mask = first;
356- picks.push_back (ga);
357- if (picks.size () == N)
358- break ;
283+ // fallback if empty
284+ if (picks.empty ()) {
285+ DWORD bytes = 0 ;
286+ GetLogicalProcessorInformationEx (RelationProcessorCore, nullptr , &bytes);
287+ std::vector<unsigned char > buf2 (bytes);
288+ if (GetLogicalProcessorInformationEx (RelationProcessorCore,
289+ reinterpret_cast <PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX >(buf2.data ()), &bytes)) {
290+ BYTE * p = buf2.data ();
291+ BYTE * end = p + bytes;
292+ while (p < end) {
293+ auto * ex = reinterpret_cast <PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX >(p);
294+ if (ex->Relationship == RelationProcessorCore) {
295+ const GROUP_AFFINITY & g = ex->Processor .GroupMask [0 ];
296+ if (g.Mask ) {
297+ KAFFINITY first = g.Mask & (~g.Mask + 1 );
298+ GROUP_AFFINITY ga{};
299+ ga.Group = g.Group ;
300+ ga.Mask = first;
301+ picks.push_back (ga);
302+ if (picks.size () == N)
303+ break ;
304+ }
359305 }
306+ p += ex->Size ;
360307 }
361- p2 += ex->Size ;
362308 }
363309 }
364310
365311 return picks;
366312}
367313
368-
369- static inline void raise_process_priority () {
314+ inline void raise_process_priority () {
370315 SetPriorityClass (GetCurrentProcess (), HIGH_PRIORITY_CLASS );
371316}
372317
373- static inline void configure_current_thread_for_performance () {
318+ inline void configure_current_thread_for_performance () {
374319 THREAD_POWER_THROTTLING_STATE s{};
375320 s.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION ;
376321 s.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED ;
377- s.StateMask = 0 ; // favor performance (disable EcoQoS)
322+ s.StateMask = 0 ; // favor performance
378323 SetThreadInformation (GetCurrentThread (), ThreadPowerThrottling, &s, sizeof (s));
379324 SetThreadPriority (GetCurrentThread (), THREAD_PRIORITY_HIGHEST );
380325}
381326
327+ #else // =============================== LINUX =================================
328+
329+ #include < cstring>
330+ #include < iostream>
331+ #include < pthread.h>
332+ #include < sched.h>
333+ #include < sys/resource.h>
334+ #include < unistd.h>
335+ #include < vector>
336+
337+ struct GROUP_AFFINITY {}; // dummy placeholder for portability
338+
339+ // Simple round-robin CPU pinning on Linux
340+ static std::vector<int > pick_physical_cores (size_t N) {
341+ std::vector<int > cores;
342+ int ncpu = std::thread::hardware_concurrency ();
343+ for (int i = 0 ; i < (int ) N && i < ncpu; ++i)
344+ cores.push_back (i);
345+ return cores;
346+ }
347+
348+ inline void raise_process_priority () {
349+ if (setpriority (PRIO_PROCESS , 0 , -10 ) != 0 )
350+ std::cerr << " Warning: cannot raise process priority (" << strerror (errno) << " )\n " ;
351+ }
352+
353+ inline void configure_current_thread_for_performance () {
354+ pthread_t this_thread = pthread_self ();
355+ cpu_set_t cpuset;
356+ CPU_ZERO (&cpuset);
357+
358+ static thread_local int next_core = 0 ;
359+ int ncpu = std::thread::hardware_concurrency ();
360+ int core_id = next_core++ % ncpu;
361+
362+ CPU_SET (core_id, &cpuset);
363+ if (pthread_setaffinity_np (this_thread, sizeof (cpu_set_t ), &cpuset) != 0 )
364+ std::cerr << " Warning: cannot set CPU affinity (" << strerror (errno) << " )\n " ;
365+
366+ // Optional: try to lower nice value further
367+ setpriority (PRIO_PROCESS , 0 , -10 );
368+ }
369+
370+ #endif
371+ // ============================================================================
372+
373+
382374// ===== End helpers =====
383375
384376struct Config {
@@ -1826,15 +1818,18 @@ int main() {
18261818 eval_function_UR5e ();
18271819 });
18281820
1829- // Pin the thread immediately after creation
1821+ #ifdef _WIN32
1822+ // Only available on Windows — pin threads to P-cores
18301823 if (t < (int ) cores.size ()) {
18311824 SetThreadGroupAffinity (
18321825 (HANDLE ) workers.back ().native_handle (),
18331826 &cores[t],
18341827 nullptr );
18351828 }
1829+ #endif
18361830 }
18371831
1832+
18381833 for (auto & th: workers)
18391834 th.join ();
18401835
0 commit comments