// ======================================================================== // // Copyright 2009-2014 Intel Corporation // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // // You may obtain a copy of the License at // // // // http://www.apache.org/licenses/LICENSE-2.0 // // // // Unless required by applicable law or agreed to in writing, software // // distributed under the License is distributed on an "AS IS" BASIS, // // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // // See the License for the specific language governing permissions and // // limitations under the License. // // ======================================================================== // #include "taskscheduler.h" #include "taskscheduler_sys.h" #if defined(__MIC__) #include "taskscheduler_mic.h" #endif #include "sysinfo.h" #include "tasklogger.h" #include "sys/sync/atomic.h" #include "math/math.h" namespace embree { /* initialization structure for threads */ struct Thread { Thread (size_t threadIndex, size_t threadCount, TaskScheduler* scheduler) : threadIndex(threadIndex), threadCount(threadCount), scheduler(scheduler) {} public: size_t threadIndex; size_t threadCount; TaskScheduler* scheduler; }; TaskScheduler* TaskScheduler::instance = NULL; void TaskScheduler::create(size_t numThreads) { if (instance) THROW_RUNTIME_ERROR("Embree threads already running."); /* enable fast pthreads tasking system */ #if defined(__MIC__) instance = new TaskSchedulerMIC; //instance = new TaskSchedulerSys; #else instance = new TaskSchedulerSys; #endif instance->createThreads(numThreads); } size_t TaskScheduler::getNumThreads() { if (!instance) THROW_RUNTIME_ERROR("Embree threads not running."); return instance->numEnabledThreads; } size_t TaskScheduler::enableThreads(size_t N) { if (!instance) THROW_RUNTIME_ERROR("Embree threads not running."); // if (!instance->defaultNumThreads) return; // FIXME: enable N = min(N,instance->numThreads); //TaskScheduler::init(N); return instance->numEnabledThreads = N; } void TaskScheduler::addTask(ssize_t threadIndex, QUEUE queue, Task* task) { if (!instance) THROW_RUNTIME_ERROR("Embree threads not running."); instance->add(threadIndex,queue,task); } void TaskScheduler::executeTask(size_t threadIndex, size_t threadCount, runFunction run, void* runData, size_t elts, completeFunction complete, void* completeData, const char* name) { TaskScheduler::Event event; TaskScheduler::Task task(&event,run,runData,elts,complete,completeData,name); instance->add(threadIndex,TaskScheduler::GLOBAL_FRONT,&task); instance->wait(threadIndex,threadCount,&event); } void TaskScheduler::executeTask(size_t threadIndex, size_t threadCount, runFunction run, void* runData, size_t elts, const char* name) { TaskScheduler::Event event; TaskScheduler::Task task(&event,run,runData,elts,NULL,NULL,name); instance->add(threadIndex,TaskScheduler::GLOBAL_FRONT,&task); instance->wait(threadIndex,threadCount,&event); } void TaskScheduler::executeTask(size_t threadIndex, size_t threadCount, completeFunction complete, void* completeData, const char* name) { TaskScheduler::Event event; TaskScheduler::Task task(&event,NULL,NULL,1,complete,completeData,name); instance->add(threadIndex,TaskScheduler::GLOBAL_FRONT,&task); instance->wait(threadIndex,threadCount,&event); } void TaskScheduler::waitForEvent(Event* event) { instance->wait(0,instance->getNumThreads(),event); } void TaskScheduler::destroy() { enableThreads(-1); if (instance) { instance->destroyThreads(); delete instance; instance = NULL; } } TaskScheduler::TaskScheduler () : terminateThreads(false), defaultNumThreads(true), numThreads(0), numEnabledThreads(0) {} void TaskScheduler::createThreads(size_t numThreads_in) { numThreads = numThreads_in; defaultNumThreads = false; #if defined(__MIC__) if (numThreads == 0) { numThreads = getNumberOfLogicalThreads()-4; defaultNumThreads = true; } #else if (numThreads == 0) { numThreads = getNumberOfLogicalThreads(); defaultNumThreads = true; } #endif numEnabledThreads = numThreads; /* generate all threads */ for (size_t t=0; trun(thread.threadIndex,thread.threadCount); } catch (const std::exception& e) { std::cout << "Error: " << e.what() << std::endl; exit(1); } LockStepTaskScheduler* LockStepTaskScheduler::instance() { return scheduler; } void LockStepTaskScheduler::setInstance(LockStepTaskScheduler* inst) { scheduler = inst; } void TaskScheduler::destroyThreads () { terminate(); for (size_t i=0; inumThreads = numThreads; } syncThreads(threadID, numThreads); numThreads = this->numThreads; if (taskPtr) { if (threadID < numThreads) { (*taskPtr)((void*)data,threadID,numThreads); } syncThreads(threadID, numThreads); return false; } if (taskPtr2) { if (threadID < numThreads) { while (true) { size_t taskID = taskCounter.inc(); if (taskID >= numTasks) break; (*taskPtr2)((void*)data,threadID,numThreads,taskID,numTasks); } } syncThreads(threadID, numThreads); return false; } return true; } void LockStepTaskScheduler::leave(const size_t threadID, const size_t numThreads) { assert(threadID == 0); releaseThreads(numThreads); } void LockStepTaskScheduler::releaseThreads(const size_t numThreads) { taskPtr = NULL; taskPtr2 = NULL; data = NULL; numTasks = 0; dispatchTask(0,numThreads); } // ================================================================================ // ================================================================================ // ================================================================================ LockStepTaskScheduler4ThreadsLocalCore::LockStepTaskScheduler4ThreadsLocalCore() { taskPtr = NULL; data = NULL; for (size_t j=0;j<2;j++) for (size_t i=0;i<4;i++) threadState[j][i] = 0; mode = 0; } void LockStepTaskScheduler4ThreadsLocalCore::syncThreads(const size_t localThreadID) { const unsigned int m = mode; if (localThreadID == 0) { __memory_barrier(); threadState[m][localThreadID] = 1; __memory_barrier(); while( (*(volatile unsigned int*)&threadState[m][0]) != 0x01010101 ) __pause_cpu(WAIT_CYCLES); mode = 1 - mode; __memory_barrier(); *(volatile unsigned int*)&threadState[m][0] = 0; } else { __memory_barrier(); threadState[m][localThreadID] = 1; __memory_barrier(); while (threadState[m][localThreadID] == 1) __pause_cpu(WAIT_CYCLES); } } void LockStepTaskScheduler4ThreadsLocalCore::dispatchTaskMainLoop(const size_t localThreadID, const size_t globalThreadID) { while (true) { bool dispatch = dispatchTask(localThreadID,globalThreadID); if (dispatch == true) break; } } bool LockStepTaskScheduler4ThreadsLocalCore::dispatchTask(const size_t localThreadID, const size_t globalThreadID) { syncThreads(localThreadID); if (taskPtr == NULL) return true; (*taskPtr)((void*)data,localThreadID,globalThreadID); syncThreads(localThreadID); return false; } void LockStepTaskScheduler4ThreadsLocalCore::releaseThreads(const size_t localThreadID, const size_t globalThreadID) { taskPtr = NULL; data = NULL; dispatchTask(localThreadID,globalThreadID); } }