非嚴格優先隊列-O(1) 時間複雜度

原文轉自:http://www.tanjp.com/archives/124 (即時修正和更新)

 

分組安全隊列 - 非嚴格優先隊列 O(1) 時間複雜度

傳統的優先隊列,都是採用二叉樹(堆)的方式來實現,插入和刪除都需要維護堆頂元素最大或最小,需要O(logN)的時間複雜度。在某些多線程環境下,把隊列當作一個數據交換的紐帶,這種開銷也是非常大的,於是設計了非嚴格的優先隊列,基滿足業務需求,同時插入和取出時間複雜度爲O(1)。

把隊列分爲 N 個組,組的編號範圍[0, N-1],push時能加入到指定的分組上(也就是能指定優先級),pop時編號越大的組會優先取出(編號越大優先級越高)。

如果只是這樣的規則,可能會導致低優先級的元素永遠無法取出。

於是,設計了幀的概念,幀數從0開始,每次push時,幀數加一,並記錄在一個幀隊列裏面。當調用pop取出元素後,判斷當前組頭部元素所屬的幀與幀隊列頭的幀比較,超過指定幀數F時,就把該頭部元素加入到最高編號(最高優先級)的組的尾部,也就是說下次會優先從最高組取出(但不一定馬上取出,因爲是加到最高組的最後)。

概括來說,這是一個不嚴格的優先隊列,當太久前加入的元素沒被取出,則提升其優先級,儘可能均衡,而不導致的元素積壓過久。這樣做避免傳統樹形結構的優先隊列,在每次 push和pop都需要 O(logN)時間複雜度,來調整樹的平衡。

分組安全隊列的push和pop基本是O(1)的時間複雜度。push的時候只要找到相應的組把元素加入進去並記錄在幀隊列中就行,時間複雜度O(1)。pop的時候,取出有數據的最高組的頭部元素,並比較取出元素的幀數與當前幀隊列頭部元素的幀數,超過指定值,就觸發提高優先級操作,時間複雜度也是O(1)。所以在性能上有很大的優勢,當然要根據需求是否能採用這種非嚴格的優先處理方式。

 

以下是實現代碼:

template<typename tpType>
class SafeGroupQueue
{
    	static const uint64 kFrameMaxValue = (std::numeric_limits<uint64>::max() - 1000U); //超過此幀數將會重置
    	static const uint32 kDefaultMaxsize = 4294967290U; //隊列容量上限
    	static const uint32 kDefaultUpgradeFrame = 8U; //默認優先處理的幀數
    	static const uint8 kDefaultGroupSize = 5U; //優先隊列默認分級大小
    	static const uint8 kDefaultPriorityValue = 2U; //優先隊列默認加入分級
    	struct FrameDataType
	{
		FrameDataType(const tpType & po_data, uint64 pn_frame)
			: data(po_data)
			, frame(pn_frame) {}
		tpType data;
		uint64 frame;
	};
	struct FrameGroupType
	{
		FrameGroupType(uint8 pn_priority, uint64 pn_frame)
			: priority(pn_priority)
			, frame(pn_frame) {}
		uint8 priority;
		uint64 frame;
	};
	typedef std::shared_ptr< std::deque<FrameDataType> > QueueType;
public:
	// @pb_block, true爲阻塞模式,push和pop操作在邊界條件下將會阻塞等待。
	//			false爲非阻塞操作,push和pop操作邊界條件將會立即返回false。
	// @pn_group_size, 組的大小,取值範圍[2, 255]。
	// @pn_upgrade_frame_count, 過幀數量,過幀到達指定該數量將會把元素提高到最高優先級。0表示不進行提高處理。
	// @pn_maxsize, 容器元素數量上限。
	explicit SafeGroupQueue(bool pb_block = true, uint8 pn_group_size = kDefaultGroupSize,
		uint32 pn_upgrade_frame_count = kDefaultUpgradeFrame, uint32 pn_maxsize = kDefaultMaxsize)
		: mn_group_size(pn_group_size > 1 ? pn_group_size : kDefaultGroupSize)
		, mn_upgrade_frame_count(pn_upgrade_frame_count)
		, mb_block(pb_block)
		, mn_frame(0)
		, mn_maxsize(pn_maxsize > 0 ? pn_maxsize : kDefaultMaxsize)		
		, mc_group_queue()
		, mc_frame_queue()
		, mn_count(0)
		, mn_first_pop_group(0)
		, mb_push_waiting(0)
		, mb_pop_waiting(0)
	{
		for (uint8 i = 0; i < mn_group_size; ++i)
		{
			mc_group_queue.push_back(QueueType(new std::deque<FrameDataType>()));
		}
	}

	~SafeGroupQueue() { }

	// 加入元素到指定分組隊列的尾部
	// @po_val, 待加入的元素。
	// @pn_priority, 指定的分組,值越大會被優先處理,取值範圍[0,group_size-1],超過分組大小將放在最高分組。
	// @return, 非阻塞模式下加入成功返回true,失敗返回false。阻塞模式下必然返回true,否則掛起等待。
	bool push(const tpType & po_val, uint8 pn_priority = kDefaultPriorityValue)
	{		
		{
			std::lock_guard<std::mutex> lock(mo_mutex);
			if (pn_priority >= mc_group_queue.size())
			{
				//越界處理
				pn_priority = uint8(mc_group_queue.size() - 1);
			}

			while ((mn_count >= mn_maxsize) && mb_block)
			{
				//滿且阻塞標記中,掛起等待
				++mb_push_waiting;
				mo_cond_push.wait(mo_mutex);
				--mb_push_waiting;
			}

			if ((mn_count >= mn_maxsize) && (!mb_block))
			{
				//滿且不阻塞標記中,馬上返回
				return false;
			}

			++mn_count; //計數
			++mn_frame; //每次加入幀數+1

			if (mn_frame >= kFrameMaxValue)
			{
				reset_frame(); //重置幀數
			}			

			FrameDataType val(po_val, mn_frame);
			mc_group_queue[pn_priority]->push_back(val); //放入指定分組
			
			if (pn_priority > mn_first_pop_group)
			{
				mn_first_pop_group = pn_priority; //優先取最高分組的數據
			}
			if ((mn_group_size > 1) && (mn_upgrade_frame_count > 0))
			{
				//記錄幀數優先級
				FrameGroupType ft(pn_priority, mn_frame);
				mc_frame_queue.push_back(ft);
			}
		} //釋放鎖
		if (mb_pop_waiting > 0)
		{
			mo_cond_pop.notify_one();
		}
		return true;
	}

	// 取出有數據的最高分組的隊列頭部元素。還會對下個元素進行過幀升組處理。
	// @po_val, 待取出的頭部元素。
	// @return, 非阻塞模式下取出成功返回true,失敗返回false。阻塞模式下必然返回true,否則掛起等待。
	bool pop(tpType & po_val)
	{
		{
			std::lock_guard<std::mutex> lock(mo_mutex);

			while ((mn_count <= 0) && mb_block)
			{
				//空且阻塞標記中,掛起等待
				++mb_pop_waiting;
				mo_cond_pop.wait(mo_mutex);
				--mb_pop_waiting;
			}

			if ((mn_count <= 0) && (!mb_block))
			{
				//空且不阻塞標記中,馬上返回
				return false;
			}
			
			--mn_count;

			FrameDataType & val = mc_group_queue[mn_first_pop_group]->front();
			uint64 zn_pop_frame = val.frame;
			po_val = val.data;
			mc_group_queue[mn_first_pop_group]->pop_front();

			if (mn_count <= 0)
			{
				//沒數據了
				mn_first_pop_group = 0;
				while (!mc_frame_queue.empty())
				{
					//隊列已經空了
					mc_frame_queue.pop_front();
				}
			}
			else
			{
				if (!mc_frame_queue.empty() && (zn_pop_frame == mc_frame_queue.front().frame))
				{
					//同一個元素,直接丟棄,意思是已處理
					mc_frame_queue.pop_front();
				}
				else
				{
					while (!mc_frame_queue.empty())
					{
						//找非空的隊列
						FrameGroupType & ft = mc_frame_queue.front();
						QueueType & zc_queue = mc_group_queue[ft.priority];
						if (zc_queue->empty() || (zc_queue->front().frame > ft.frame))
						{
							//幀隊列爲空或頭部元素已經被處理掉,直接丟棄,意思是已處理
							mc_frame_queue.pop_front();
						}
						else
						{
							int64 len = zn_pop_frame - ft.frame; //已經超過的幀數
							uint8 zn_max_priority = (uint8)(mc_group_queue.size() - 1);
							if ((len > mn_upgrade_frame_count) && (ft.priority < zn_max_priority))
							{
								//超過指定幀數沒被處理,提高優先級,移動到最高優先級的隊列中

								FrameDataType & move_val = zc_queue->front();
								mc_group_queue[zn_max_priority]->push_back(move_val);
								zc_queue->pop_front();

								ft.priority = zn_max_priority; //改變優先級到最高
								mn_first_pop_group = zn_max_priority;
							}
							break; //每次取出,只提升一個元素優先級
						}
					} //while						
				}
				while (mc_group_queue[mn_first_pop_group]->empty())
				{
					//找下個優先級高的隊列
					--mn_first_pop_group;
					if (mn_first_pop_group <= 0)
					{
						break;
					}
				} //while
			} //else
		} //釋放鎖
		if (mb_push_waiting > 0)
		{
			mo_cond_push.notify_one();
		}
		return true;
	}
	bool empty()
	{
		std::lock_guard<std::mutex> lock(mo_mutex);
		return (mn_count <= 0);
	}
	bool full()
	{
		std::lock_guard<std::mutex> lock(mo_mutex);
		return (mn_count >= mn_maxsize);
	}
	uint32 size()
	{
		std::lock_guard<std::mutex> lock(mo_mutex);
		return mn_count;
	}
	void clear()
	{
		{
			std::lock_guard<std::mutex> lock(mo_mutex);
			for (uint32 i = 0; i < mc_group_queue.size(); ++i)
			{
				while (!mc_group_queue[i]->empty())
					mc_group_queue[i]->pop_front();
			}
			mc_frame_queue.clear();

			mn_frame = 0;
			mn_first_pop_group = 0;
			mn_count = 0;			
		}
		mo_cond_push.notify_all();
	}
	void set_block(bool pb_block)
	{
		{
			std::lock_guard<std::mutex> lock(mo_mutex);
			if (mb_block == pb_block)
			{
				//一樣的,不需要重複設置
				return;
			}
			mb_block = pb_block;
		}
		mo_cond_pop.notify_all();
		mo_cond_push.notify_all();
	}
	bool set_maxsize(uint32 pn_maxsize)
	{
		std::lock_guard<std::mutex> lock(mo_mutex);

		if ((pn_maxsize < mn_count) || (pn_maxsize <= 0))
		{
			return false;
		}

		mn_maxsize = pn_maxsize;
		return true;
	}
	uint8 group_size() const { return mn_group_size; }

protected:
	//不可拷貝不可移動
	SafeGroupQueue(const SafeGroupQueue&) = delete;
	SafeGroupQueue(SafeGroupQueue&&) = delete;
	SafeGroupQueue& operator=(const SafeGroupQueue&) = delete;
	SafeGroupQueue& operator=(SafeGroupQueue&&) = delete;
	//幀數過大,重置幀數
	void reset_frame()
	{
		// 幀數太大,消減 9/10
		const uint64 zn_sub_frame = mn_frame - (mn_frame / 10U);
		mn_frame -= zn_sub_frame;
		for (auto it = mc_frame_queue.begin(); it != mc_frame_queue.end(); ++it)
		{
			FrameGroupType & fgt = *it;
			fgt.frame -= zn_sub_frame;
		}
		for (auto it_vec = mc_group_queue.begin(); it_vec != mc_group_queue.end(); ++it_vec)
		{
			QueueType & zc_group = *it_vec;
			for (auto it_queue = zc_group->begin(); it_queue != zc_group->end(); ++it_queue)
			{
				FrameDataType & fdt = *it_queue;
				fdt.frame -= zn_sub_frame;
			}
		}
	}
protected:
	const uint8 mn_group_size; //組大小
	const uint32 mn_upgrade_frame_count;	//超過幀數沒執行,提高優先級,0表示無效
	bool mb_block;
	uint64 mn_frame; //幀數,每次加入時+1
	uint32 mn_maxsize;	
	std::vector<QueueType> mc_group_queue; //分組隊列
	std::deque<FrameGroupType> mc_frame_queue; //幀隊列
	uint32 mn_count;
	uint8 mn_first_pop_group; //當前已取出的組
	std::mutex mo_mutex;
	std::condition_variable_any mo_cond_push;
	std::condition_variable_any mo_cond_pop;
	std::atomic<int32> mb_push_waiting;
	std::atomic<int32> mb_pop_waiting;
};

 

應用示例:

int main(int argc, char* argv[])
	{
		const uint8 kGroupSize = 4;
		const uint32 kUpgradeFrameNum = 20;
		SafeGroupQueueType zc_group_queue(false, kGroupSize, kUpgradeFrameNum);

		std::cout << "input : " << std::endl;
		for (uint32 i = 0; i < 10; ++i)
		{
        uint32 zn_val = i + 1;
        zc_group_queue.push(zn_val, 0);
        std::cout << zn_val << ", ";
    }
    for (uint32 i = 10; i < 20; ++i)
    {
        uint32 zn_val = i + 1;
        zc_group_queue.push(zn_val, 1);
        std::cout << zn_val << ", ";
    }
    for (uint32 i = 20; i < 30; ++i)
    {
        uint32 zn_val = i + 1;
        zc_group_queue.push(zn_val, 2);
        std::cout << zn_val << ", ";
    }
    std::cout << std::endl;
    std::cout << "output : " << std::endl;
    while (true)
    {
        uint32 zn_val = 0;
        if (zc_group_queue.pop(zn_val))
        {
            std::cout << zn_val << ", ";
        }
        else
        {
            break;
        }
    }
    std::cout << std::endl;
    return 0;
}

輸出結果爲

input :

1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,

output :

21, 22, 1, 23, 2, 24, 3, 25, 4, 26, 5, 27, 6, 28, 7, 29, 8, 30, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 10,

 

超過 20 幀的元素 1, 2, 3, 4, 5, 6, 7, 8, 9 都被提高優先級處理。

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章