Tengo una clase de agrupación de subprocesos personalizada, que crea algunos subprocesos que cada uno espera en su propio evento (señal). Cuando se agrega un nuevo trabajo al grupo de subprocesos, se activa el primer subproceso libre para que ejecute el trabajo.Overhead debido al uso de eventos
El problema es el siguiente: Tengo alrededor de 1000 loops de cada uno alrededor de 10'000 iteraciones. Estos bucles se deben ejecutar secuencialmente, pero tengo 4 CPU disponibles. Lo que intento hacer es dividir los 10'000 bucles de iteración en 4 bucles de 2'500 iteraciones, es decir, uno por hilo. Pero tengo que esperar a que los 4 bucles pequeños terminen antes de pasar a la siguiente iteración "grande". Esto significa que no puedo agrupar los trabajos.
Mi problema es que usar el grupo de subprocesos y 4 subprocesos es mucho más lento que hacer los trabajos secuencialmente (tener un ciclo ejecutado por un subproceso separado es mucho más lento que ejecutarlo directamente en el subproceso principal secuencialmente).
Estoy en Windows, entonces creo eventos con CreateEvent()
y luego espero en uno de ellos usando WaitForMultipleObjects(2, handles, false, INFINITE)
hasta que el hilo principal llame al SetEvent()
.
Parece que todo este evento (¡junto con la sincronización entre los subprocesos usando secciones críticas) es bastante caro!
Mi pregunta es: ¿es normal que usar eventos tome "mucho" tiempo? Si es así, ¿hay otro mecanismo que pueda usar y que sea menos costoso en el tiempo?
Aquí hay un código para ilustrar (algunas partes relevantes copiados de mi clase de grupo de subprocesos):
// thread function
unsigned __stdcall ThreadPool::threadFunction(void* params) {
// some housekeeping
HANDLE signals[2];
signals[0] = waitSignal;
signals[1] = endSignal;
do {
// wait for one of the signals
waitResult = WaitForMultipleObjects(2, signals, false, INFINITE);
// try to get the next job parameters;
if (tp->getNextJob(threadId, data)) {
// execute job
void* output = jobFunc(data.params);
// tell thread pool that we're done and collect output
tp->collectOutput(data.ID, output);
}
tp->threadDone(threadId);
}
while (waitResult - WAIT_OBJECT_0 == 0);
// if we reach this point, endSignal was sent, so we are done !
return 0;
}
// create all threads
for (int i = 0; i < nbThreads; ++i) {
threadData data;
unsigned int threadId = 0;
char eventName[20];
sprintf_s(eventName, 20, "WaitSignal_%d", i);
data.handle = (HANDLE) _beginthreadex(NULL, 0, ThreadPool::threadFunction,
this, CREATE_SUSPENDED, &threadId);
data.threadId = threadId;
data.busy = false;
data.waitSignal = CreateEvent(NULL, true, false, eventName);
this->threads[threadId] = data;
// start thread
ResumeThread(data.handle);
}
// add job
void ThreadPool::addJob(int jobId, void* params) {
// housekeeping
EnterCriticalSection(&(this->mutex));
// first, insert parameters in the list
this->jobs.push_back(job);
// then, find the first free thread and wake it
for (it = this->threads.begin(); it != this->threads.end(); ++it) {
thread = (threadData) it->second;
if (!thread.busy) {
this->threads[thread.threadId].busy = true;
++(this->nbActiveThreads);
// wake thread such that it gets the next params and runs them
SetEvent(thread.waitSignal);
break;
}
}
LeaveCriticalSection(&(this->mutex));
}
edición para precisar su pregunta ... – neuro