注:问号以及未注释部分 会在x265-1.8版本内更新
/*****************************************************************************
* Copyright (C) 2013 x265 project
*
* Authors: Steve Borho
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111, USA.
*
* This program is also available under a commercial proprietary license.
* For more information, contact us at license @ x265.com
*****************************************************************************/
#ifndef X265_THREADPOOL_H
#define X265_THREADPOOL_H
#include "common.h"
#include "threading.h"
namespace x265 {
// x265 private namespace
class ThreadPool;
class WorkerThread;
class BondedTaskGroup;
#if X86_64
typedef uint64_t sleepbitmap_t;
#else
typedef uint32_t sleepbitmap_t;
#endif
static const sleepbitmap_t ALL_POOL_THREADS = (sleepbitmap_t)-1;
enum { MAX_POOL_THREADS = sizeof(sleepbitmap_t) * 8 };
enum { INVALID_SLICE_PRIORITY = 10 }; // a value larger than any X265_TYPE_* macro
// Frame level job providers. FrameEncoder and Lookahead derive from
// this class and implement findJob()
class JobProvider
{
public:
ThreadPool* m_pool; //指向encoder类中的m_threadPool
sleepbitmap_t m_ownerBitmap; //当前任务拥有的核
//ALL_POOL_THREADS = -1 sleepbitmap_t 为无符号数64位整数,初始化为0
//这是一个map,如4核机器:cup(0,1,2,3) 对应 (1,10,100,1000),查看当前哪些位置是1表示当前哪个cpu在干活,如果是4核,最大值当然就是1111,15
//在函数JobProvider::tryWakeOne()和WorkerThread::threadMain()会对其改变 在tryWakeOne()中,将释放当前其不应该拥有的id,重新更新map
int m_jpId; //其对应的jobid,一个任务一个id如:4核单机中:m_frameEncoder[0] m_jpId:0 m_frameEncoder[1] m_jpId:1 m_lookahead m_jpId:2
int m_sliceType; //???在FrameEncoder类中表示其帧类型
bool m_helpWanted; //初始化为false,会在JobProvider::tryWakeOne()、WaveFront::findJob(int threadId)、 Lookahead::findJob()中有更新
//Lookahead::findJob()中:如果当前还不能进行帧类型决策,则将其置为false,表示暂不需要核
//JobProvider::tryWakeOne() 中,如果当前没有返回可用的mid(核),则置为true,表示需要核
//WaveFront::findJob(int threadId)???
bool m_isFrameEncoder; // 当前子类是否是FrameEncoder,在创建子类FrameEencoder时候会对该值进行赋值/* rather ugly hack, but nothing better presents itself */
JobProvider()
: m_pool(NULL)
, m_ownerBitmap(0)
, m_jpId(-1)
, m_sliceType(INVALID_SLICE_PRIORITY)
, m_helpWanted(false)
, m_isFrameEncoder(false)
{}
virtual ~JobProvider() {}
// Worker threads will call this method to perform work
virtual void findJob(int workerThreadId) = 0;
// Will awaken one idle thread, preferring a thread which most recently
// performed work for this provider.
void tryWakeOne();
};
class ThreadPool
{
public:
sleepbitmap_t m_sleepBitmap;// 用于记录当前哪些核是sleep,其对应位为1的 ,一开始拥有全部的全部为sleep (例如四核机器:一般是0~15的数据)
// 在函数tryAcquireSleepingThread 和WorkerThread::threadMain()会对其改变
int m_numProviders;//当前提供给几个线程 如: 0 frameEncode[0] 1 frameEncoder[1] 2 lookachead
int m_numWorkers;//当前机器核数 单机4核测试是4
int m_numaNode;
bool m_isActive;
JobProvider** m_jpTable;
WorkerThread* m_workers;
ThreadPool();
~ThreadPool();
bool create(int numThreads, int maxProviders, int node);
bool start();
void stopWorkers();
/** 函数功能 :设置线程间能够在不同的核运行,而不会同时占用同一个核
/* 调用范围 :只在 WorkerThread::threadMain()和FrameEncoder::threadMain()函数中被调用
* \参数 numaNode :表示当前处于node的位置,如果没有配置,则只有一个就是0
* \返回 :null * */
void setCurrentThreadAffinity();
/** 函数功能 :在tryWakeOne中返回当前机器任意一个正在睡眠的核,tryBondPeers返回当前线程拥有核且正在睡眠的核
/* 调用范围 :只在 JobProvider::tryWakeOne()和ThreadPool::tryBondPeers函数中被调用
* \参数 firstTryBitmap :为Job的m_ownerBitmap (如4核机器:一般传入是-1~15的数据)
* \参数 secondTryBitmap :只有0 和 -1 两个值能够传进来,tryBondPeers为0 tryWakeOne() 为-1(全为1,mask值)
* \返回 :在tryWakeOne中返回当前机器任意一个正在睡眠的核,tryBondPeers返回当前线程拥有核且正在睡眠的核 * */
int tryAcquireSleepingThread(sleepbitmap_t firstTryBitmap, sleepbitmap_t secondTryBitmap);
/** 函数功能 : 返回当前可用核数,并在threadmain中触发相应processtask
/* 调用范围 : 只在slicetypeDecide、CostEstimateGroup::finishBatch()、CostEstimateGroup::estimateFrameCost、predInterSearch、compressFrame()和compressInterCU_dist函数中被调用
* \返回 : 返回当前可用核数 * */
int tryBondPeers(int maxPeers, sleepbitmap_t peerBitmap, BondedTaskGroup& master);
static ThreadPool* allocThreadPools(x265_param* p, int& numPools);
static int getCpuCount();
static int getNumaNodeCount();
/** 函数功能 :设置线程间能够在不同的核运行,而不会同时占用同一个核
/* 调用范围 :只在 ThreadPool::setCurrentThreadAffinity()函数中被调用
* \参数 numaNode :表示当前处于node的位置,如果没有配置,则只有一个就是0
* \返回 :null * */
static void setThreadNodeAffinity(int node);
};
/* Any worker thread may enlist the help of idle worker threads from the same
* job provider. They must derive from this class and implement the
* processTasks() method. To use, an instance must be instantiated by a worker
* thread (referred to as the master thread) and then tryBondPeers() must be
* called. If it returns non-zero then some number of slave worker threads are
* already in the process of calling your processTasks() function. The master
* thread should participate and call processTasks() itself. When
* waitForExit() returns, all bonded peer threads are quarunteed to have
* exitied processTasks(). Since the thread count is small, it uses explicit
* locking instead of atomic counters and bitmasks */
class BondedTaskGroup //被PMODE、WeightAnalysis、PME、CostEstimateGroup、PreLookaheadGroup继承
{
public:
Lock m_lock; //在多线程操作中获取临界资源时用于加锁
ThreadSafeInteger m_exitedPeerCount; //加锁计数参量,每当完成一个任务自加1,初始化为0
int m_bondedPeerCount; //计算当前任务拥有多少核
int m_jobTotal; //当前需要运行的任务总数: 如在PreLookaheadGroup子类中表示当前有多少帧没有初始化(lookachead)
//CostEstimateGroup子类中表示当前有多少需要计算frame-cost的帧数 1. 2. lookachead 多slice配置中:一帧有多少slice
//????
int m_jobAcquired; //计数当前已经完成的任务数目
BondedTaskGroup() { m_bondedPeerCount = m_jobTotal = m_jobAcquired = 0; }//初始化
/* Do not allow the instance to be destroyed before all bonded peers have
* exited processTasks() */
~BondedTaskGroup() { waitForExit(); }//等待全部任务完成,释放内存
/* Try to enlist the help of idle worker threads on most recently associated
* with the given job provider and "bond" them to work on your tasks. Up to
* maxPeers worker threads will call your processTasks() method. */
/** 函数功能 : 返回当前可用核数,并在threadmain中触发相应processtask,从当前job中拥有核并且sleep状态的核才可以触发
/* 调用范围 : 只在Analysis::compressInterCU_dist、FrameEncoder::compressFrame()、Search::predInterSearch函数中被调用
* \返回 : 返回当前可用核数 * */
int tryBondPeers(JobProvider& jp, int maxPeers)
{
int count = jp.m_pool->tryBondPeers(maxPeers, jp.m_ownerBitmap, *this);
m_bondedPeerCount += count;
return count;
}
/* Try to enlist the help of any idle worker threads and "bond" them to work
* on your tasks. Up to maxPeers worker threads will call your
* processTasks() method. */
/** 函数功能 : 返回当前可用核数,并在threadmain中触发相应processtask,只要是sleep状态的核都可以触发
/* 调用范围 : 只在slicetypeDecide、CostEstimateGroup::finishBatch()、CostEstimateGroup::estimateFrameCost函数中被调用
* \返回 : 返回当前可用核数 * */
int tryBondPeers(ThreadPool& pool, int maxPeers)
{
int count = pool.tryBondPeers(maxPeers, ALL_POOL_THREADS, *this);
m_bondedPeerCount += count;
return count;
}
/* Returns when all bonded peers have exited processTasks(). It does *NOT*
* ensure all tasks are completed (but this is generally implied). */
/** 函数功能 : 一直等待到任务全部完成,这里等待的是核释放,内核释放了任务也就完成了
/* 调用范围 : 只在compressInterCU_dist、compressFrame()、predInterSearch、slicetypeDecide()、CostEstimateGroup::finishBatch()、CostEstimateGroup::estimateFrameCost函数中被调用
* \返回 : null * */
void waitForExit()
{
int exited = m_exitedPeerCount.get();
while (m_bondedPeerCount != exited) //如果在threadmain释放的核数等于当前拥有的核数说明当前已经完成任务
exited = m_exitedPeerCount.waitForChange(exited);
}
/* Derived classes must define this method. The worker thread ID may be
* used to index into thread local data, or ignored. The ID will be between
* 0 and jp.m_numWorkers - 1 */
//虚函数,具体运行其子类的task
virtual void processTasks(int workerThreadId) = 0;
};
} // end namespace x265
#endif // ifndef X265_THREADPOOL_H