tensorflow原始碼解析之framework-resource

nt1979發表於2021-09-09

核心概念

資源大多數情況下指的是記憶體,首先我們來看下相關的序列化結構,ResourceHandleProto

message ResourceHandleProto {    //包含該資源的裝置唯一名稱
    string device = 1;    //包含該資源的容器名稱
    string container = 2;    //該資源的唯一名稱
    string name = 3;    //該資源所屬型別的唯一雜湊值,僅在當前裝置和當前執行中有效
    uint64 hash_code = 4;    //如果可以獲取的話,表示當前控制程式碼指向資源的型別,僅除錯使用
    string maybe_type_name = 5;
}

簡單來說,這是一個資源的控制程式碼,它包含了描述這個資源的位置、屬性的關鍵資訊。

resource_handle

細看一下resource_handle.h,就會發現其中的ResrouceHandle類根本就是前面proto的C++實現。之所以要單獨再實現一個C++版本的資源控制程式碼,是為了避免讓kernels依賴於protos。

resource_mgr

系統中的大量資源需要被高效的管理,這些資源型別繁多,用處又各有不同,因此TF提出了包含容器的資源管理類——ResourceMgr。三者的關係如下:

graph LRResourceMgr-->ContainerContainer-->Resource

看看ResourceMgr類中都包含了哪些私有資料成員:

class ResourceMgr {
    //...private:    //...
    typedef std::pair Key;    typedef std::unordered_map Container;    const string default_container_;    mutable mutext mu_;    std::unordered_map containers_ GUARDED_BY(mu_);    std::unordered_map debug_type_names GUARDED_BY(mu_);
}

其中,容器Container本質上是一個對映,從Key到ResourceBase*的對映,前者包含資源型別的雜湊值(想想ResourceHandleProto中的hash_code欄位)和資源的名稱,而後者就是所有資源的基類物件的指標,這個基類物件我們後面會講到。資源管理器中最核心的私有資料是containers_,它也是一個對映,把容器的名稱對映為容器指標。透過這樣一個兩層的對映,TF實現了資源管理的功能。
剛才提到了ResourceBase類,我們看一下它的實現:

class ResourceBase : public core::RefCounted {public:    virtual string DebugString() = 0;    virtual int64 MemoryUsed() const {return 0;};
};

因此,ResourceBase徒有其名,本質上只是一個提供了引用計數功能的物件。資源的使用一定要慎之又慎,提供引用計數功能也是為了方便對資源做回收。
再回到ResourceMgr類,為了透徹理解它的功能,我們再看下它包含的主要介面:

class ResourceMgr {public:    //在container容器中建立一個名為name的資源
    Status Create(const string& container, const string& name, T* resource);    //在container中查詢一個名為name的資源
    Status Lookup(const string& container, const string& name, T** resource) const;    //如果container中包含名為name的資源,填充到*resource中,否則,使用creater()建立一個資源
    Status LookupOrCreate(const string& container, const string& name, T** resource, std::function creater);    //刪除container中的名為name的資源
    Status Delete(const string& container, const string& name);    //刪除控制程式碼handle指向的資源
    Status Delete(const ResourceHandle& handle);    //刪除container中的所有資源,並刪除該container
    Status Cleanup(const string& container);    //刪除所有容器中的所有資源
    void Clear();
}

有意思的是,ResourceMgr包含了兩個Delete函式,其中一個以容器名稱和資源名稱為引數,另一個以ResourceHandle為引數,這裡資源控制程式碼的作用就很明顯了,它可以方便的指代一個資源的位置。
為了更方便的使用ResourceHandle,TF還提供了很多輔助函式,為了節省篇幅,僅列出函式名:

//產生ResourceHandleMakeResourceHandle
MakeResourceHandleToOutput
MakePerStepResourceHandle
HandleFromInput//根據ResourceHandle查詢或構造資源CreateResource
LookupResource
LookupOrCreateResource
DeleteResource

說了這麼多,resource到底是什麼呢?在TF中,有些kernel是帶狀態的,在某次執行完成後,它需要儲存一些狀態資訊,方便下次執行的時候時候。這些狀態資訊就是resource的一種,為了方便管理這些狀態資訊,才構造了資源相關的類。對於一個kernel來說,一個很自然的問題是,如果它需要將中間狀態作為resource儲存起來,它需要選擇哪個container呢?如果不同kernel的臨時狀態被隨機的存放在資源管理器中,很不方便,因此TF設計了一個類,專門用來輔助kernel,幫它找到一個合適的容器,這個類就是ContainerInfo:

class ContainerInfo {public:    Status Init(ResourceMgr* rmgr, const NodeDef& ndef, bool use_node_name_as_default);    //...private:
    ResourceMgr* rmgr_ = nullptr;    string container_;    string name_;    bool resource_is_private_to_kernel_ = false;
}

這個決定的具體過程是這樣的:

  • 如果container引數非空,就使用它,否則使用資源管理器的預設container

  • 如果shared_name引數是非空的,就使用它,否則,如果use_node_name_as_default為真,這個kernel的節點名稱被用作資源名稱,否則,建立一個專屬於當前程式的名稱
    注意,這裡的container和shared_name都是NodeDef的屬性值。
    於是就有了如下這個,幫助kernel從ctx->resource_manager()中獲取資源的函式:

Status GetResourceFromContext(OpKernelContext* ctx, const string& input_name, T** resource);

另外,resource_mgr.h還包含了如下的內容:

  • 一個判斷資源是否被初始化的OpKernel

  • 一個用來註冊op的宏,這個op可以生成某種型別的資源控制程式碼

  • 一個用於生成指定資源的控制程式碼的類

  • 一個用於註冊kernel的宏,這個kernel針對的是可以生成某種型別資源控制程式碼的op

原文出處:https://www.cnblogs.com/jicanghai/p/9535504.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4822/viewspace-2813233/,如需轉載,請註明出處,否則將追究法律責任。

相關文章