● 抢占式多线程管理
我们画面中的每一个像素在程序中都会对应一个线程,以2560X1600分辨率为例,假设一帧画面中平均每一个像素会被处理一次,那么这一帧画面也就对应了4096000个线程。像素的处理繁简不一,有些可能只需要简单的加减1甚至0,而有些则可能需要完成复杂的如大数位浮点MUL之类的运算和操作。如果要达到流畅画面所需要的每秒60帧,整个GPU在1秒钟之内所要面对的线程数将轻松超过2亿个,如果再算上其他操作所需要占有的线程,则线程总数将更加庞大。这种情况下,如何管理好线程队列就成了保证整个GPU效率的关键。
常见的多线程管理有协作式多线程,单级抢占式多线程以及多级队列式多线程等。由于Barts、Cayman甚至整个AMD图形构架均采用VLIW形式的SIMD吞吐结构,整个VLIW CORE一直都是一个整体,因此只能采用更加有利于吞吐的单级抢占式多线程管理。
单级抢占式多线程管理与其他线程管理模式最大的不同,在于任务队列的形式。在这种管理模式下,所有线程会被排成一个统一的队列,队列中的每一个任务都会被赋予一个优先级,优先级意味着被执行的优先度,更高的优先级会让该线程打乱队列位置,优先被送入执行单元。当队列形成之后,线程调度机制会阶段性的对线程队列完成吞吐,然后将吞吐进来的线程按照优先级进行排序,并以此为依据将线程送入执行单元中。
具体到Barts和Cayman,在进行任务管理时又有自己独特的特色,那就是任务管理机制UTDP的抢占式工作以及wavefront的最小线程块单位。双UTDP在进行任务管理时也服从抢占规则,当某个UTDP完成既定现成的吞吐和分配之后,他会直接从任务队列中吞吐其他的线程,两个UTDP交替完成吞吐和分配,原则上可以更好的在时间和空间上弥合吞吐和操作造成的周期损失,达到更高效的吞吐和管理效率。而线程队列的基本单位也不是单个thread,AMD自R600以来的所有构架对于线程的管理均基于GPR结构,体系处理的最细粒度是64个thread构成的线程块,AMD称之为wavefront。UTDP会对wavefront进行吞吐,并将其送去对应的VLIW CORE部分。
我们不妨把UTDP对像素线程的管理想象成入学分班,这个类似小学生排队的场景大体上就是这样的——新生开学入学,所有新生(一帧画面中所有的像素线程)排成了一路纵队,然后从头到尾依次报数(像素队列排序)并等待分班,教务处的两个主任(UTDP)站在队列最前面,每人每次都会放一波学生(若干wavefront)过来看看,长得漂亮的或者过去获过奖的乖孩子(高优先级)被分去教师最好的班级(空闲处理器资源),调皮捣蛋的坏孩子(低优先级)则被分去了较差的班级(非空闲处理器资源),两位教务主任一刻不停地忙碌着,谁有空谁就去接下一波学生。