緩存置換:用c++實現最不經常使用(LFU)算法

在探討緩存置換算法時,我們曾詳細解讀過LRU(Least Recently Used)算法,它憑借 “最近最少使用” 的策略在緩存管理領域大放異彩。今天,讓我們將目光聚焦于另一種重要的緩存置換算法 ——LFU(Least Frequently Used 最不經常使用),深入探究它的原理、實現方式、應用場景以及與 LRU 的差異。

一、LFU 算法核心原理

LFU 算法的核心思想是根據數據的訪問頻率來決定淘汰對象。與 LRU 依據數據最近的訪問時間不同,LFU 更關注數據被訪問的頻繁程度。它認為,在過去一段時間內訪問次數最少的數據,在未來被訪問的可能性也相對較低,因此當緩存空間不足時,優先淘汰這類數據。

想象一下圖書館的藏書管理,有些書籍經常被借閱,而有些則鮮有人問津。LFU 算法就如同圖書館管理員,會定期清理那些借閱次數最少的書籍,為新購入的書籍騰出空間。這樣,圖書館的書架上始終保留著最受歡迎、被借閱可能性最高的書籍,提高了讀者找到所需書籍的效率。在緩存管理中,LFU 算法通過記錄每個數據的訪問次數,在緩存滿時淘汰訪問次數最少的數據,以此來維持緩存的高效運行。

二、LFU 算法的數據結構實現

實現 LFU 算法需要借助合適的數據結構,我們的代碼實現使用的是哈希表和多個雙向鏈表的組合。

  1. 哈希表:哈希表則用于快速定位數據。它以數據的鍵作為索引,存儲對應數據在雙向鏈表中的節點指針。這樣,在查找數據時,通過哈希表可以在?O(1)?的時間復雜度內找到對應的數據節點,然后根據節點指針在雙向鏈表中進行操作。例如,在一個基于鍵值對存儲的內存緩存中,通過哈希表可以快速找到特定鍵對應的數據,提高緩存的查詢效率。

不清楚哈希表的可以看我以前的文章c++ 手寫STL篇(三)實現hashtable

  1. 雙向鏈表:在緩存管理中,雙向鏈表常被用于維護數據的訪問順序,鏈表里的每個節點都代表著一個緩存數據。LRU 和 LFU 算法都用到了雙向鏈表,但使用方式有所不同。

    在 LRU 算法里,只有一個雙向鏈表。這個鏈表有個很清晰的規則:頭部的節點代表著最近使用的數據,就好像是你剛剛才翻閱過的資料,被放在了最顯眼的位置;而尾部的節點則代表最近最少使用的數據,類似于你很久都沒碰過的舊文件,被放在了最角落。

    LFU 算法就不太一樣了。在 LFU 中存在多個雙向鏈表,那些訪問頻次相同的數據節點會被串聯在同一個雙向鏈表中。比如說,有一些數據都只被訪問過 1 次,它們就會被放在一個雙向鏈表;另外一些都被訪問過 3 次的數據,則會在另一個雙向鏈表。為了能快速找到不同訪問頻次對應的雙向鏈表,LFU 通過哈希表將訪問頻次和對應的雙向鏈表建立映射關系,這樣就能很方便地管理和查找數據了。雙向鏈表的優勢在于,插入和刪除操作的時間復雜度都是?O(1)?,這對于頻繁的緩存操作來說非常高效。例如,在一個頻繁讀寫的數據庫緩存中,數據的插入和刪除操作會經常發生,雙向鏈表的這種特性可以保證緩存操作的快速執行。

不清楚雙向鏈表的可以看這篇文章?c++ 手寫STL篇(二) 用雙向鏈表實現list核心功能_c++ 二維鏈表手寫-CSDN博客

通過哈希表和雙向鏈表的協同工作,LFU 算法能夠高效地管理數據的訪問次數,并快速找到訪問次數最少的數據進行淘汰。

三、LFU 算法的操作流程

  1. 查詢操作:當進行查詢操作時,首先在哈希表中查找數據的鍵。如果找到,說明數據在緩存中,將對應數據的訪問次數加 1,并調整雙向鏈表以保持堆的有序性(因為訪問次數發生了變化)。如果未找到,則說明數據不在緩存中,需要從其他數據源獲取數據(如磁盤),然后將數據插入到緩存中。

  2. 插入操作:插入操作分為兩種情況。若緩存未滿,直接將數據插入到哈希表和雙向鏈表中,并將其訪問次數初始化為 1。若緩存已滿,則先從訪問頻次最小的雙向鏈表中取出元素,將其從哈希表和雙向鏈表中刪除,然后再將新數據插入到哈希表和雙向鏈表中,并將其訪問次數設置為 1。

  3. 刪除操作:刪除操作相對簡單,在哈希表中查找要刪除數據的鍵,找到后從哈希表和雙向鏈表中刪除對應的元素即可。

四、LFU 算法的應用場景

  1. CDN 緩存優化:內容分發網絡(CDN)的主要任務是將內容緩存到離用戶更近的節點,以加快用戶的訪問速度。在 CDN 中,LFU 算法可用于管理緩存內容。例如,在視頻網站的 CDN 系統中,視頻片段會被緩存到各個節點。隨著用戶觀看不同的視頻,CDN 節點的緩存空間會逐漸被占滿。LFU 算法會根據視頻片段的訪問次數,淘汰那些訪問次數最少的片段,為新的熱門視頻片段騰出空間。這樣,用戶在訪問視頻時,CDN 節點能夠更快地提供熱門視頻內容,提升用戶體驗。(在流媒體中cdn是成本最高環節,因此如何節省資源很重要)

  2. 搜索引擎緩存管理:搜索引擎在處理大量的搜索請求時,會將經常查詢的關鍵詞及其搜索結果緩存起來。LFU 算法可以幫助搜索引擎決定淘汰哪些緩存內容。當緩存空間不足時,優先淘汰那些搜索次數最少的關鍵詞及其結果。比如,在某一時間段內,一些熱門話題的搜索頻率很高,而一些生僻關鍵詞的搜索次數較少。LFU 算法會將這些生僻關鍵詞的緩存結果淘汰,保證緩存中保留的是更有可能被再次搜索的熱門關鍵詞及其結果,提高搜索引擎的響應速度。

  3. 游戲資源緩存策略:在游戲運行過程中,會加載大量的資源,如紋理、模型等。為了提高游戲的加載速度和運行效率,游戲會將這些資源緩存到內存中。LFU 算法可以根據資源的使用頻率,淘汰那些使用次數最少的資源。例如,在一款開放世界游戲中,玩家在某個區域頻繁活動,該區域的游戲資源訪問頻率較高,而一些偏遠區域的資源訪問次數較少。LFU 算法會優先淘汰這些偏遠區域的資源,為當前區域更需要的資源騰出空間,確保游戲能夠流暢運行。

五、LFU 與 LRU 的對比分析

  1. 原理差異:LRU 基于數據的訪問時間,優先淘汰最近最少使用的數據;而 LFU 基于數據的訪問頻率,優先淘汰訪問次數最少的數據。這意味著 LRU 更注重數據的時效性,而 LFU 更關注數據的使用頻繁程度。

  2. 適用場景差異:LRU 適用于數據訪問具有時間局部性的場景,即近期訪問過的數據在未來一段時間內再次被訪問的概率較高。例如,在瀏覽器緩存中,用戶通常會在短時間內回訪之前瀏覽過的網頁,LRU 算法能夠很好地適應這種場景。而 LFU 適用于數據訪問頻率相對穩定的場景,對于那些訪問頻率變化較大的數據,LFU 可能無法及時適應。比如在電商平臺中,商品的熱門程度可能會隨著時間和促銷活動發生較大變化,此時 LFU 算法可能不太適用。

  3. 實現復雜度差異:LFU 算法由于需要記錄數據的訪問次數并維護一個有序的數據結構,其實現復雜度相對較高。而 LRU 算法通常借助雙向鏈表和哈希表即可實現,相對來說實現較為簡單。

六、LFU 算法的優缺點

  1. 優點:能更準確地反映數據的使用頻繁程度,在數據訪問頻率穩定的場景下,能夠有效提高緩存命中率,減少數據的重復讀取。例如在一些數據訪問模式較為固定的企業級應用中,LFU 算法可以充分發揮其優勢,提升系統性能。

  2. 缺點:需要額外的空間來記錄數據的訪問次數,并且維護有序數據結構的操作也會增加時間復雜度;對于過時的熱點數據,因為其訪問頻次高,難以被淘汰;在高并發環境下,頻繁更新訪問次數和調整有序數據結構可能會帶來性能瓶頸;對于剛加入緩存的數據可能應為頻次很低而被快速淘汰,即便這項是近期熱點數據(短期熱點數據無法及時緩存)。

七、用c++實現LFU算法

LFU 緩存置換算法是一種經典且實用的算法,在眾多領域都有廣泛的應用。通過深入理解其原理、數據結構選擇、操作流程以及應用場景,我們可以更好地將其應用到實際項目中,提升系統的性能。接下來,我們將通過手撕代碼的方式,詳細展示如何實現一個 LFU 緩存。

代碼參考自:https://github.com/youngyangyang04/KamaCache

接口類,對緩存置換算法設計統一接口實現隔離?:

#ifndef CACHEPOLICY_H
#define CACHEPOLICY_Htemplate<typename Key, typename Value>
class CachePolicy
{
public:virtual ~CachePolicy() {};virtual void put(Key key, Value value) = 0;virtual bool get(Key key,Value& value) = 0;virtual Value get(Key key) = 0;
};#endif

雙向鏈表實現,包含節點結構體實現,用于存儲鍵值對和前向后向節點指針,并且實現了雙向鏈表基本功能,例如判斷是否為空,增加、刪除節點、獲取鏈表頭節點等方法

template<typename Key, typename Value> class LfuCache;template<typename Key, typename Value>
class FreqList
{
public:	struct Node//節點結構體,存儲鍵值對和前向后向節點指針{int freq;Key key;Value value;std::shared_ptr<Node> pre;std::shared_ptr<Node> next;Node() :freq(1), pre(nullptr), next(nullptr) {}Node(Key key, Value value) :key(key), value(value), freq(1), pre(nullptr), next(nullptr) {}};using NodePtr = std::shared_ptr<Node>;int freq_;NodePtr head_;NodePtr tail_;public:	explicit FreqList(int n)://expicit禁止隱式構造freq_(n)//構建虛擬頭節點和虛擬尾節點,然后頭尾相連完成初始化{head_ = std::make_shared<Node>();tail_ = std::make_shared<Node>();head_->next = tail_;tail_->pre = head_;}//判空bool isEmpty() const{return head_->next == tail_;}//增加節點void addNode(NodePtr node){if(!node || !head_ || !tail_) return;node->pre = tail_->pre;node->next = tail_;tail_->pre->next = node;tail_->pre = node;}//刪除節點void removeNode(NodePtr node){if(!node || !head_ || !tail_) return;if(!node->pre || !node->next) return;node->pre->next = node->next;node->next->pre = node->pre;node->pre = nullptr;node->next = nullptr;}//獲取頭節點NodePtr getFirstNode() const {return head_->next;}//類作為友元,方便LfuCache類直接訪問FreqList類的私有成員friend class LfuCache<Key,Value>;
};

在 LFU 類中,實現了數據的增刪改查功能。這里要著重講一下計數操作,它是 LFU 算法的關鍵環節。想象一下,緩存就像是一個熱鬧的集市,里面的每個攤位(數據節點)都有自己的客流量(訪問頻次)。如果不對每個攤位的客流量計數加以限制,那些長期火爆的攤位(熱數據),客流量計數就會像火箭一樣不斷飆升。這不僅會占用大量的 “記錄空間”,就好比攤位的賬本越寫越厚,最后可能連存放賬本的地方都沒有了(占用空間甚至計數溢出)。而且,一些曾經很火但現在已經過時的攤位(過期的熱點數據),因為之前積累的客流量太大,很難被擠出這個集市(難以被清除出緩存)。

為了解決這些問題,采用了一種巧妙的辦法 —— 設置最大平均訪問次數限制。它就像是給集市的熱鬧程度設定了一個上限。通過curTotalNum_這個 “總客流量計數器”,把集市里所有攤位的客流量都加起來,再除以攤位總數,就能得到平均每個攤位的客流量(平均訪問次數curAverageNum_)。當這個平均客流量超過了設定的最大平均客流量maxAverageNum_后,就會啟動handleOverMaxAverageNum這個 “攤位熱度調整員”。它會把每個攤位的客流量都減去最大平均客流量的一半,這樣一來,每個攤位的客流量上限就被控制住了,那些長期賴在集市里的過期熱門攤位也終于有機會被清理出去了,集市(緩存)也就能夠保持高效的運營狀態啦。

template<typename Key, typename Value>
class LfuCache : public CachePolicy<Key,Value>
{
public:using Node = typename FreqList<Key,Value>::Node;using NodePtr = std::shared_ptr<Node>;using NodeMap = std::unordered_map<Key,NodePtr>;LfuCache(int capacity, int maxAverageNum = 10):capacity_(capacity),minFreq_(INT8_MAX),maxAverageNum_(maxAverageNum),curAverageNum_(0),curTotalNum_(0) {}~LfuCache() override = default;//存入緩存void put(Key key, Value value) override{if(capacity_ <= 0) return;std::lock_guard<std::mutex> lock(mutex_);auto it = nodeMap_.find(key);if(it != nodeMap_.end()){it->second->value = value;getInternal(it->second,value);return;}putInternal(key,value);}//讀取緩存bool get(Key key, Value& value) override{std::lock_guard<std::mutex> lock(mutex_);auto it = nodeMap_.find(key);if(it != nodeMap_.end()){getInternal(it->second,value);return true;}return false;}//get重載Value get(Key key) override{Value value{};get(key,value);return value;}//清除所有緩存void purge(){nodeMap_.clear();freqToFreqList_.clear();}private:void putInternal(Key key, Value value);void getInternal(NodePtr key,Value& value);void kickOut();//根據最小訪問計數minFreq_清除最不經常使用節點void removeFromFreqList(NodePtr node);//從雙向鏈表中移除節點void addToFreqList(NodePtr node);//向雙向鏈表加入節點void addFreqNum();//訪問頻次總數加1并計算當前平均訪問次數,如果大于最大值調用節點處理函數void decreaseFreqNum(int num);//訪問頻次總數見num并計算當前平均訪問次數void handleOverMaxAverageNum();//節點處理函數,對每個節點訪問計數減去最大平均訪問計數的一半,并調整freqToFreqList_這個哈希表void updateMinFreq();//遍歷所有節點,找出最小訪問計數private:int capacity_;//容量int minFreq_;//所有節點中訪問計數的最小值int maxAverageNum_;//最大平均訪問次數int curAverageNum_;//當前緩存的平均訪問計數int curTotalNum_;//當前緩存中所有節點的訪問計數總和std::mutex mutex_;NodeMap nodeMap_;//節點key與節點指針映射的哈希表std::unordered_map<int,FreqList<Key,Value>*> freqToFreqList_;//訪問計數與雙向鏈表映射的哈希表 
};

下面是私有成員方法的實現

template<typename Key, typename Value>
void LfuCache<Key,Value>::getInternal(NodePtr node, Value& value)
{value = node->value;removeFromFreqList(node);node->freq++;addToFreqList(node);
//判斷并調整最小訪問計數值if(node->freq - 1 == minFreq_ && freqToFreqList_[node->freq - 1]->isEmpty()){minFreq_++;}addFreqNum();
}template<typename Key, typename Value>
void LfuCache<Key,Value>::putInternal(Key key, Value value)
{
//如果緩存已滿,清除最不經常使用節點if(nodeMap_.size() >= capacity_){kickOut();}NodePtr node = std::make_shared<Node>(key,value);nodeMap_[key] = node;addToFreqList(node);addFreqNum();minFreq_ = std::min(minFreq_,1);
}template<typename Key, typename Value>
void LfuCache<Key,Value>::kickOut()
{NodePtr node = freqToFreqList_[minFreq_]->getFirstNode();removeFromFreqList(node);nodeMap_.erase(node->key);decreaseFreqNum(node->freq);
}template<typename Key, typename Value>
void LfuCache<Key,Value>::addFreqNum()
{curTotalNum_++;if(nodeMap_.empty()){curAverageNum_ = 0;}else{curAverageNum_ = curTotalNum_ / nodeMap_.size();}if(curAverageNum_ > maxAverageNum_){handleOverMaxAverageNum();}
}template<typename Key, typename Value>
void LfuCache<Key,Value>::decreaseFreqNum(int num)
{curTotalNum_ -= num;if(nodeMap_.empty()){curAverageNum_ = 0;}else{curAverageNum_ = curTotalNum_ / nodeMap_.size();}
}template<typename Key, typename Value>
void LfuCache<Key,Value>::removeFromFreqList(NodePtr node)
{if(!node) return;auto freq = node->freq;freqToFreqList_[freq]->removeNode(node);
}template<typename Key, typename Value>
void LfuCache<Key,Value>::addToFreqList(NodePtr node)
{if(!node) return;auto freq = node->freq;if(freqToFreqList_.find(freq) == freqToFreqList_.end()){freqToFreqList_[freq] = new FreqList<Key,Value>(node->freq);}freqToFreqList_[freq]->addNode(node);
}
//這個操作邏輯有點像hashtable的重哈希擴容
template<typename Key, typename Value>
void LfuCache<Key,Value>::handleOverMaxAverageNum()
{if(nodeMap_.empty()) return;for(auto it = nodeMap_.begin(); it != nodeMap_.end(); it++){if(!it->second) continue;NodePtr node = it->second;//因為會改變訪問計數值,其在freqList中的映射位置也會改變,所以先從雙向鏈表內取出removeFromFreqList(node);//遍歷所有節點,對節點訪問計數減去最大平均計數值的一半,如果減后值小于0,則置為1node->freq -= maxAverageNum_ / 2;if(node->freq < 1) node->freq = 1;//插入雙向鏈表addToFreqList(node);}updateMinFreq();
}template<typename Key, typename Value>
void LfuCache<Key,Value>::updateMinFreq()
{minFreq_ = INT8_MAX;for(const auto& pair : freqToFreqList_){if(pair.second && !pair.second->isEmpty()){minFreq_ = std::min(minFreq_,pair.first);}}if(minFreq_ == INT8_MAX)minFreq_ = 1;
}

通過以上代碼也可以看出它和LRU有相同的鎖粒度大問題,這樣的話高并發下會消耗大量資源用于線程同步等待,這個問題怎么解決呢?其實可以把一個大的緩存池分成幾個小的緩存池,就好比多個人都要從一個倉庫取貨,但倉庫每次只能進一個人,如果多個人要取貨那就必須得排隊等待,哪怕每個人要取的貨物不同,既然這樣那不如把一個大倉庫分成若干個小倉庫,每個人取得貨物可能分布在不同倉庫內,哪怕仍然有好幾個人要的貨物碰巧在同一個倉庫,那隊伍也不會排的像原先那么長。這樣就實現了分流。

template<typename Key, typename Value>
class HashLfuCache
{
public:HashLfuCache(size_t capacity, int sliceNum, int maxAverageNum = 10):	sliceNum_(sliceNum > 0 ? sliceNum : std::thread::hardware_concurrency()),capacity_(capacity){size_t sliceSize = std::ceil(capacity_ / static_cast<double>(sliceNum_));for(size_t i = 0; i < sliceNum_; i++){lfuSliceCaches_.emplace_back(new LfuCache<Key,Value>(sliceSize,maxAverageNum));}}void put(Key key, Value value){size_t sliceIndex = Hash(key) % sliceNum_;return lfuSliceCaches_[sliceIndex]->put(key,value);}bool get(Key key, Value& value){size_t sliceIndex = Hash(key) % sliceNum_;return lfuSliceCaches_[sliceIndex]->get(key,value);}Value get(Key key){Value value{};get(key,value);return value;}void purge(){for(auto& lfuSliceCache : lfuSliceCaches_){lfuSliceCache.purge();}}private:size_t Hash(Key key){std::hash<Key> hashFunc;return hashFunc(key);}private:size_t capacity_;int sliceNum_;std::vector<std::unique_ptr<LfuCache<Key, Value>>> lfuSliceCaches_;
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/80065.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/80065.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/80065.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

深度學習模型的部署實踐與Web框架選擇

引言 在深度學習項目的完整生命周期中&#xff0c;模型訓練只是第一步&#xff0c;將訓練好的模型部署到生產環境才能真正發揮其價值。本文將詳細介紹模型部署的核心概念、常見部署方式以及三種主流Python Web框架的對比分析&#xff0c;幫助開發者選擇最適合自己項目的技術方…

多功能氣體檢測報警系統,精準監測,守護安全

在化學品生產、石油化工、礦山、消防、環保、實驗室等領域&#xff0c;有毒有害氣體泄漏風險嚴重威脅工作人員和環境安全。化工企業生產中易產生大量可燃有毒氣體&#xff0c;泄漏達一定濃度易引發爆炸、中毒等重大事故&#xff1b;礦井下瓦斯、一氧化碳等有害氣體的濃度實時監…

lvgl多語言設置

搭建開發環境 安裝node.js 安裝node.js&#xff0c;點擊進入官網地址 安裝lv_i18n lv_i18n項目地址&#xff1a;Github&#xff1a;https://github.com/lvgl/lv_i18ngit運行命令安裝lv_i18n&#xff1a;npm i lv_i18n -g。測試命令&#xff1a;lv_i18n -h 搭建過程報錯 …

線程池技術

線程池基本概念 線程池就是在任務還沒有到來前&#xff0c;預先創建一定數量的線程放入空閑列表。這些線程都是處于阻塞狀態&#xff0c;不消耗CPU&#xff0c;但占用較小的內存空間。 當新任務到來時&#xff0c;緩沖池選擇一個空線程&#xff0c;把任務傳入此線程中運行&…

Go語言中的并發編程--詳細講解

文章目錄 Go語言并發編程**簡單介紹**goroutine channel 實現并發和并行for循環開啟多個協程Channel管道goroutine 結合 channel 管道**goroutine 結合 channel打印素數**單向管道Select多路復用Goroutine Recover解決協程中出現的PanicGo中的并發安全和互斥鎖 Go語言并發編程 …

C# NX二次開發:投影曲線和偏置曲線UFUN函數詳解

大家好&#xff0c;今天要講的是關于投影曲線和偏置曲線相關的函數。 &#xff08;1&#xff09;UF_CURVE_create_proj_curves1&#xff1a;這個函數的定義為創建投影曲線。 Defined in: uf_curve.h Overview Creates projection curves. Objects to project may be poi…

用R語言+隨機森林玩轉遙感空間預測-基于R語言機器學習遙感數據處理與模型空間預測技術及實際項目案例分析

遙感數據具有高維度、非線性及空間異質性等特點&#xff0c;傳統分析方法往往難以充分挖掘其信息價值。機器學習技術的引入為遙感數據處理與模型預測提供了新的解決方案&#xff0c;其中隨機森林&#xff08;Random Forest&#xff09;以其優異的性能和靈活性成為研究者的首選工…

unity 導入圖片后,可選擇精靈表自動切片,并可以導出為png

腳本源代碼&#xff1a; #if UNITY_EDITOR using UnityEditor; using UnityEngine; using System.IO; using UnityEditorInternal; using System.Collections.Generic; using System;public class TextureImporterWindow : EditorWindow {private string folderPath "D:…

使用 Azure DevSecOps 和 AIOps 構建可擴展且安全的多區域金融科技 SaaS 平臺

引言 金融科技行業有一個顯著特點&#xff1a;客戶期望能夠隨時隨地即時訪問其財務數據&#xff0c;并且對宕機零容忍。即使是短暫的中斷也會損害用戶的信心和忠誠度。與此同時&#xff0c;對數據泄露的擔憂已將安全提升到整個行業的首要地位。 在本文中&#xff0c;我們將探…

基于Django框架開發的B2C天天生鮮電商平臺

天天生鮮 介紹 天天生鮮是一個基于Django框架開發的B2C(Business-to-Customer)電商平臺&#xff0c;專注于生鮮食品的在線銷售。該項目采用了主流的Python Web開發框架Django&#xff0c;結合MySQL數據庫、Redis緩存等技術&#xff0c;實現了一個功能完整、界面友好的電商網站…

ASP.NET MVC4 技術單選及多選題目匯編

一、單選題&#xff08;共50題&#xff0c;每題2分&#xff09; 1、ASP.NET MVC4 的核心架構模式是什么&#xff1f; A. MVP B. MVVM C. MVC D.三層架構 答案&#xff1a;C 2、在 MVC4 中&#xff0c;默認的路由配置文件名是&#xff1f; A. Global.asax B. RouteConfig.cs C.…

26屆秋招收割offer指南

26屆暑期實習已經陸續啟動&#xff0c;這也意味著對于26屆的同學們來說&#xff0c;“找工作”已經提上了日程。為了幫助大家更好地準備暑期實習和秋招&#xff0c;本期主要從時間線、學習路線、核心知識點及投遞幾方面給大家介紹&#xff0c;希望能為大家提供一些實用的建議和…

數據中心機電建設

電氣系統 供配電系統 設計要求&#xff1a;數據中心通常需要雙路市電供電&#xff0c;以提高供電的可靠性。同時&#xff0c;配備柴油發電機組作為備用電源&#xff0c;確保在市電停電時能及時為關鍵設備供電。根據數據中心的規模和設備功耗&#xff0c;精確計算電力負荷&…

每日一題洛谷P1025 [NOIP 2001 提高組] 數的劃分c++

P1025 [NOIP 2001 提高組] 數的劃分 - 洛谷 (luogu.com.cn) #include<iostream> using namespace std; int n, k; int res 0; void dfs(int num,int step,int sum) {//判斷if (sum n) {if (step k) {res;return;}}if (sum > n || step k)return;//搜索for (int i …

大模型推理--從零搭建大模型推理服務器:硬件選購、Ubuntu雙系統安裝與環境配置

自從大模型火了之后就一直想自己組裝一臺機器去深入研究一下大模型&#xff0c;奈何囊中羞澀&#xff0c;遲遲也沒有行動。在下了很大的勇氣之后&#xff0c;終于花了接近4萬塊錢組裝了一臺臺式機&#xff0c;下面給大家詳細介紹一下我的裝機過程。 1.硬件配置 研究了一周&am…

第35周Zookkeeper+Dubbo Dubbo

Dubbo 詳解 一、Dubbo 是什么 官網與定義 Dubbo 是一款高性能、輕量級的開源服務框架&#xff0c;其官網為 double.apache.org&#xff0c;提供中文版本&#xff08;網址含 “zh”&#xff09;。 核心能力 Dubbo 具備六大核心能力&#xff1a; 面向接口代理的高性能 RPC …

NX二次開發——BlockUI 彈出另一個BlockUI對話框

最近在研究&#xff0c;裝配體下自動導出BOM表格中需要用到BlockUI 彈出另一個BlockUI對話框。通過對網上資料進行整理總結&#xff0c;具體如下&#xff1a; 1、明確主對話框、子對話框1和子對話框2 使用BlockUI創建.cpp和.hpp文件&#xff0c;dlx文件內容如下所示 主對話框…

PostgreSQL 系統管理函數詳解

PostgreSQL 系統管理函數詳解 PostgreSQL 提供了一系列強大的系統管理函數&#xff0c;用于數據庫維護、監控和配置。這些函數可分為多個類別&#xff0c;以下是主要功能的詳細說明&#xff1a; 一、數據庫配置函數 1. 參數管理函數 -- 查看所有配置參數 SELECT name, sett…

【2025軟考高級架構師】——計算機網絡(9)

摘要 全文主要圍繞計算機網絡相關知識展開&#xff0c;包括域名服務器查詢方式、網絡規劃與設計的關鍵技術、雙協議棧與隧道技術、層次化網絡設計、網絡冗余設計以及高可靠和高可用性等方面&#xff0c;旨在為軟考高級架構師的備考提供知識參考。 1. 通信網絡架構圖 2. 通信架…

yolov8n-obb訓練rknn模型

必備&#xff1a; 準備一臺ubuntu22的服務器或者虛擬機&#xff08;x86_64&#xff09; 1、數據集標注&#xff1a; 1&#xff09;推薦使用X-AnyLabeling標注工具 2&#xff09;標注選【旋轉框】 3&#xff09;可選AI標注&#xff0c;再手動補充&#xff0c;提高標注速度 …