#include "ort_prive.h"
#include <stdlib.h>
#include <stdio.h>
#define FAILURE 0
#define NO_TASKS_LEFT -1
#define NO_VICTIM_LEFT -1
//流量控制模块,开启流量控制时任务立即执行而不放到任务队列中去
ee_key_t throttle_key;
//开启流量控制
#define __start_throttling() ee_setspecific(throttle_key,(void *) 1)
//停止流量控制
#define __stop_throttling() ee_setspecific(throttle_key,(void *) 0)
//判断是否开启了流量控制,返回1表示开启了流量控制
#define __check_throttling() (0 != (int) ee_getspecific(throttle_key))
inline static void ort_task_check_throttling(ort_eecb_t *me)
{
ort_eecb_t *my_parent = me->sdn;//获取父eecb的指针
int my_id = me->thread_num;//获取我的组内ID
int old_bottom, old_top;
//读取任务队列的长度,获取任务队列的底部和顶部
old_bottom = atomic_read
(&(my_parent->tasking.queue_table[my_id].bottom));
old_top = atomic_read
(&(my_parent->tasking.queue_table[my_id].top));
//当前队列小于队列长度的70%时关闭流量控制,目的是尽量让任务入队,组内线程共同执行
if ((old_bottom - old_top) < (int)(TASKQUEUESIZE*0.7))
{
__stop_throttling();//关闭流量控制
}
return;
}
static int ort_task_execute(ort_eecb_t *me, int startfrom)
{//当线程调用此函数时,从队列中取出一个任务或者从其他队列中偷取一个任务
int thread_id = me->thread_num;//获取当前线程的编号
ort_eecb_t *my_parent = me->sdn;//获取父eecb
ort_task_node_t* task_to_execute;
ort_task_node_t* prev_task_to_execute;//用来保存前一个执行的task
int victim;
int my_team_members = my_parent->num_children;//获取组内线程数目
int my_thread_id = thread_id;
int search_limit;
if(OMPI_STEAL_POLICY == LIFO)//从bottom位置获取一个任务
task_to_execute = ort_task_worker_dequeue(me);
else//从top位置获取一个任务
task_to_execute = ort_task_thief_steal(me, thread_id);
/* Processes task queue is empty! Have to steal something... */
if(task_to_execute == NULL)
{//没有task可以执行,偷取一个task
if (startfrom >= 0) /* Start stealing from there */
{//搜索是从startfrom开始,正方向环行一周
thread_id = startfrom - 1;
search_limit = my_team_members + thread_id + 1;
}
else//从threadid开始,正方向环行一周
search_limit = my_team_members + thread_id;
for(victim=thread_id+1; victim<search_limit; victim++)
{//正方向环行一周
if(victim == my_thread_id)
continue;//跳过本节点
//从指定线程的队列中偷取一个任务
task_to_execute = ort_task_thief_steal(me, victim%my_team_members);
//偷取到的任务为空,到别的队列中取偷取
if(task_to_execute == NULL)
continue;
//此时已经偷取成功,可以执行这个task
prev_task_to_execute = __CURRTASK(me);//保存当前任务,以便后面恢复
__CURRTASK(me) = task_to_execute;//设置当前任务为偷取的任务
//执行偷取的任务,任务函数的参数存储在funcarg指针的下一个位置,那funcarg的首位存什么?
(task_to_execute->func)(NP(task_to_execute->funcarg));
//恢复前一个task
__CURRTASK(me) = prev_task_to_execute;
if(task_to_execute->parent != NULL)
{
_faa(&((task_to_execute->parent)->num_children), -1);//父任务的子任务数目字段减1
}
//回收已经执行完的任务节点
ort_task_free(me, task_to_execute);
return victim%(my_parent->num_children);//返回执行的任务的属于那个线程
}
return -1;//循环正常退出,说明同组的线程的任务队列中都没有任务
}
else
{//无需偷取,有可执行的任务
prev_task_to_execute = __CURRTASK(me);//保存当前任务
__CURRTASK(me) = task_to_execute;//设置将要执行的任务
//执行任务函数
(task_to_execute->func)(NP(task_to_execute->funcarg));
//任务执行结束,恢复前一个任务
__CURRTASK(me) = prev_task_to_execute;
/* If task has a parent then */
if(task_to_execute->parent != NULL)
{
_faa(&((task_to_execute->parent)->num_children), -1);//父任务的子任务数目字段减1
}
//回收任务节点
ort_task_free(me, task_to_execute);
return me->thread_num;//返回执行的任务所属的线程
}
}
int check_for_tasks(ort_eecb_t *me)
{
int teamsize = me->num_siblings, victim, retry = NO_VICTIM_LEFT;
ort_task_queue_t *q = me->parent->tasking.queue_table;
for (victim = me->thread_num+1; victim < teamsize + me->thread_num; victim++)
{//若共享队列表中仍然有未完成的任务,那么返回它的队列编号
if ((q[victim % teamsize]).implicit_task_children != NULL &&
*((q[victim % teamsize]).implicit_task_children) > 0)
return ( victim % teamsize );//若有任务没有结束,则它永远不会为-1
}
return retry;//返回没有任务剩下
}
//以下函数在ort界面可见
void *ort_get_current_task()
{//获取当前eecb的当前任务
return (void *) __CURRTASK(__MYCB);
}
void ort_init_tasking()
{//初始化任务模块
ee_key_create(&throttle_key, 0);
__stop_throttling();//默认关闭流量控制,此时产生的任务将会进入队列中
}
void ort_create_task_immediate_node(ort_eecb_t *thr)
{//创建一个立即执行的任务节点,不是从任务节点池中获取任务节点,该任务节点执行完毕之后会被放到一个新建的任务节点池的回收站中去
ort_task_node_t *new_node;
//获取一个空任务节点
new_node = ort_task_alloc(NULL, NULL);
//设置任务节点的参数
new_node->func = NULL;
new_node->num_children = 0;
new_node->next = NULL;
new_node->parent = __CURRTASK(thr);//设置当前节点的父任务节点
new_node->icvs = __CURRTASK(thr)->icvs;//继承父任务节点的内部控制变量
new_node->inherit_task_node = 0;//该任务节点重用的次数
new_node->isfinal = __FINALTASK(thr);//继承父任务节点的final属性
//现在我有了自己的任务节点,父任务节点的继承次数字段减1
__INHERITASK(thr) --;
//减小父task的final计数
if(__FINALTASK(thr) > 0)
__FINALTASK(thr)--;
__CURRTASK(thr) = new_node;//设置此任务节点为当前节点
//判断是否应当关闭流量控制
if (thr->num_siblings != 1)
ort_task_check_throttling(thr);
return;
}
void ort_execute_my_tasks(ort_eecb_t *me)
{//只从当前线程的队列中取得任务,而不偷取任务
ort_task_node_t *task, *currtask = __CURRTASK(me);//保存任务,以便恢复
for (;;)
{
if(OMPI_STEAL_POLICY == LIFO)//从bottom位置偷取一个任务
task = ort_task_worker_dequeue(me);
else//从top位置偷取一个任务
task = ort_task_thief_steal(me, me->thread_num);
if (task == NULL)
return;//没有可以执行的任务
__CURRTASK(me) = task;//成功获取任务,设置为当前执行任务
(task->func)(NP(task->funcarg));//任务函数的参数存在funcarg下一个指针中
__CURRTASK(me) = currtask;//恢复前一个任务
if (task->parent != NULL)
{
_faa(&(task->parent->num_children), -1);//父任务的子任务字段减1
}
//回收任务节点
ort_task_free(me, task);
}
}
inline void *ort_task_immediate_start(int final)
{
ort_eecb_t *me = __MYCB ;
//修改引用计数,直接从父对象中继承任务节点
__INHERITASK(me) ++;
//对final计数增加
if(__FINALTASK(me) > 0 || final > 0)
__FINALTASK(me)++;
//检查流量控制
if (me->num_siblings != 1)
ort_task_check_throttling(me);
return me;
}
inline void ort_task_immediate_end(void *my_eecb)
{
ort_eecb_t *me = (ort_eecb_t *)my_eecb;
ort_task_node_t *task_node;
if(__INHERITASK(me) > 0)
{//判断是不是从父任务继承而来,若是则不需要回收任务节点,只需要修改父任务的继承和终止字段的计数即可
if(__FINALTASK(me) > 0)
__FINALTASK(me)--;
__INHERITASK(me) --;//修改继承字段的计数
return;
}
//当前任务节点不是从父任务节点继承而来的,需要回收此任务节点
task_node = __CURRTASK(me);
__CURRTASK(me) = task_node->parent;//恢复上一个任务节点
ort_task_free(me, task_node);//回收任务节点
return;
}
void ort_new_task(int final, int untied, void *(*func)(void *arg), void *arg)
{
ort_eecb_t *me = __MYCB;
ort_task_node_t *tnode;//应当改为void *类型
if(__FINALTASK(me) > 0)
{//如果当前eecb的当前任务是一个终止任务,那么其所有子任务立即执行
tnode = ort_task_immediate_start(1);//是否应该强制转换?void *转ort_task_node_t *类型,
//因为该函数返回的是一个指向eecb的指针,所以使用一个void*来接收会不会更好一些?
(*func)(arg);//执行任务函数
if(arg != NULL)//当任务函数的参数非空时,回收此任务节点
ort_task_free(me, *((ort_task_node_t **)PP(arg)));//回收此任务节点
ort_task_immediate_end(tnode);//修改父任务的继承和终止字段的计数
return;
}
//非终止任务,那么将其放到任务队列中去
ort_task_worker_enqueue(me, func, arg, final);
me->parent->tasking.never_task = 1;//设置队列中有任务了
}
/* How = 0 (wait for my children), 1 (wait for all team tasks),
* 2 (wait at the end of parallel)
*/
void ort_taskwait(int how)
{
ort_eecb_t *me = __MYCB;
int victim = NO_VICTIM_LEFT;
if (me->num_siblings == 1)
return;
else if(how < 2 && me->parent->tasking.never_task == 0)
return;
else if(how == 2)
{//并行域级别的等待
parallel_barrier_wait(&me->parent->barrier, me->thread_num);
return;
}
if (how > 0)
{//帮助组内其他线程完成任务
do{
while((victim = ort_task_execute(me, victim)) != NO_TASKS_LEFT)
;
}while ((victim = check_for_tasks(me)) != NO_VICTIM_LEFT);
}
else//只等待当前任务的子任务全部完成
while (__CURRTASK(me)->num_children > 0)
ort_task_execute(me, victim);
}
/* Task throttling.
* For the moment, this is a per-thread flag that should be adjusted
* adaptively.
* A simple policy would be to __start_throttling() when the number of
* tasks in my private queue exceeds c*N where c is a constant and N
* is the number of processors. If later I discover that the number
* fell below this threshold, I __stop_throttling().
*/
int ort_task_throttling(void)
{//任务流量控制
int worker_id, old_bottom, old_top;
ort_eecb_t *my_parent;
ort_eecb_t *me = __MYCB;
//已经开启了流量控制
if(__check_throttling())
return 1;
if(me->num_siblings == 1)
{//组内只有一个线程时,不需要将任务入队
__start_throttling();
return 1;
}
worker_id = me->thread_num;
my_parent = me->sdn;
//获取当前eecb在父eecb的队列表中的队列底部和顶部
old_bottom = atomic_read
(&(my_parent->tasking.queue_table[worker_id].bottom));
old_top = atomic_read
(&(my_parent->tasking.queue_table[worker_id].top));
if ((old_bottom - old_top) >= TASKQUEUESIZE)
{//队列已满,开启节流控制
__start_throttling();
return 1;
}
return 0;//其他情况下不需要节流
}
void ort_start_implicit_task(ort_eecb_t *thr)
{
ort_eecb_t *parent_thread = thr->parent;
ort_task_node_t *tnode, *parent_task;
//是应该使用新节点还是继承父任务节点
if(__INHERITASK(parent_thread))
ort_create_task_immediate_node(parent_thread);
parent_task = __CURRTASK(parent_thread);
task_pools_init(thr);//初始化此eecb的任务池
tnode = ort_task_alloc(NULL, NULL);//新建一个任务节点,并设置其参数
tnode->func = NULL;
tnode->num_children = 0;
tnode->next = NULL;
tnode->parent = parent_task;
tnode->inherit_task_node = 0;
tnode->icvs = parent_task->icvs;
tnode->isfinal = 0;
if (thr->level < ort->set_nthrlevs)
tnode->icvs.nthreads = ort->nthr_per_level[thr->level];//修改任务的线程数目
//初始化父eecb的任务队列表中此线程的隐式任务个数为0
(parent_thread->tasking.queue_table[thr->thread_num]).implicit_task_children
= &(tnode->num_children);
_faa(&(parent_task->num_children), 1);//父任务的孩子数目增1
__SETCURRTASK(thr, tnode);//将此节点设置为当前任务节点
}
void ort_finish_implicit_task(ort_eecb_t *thr)
{//完成隐式任务
ort_task_node_t *tnode = __CURRTASK(thr);
_faa(&(tnode->parent->num_children), -1);//减小父任务的孩子数目
if (thr->num_siblings > 1)
ort_taskwait(1);//等待本task的所有子task完成
__SETCURRTASK(thr, tnode->parent);//恢复父task
ort_task_free(thr, tnode);//回收此任务节点
}
ort_tasks.c
最新推荐文章于 2023-04-10 06:08:38 发布