OneFlow原始碼解析:運算元指令在虛擬機器中的執行

語言: CN / TW / HK

 

撰文|鄭建華、趙露陽

 

1 

Op在虛擬機器裡的執行

 

1.1 PhysicalRun和InstructionsBuilder

 

上一篇文章OneFlow原始碼解析:Op、Kernel與直譯器提到:

PhysicalRun接受一個lambda函式作為引數,這裡即InstructionsBuilder->Call方法,該方法接受kernel、input/output的eager blob object、kernel執行的上下文作為引數。Call方法實際會完成OpCall指令的構建,並最終將其派發至vm指令列表中,等待VM實際排程執行。

這個PhysicalRun函式裡包裹著一個lambda函式:

 

 
JUST(PhysicalRun([&](InstructionsBuilder* builder) -> Maybe<void> {    return builder->Call(xxx);}));

 

其中,lambda函式接受一個InstructionsBuilder指標(builder),並呼叫builder->Call方法,用於實際完成Op指令在VM中的構建。而PhysicalRunhttps://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/framework/instructions_builder.h#L160oneflow/core/framework/instructions_builder.h中定義,其接受lambda函式作為模版引數(CallbackT):

 

 
// Make VM instructions with instruction builder and run instructions with physical/local view.template<typename CallbackT>Maybe<void> PhysicalRun(const CallbackT& Build) {  vm::InstructionList instruction_list;  InstructionsBuilder instructions_builder(&instruction_list);  JUST(Build(&instructions_builder));  JUST(vm::Run(instructions_builder.mut_instruction_list()));  return Maybe<void>::Ok();}

 

可見,PhysicalRun函式中,首先初始化一個InstructionsBuilder,然後將InstructionsBuilder指標作為引數傳給lambda函式,完成實際指令的構建;最後通過vm::Run()方法將該指令傳送至VM,等候VM實際排程和執行。Run方法如下:

 

 
Maybe<void> Run(vm::InstructionList* instruction_list) {  auto* virtual_machine = JUST(SingletonMaybe<VirtualMachine>());  JUST(virtual_machine->Receive(instruction_list));  return Maybe<void>::Ok();}

 

可以看見,Run()方法獲取了全域性單例的VM物件指標,然後通過vm的Receive()方法,將該條指令傳送給虛擬機器(所以這裡Run其實有點歧義,更貼切的意思,其實是指令傳送或傳送)

 

這個VirtualMachine->Receive方法很重要,會在後面的第2.章節中詳細展開

 

1.2 InstructionsBuilder

 

上面PhysicalRun函式中的InstructionsBuilder,類似一個指令構建的helper,InstructionsBuilder的系列方法配合指令策略(InstructionPolicy),可以幫助構建不同型別的vm指令。

 

InstructionsBuilder

https://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/framework/instructions_builder.h#L47)的定義中,我們可以看到指令的構建方法,其中常用方法如下:

 

 
// 用於lazy mode(nn.Graph)// Build VM execution instructions with NNGraph's inputs/outputs/parameters for NNGraph execution.Maybe<void> LaunchLazyJob(const vm::EagerBlobObjectListPtr& inputs,                          const vm::EagerBlobObjectListPtr& outputs,                          const vm::EagerBlobObjectListPtr& parameters,                          const std::shared_ptr<NNGraphIf>& nn_graph);

// 用於全域性同步,同步等待所有指令呼叫完成Maybe<void> GlobalSync();
// 用於Tensor記憶體釋放(歸還allocator)Maybe<void> ReleaseTensor(const std::shared_ptr<vm::EagerBlobObject>& eager_blob_object);
// 操作Tensor實際記憶體(blob)template<typename T>Maybe<void> AccessBlobByCallback(    const T tensor,    const std::function<void(ep::Stream*, const std::shared_ptr<vm::EagerBlobObject>&)>& callback,    const std::string& modifier);
// 最常用的指令構建方法,用於構造op執行所需的OpCall指令Maybe<void> Call(const std::shared_ptr<one::StatefulOpKernel>& opkernel,                   vm::EagerBlobObjectList&& input_eager_blob_objects,                   vm::EagerBlobObjectList&& output_eager_blob_objects,                   const one::OpExprInterpContext& ctx, Symbol<Stream> stream);

 

1.3 InstructionPolicy

 

InstructionPolicy

https://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/vm/instruction_policy.h#L34)——指令策略,通常用於配合InstructionsBuilder實際構建出不同的vm指令。InstructionPolicy的子類實現如下:

 

 

這些子類的InstructionPolicy可近似認為是指令型別。如,用於Op執行的OpCallInstructionPolicy、用於Tensor記憶體釋放的ReleaseTensorInstructionPolicy、用於屏障阻塞的BarrierInstructionPolicy等。

 

以Op執行為例:

 

 
JUST(PhysicalRun([&](InstructionsBuilder* builder) -> Maybe<void> {    return builder->Call(xxx);}));

 

實際上是通過InstructionsBuilder的Call方法

https://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/framework/instructions_builder.cpp#L355),配合OpCall的指令策略(OpCallInstructionPolicy),構造了OpCall指令:

 

 
Maybe<void> InstructionsBuilder::Call(    const std::shared_ptr<one::StatefulOpKernel>& opkernel,    vm::EagerBlobObjectList&& input_eager_blob_objects,    vm::EagerBlobObjectList&& output_eager_blob_objects,    const std::shared_ptr<const one::GlobalTensorInferResult>& global_tensor_infer_result,    const one::OpExprInterpContext& ctx, Symbol<Stream> stream) {  ...  ...  // 獲取當前vm stream  auto* vm_stream = JUST(Singleton<VirtualMachine>::Get()->GetVmStream(stream));  // 通過OpCallInstructionPolicy初始化OpCall指令  auto instruction = intrusive::make_shared<vm::Instruction>(      vm_stream, std::make_shared<vm::OpCallInstructionPolicy>(                     vm_stream, opkernel, std::move(input_eager_blob_objects),                     std::move(output_eager_blob_objects), global_tensor_infer_result, ctx,                     *one::CurrentDevVmDepObjectConsumeMode()));  // 指令入列表  instruction_list_->EmplaceBack(std::move(instruction));  return Maybe<void>::Ok();}

 

並將構建好的指令塞入指令列表,待後續VM排程並實際執行。

 

2 

虛擬機器的執行原理

 

2.1 VM初始化

 

OneFlow環境初始化時,會觸發VirtualMachineScope

https://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/vm/virtual_machine_scope.cpp#L24)的初始化:

 

 
VirtualMachineScope::VirtualMachineScope(const Resource& resource) {  Singleton<VirtualMachine>::New();}

 

進而觸發VM物件——VirtualMachine

https://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/vm/virtual_machine.cpp#L81)的初始化。VM作為一個Singleton物件,全域性唯一。

 

 
VirtualMachine::VirtualMachine() : disable_vm_threads_(false), scheduler_stopped_(false) {  // Class VirtualMachineEngine only cares the basic logical of vm, while class VirtualMachine  // manages threads and condition variables.  // In order to notify threads in VirtualMachineEngine, a notify callback lambda should be take as  // an argument for VirtualMachineEngine's constructor.  engine_ = intrusive::make_shared<vm::VirtualMachineEngine>();  OF_PROFILER_NAME_THIS_HOST_THREAD("_Main");  std::function<void()> SchedulerInitializer;  GetSchedulerThreadInitializer(&SchedulerInitializer);  schedule_thread_ = std::thread(&VirtualMachine::ScheduleLoop, this, SchedulerInitializer);  transport_local_dep_object_.Reset();}

 

VM初始化中最重要的內容,便是:

 

1.初始化了一個VM的執行引擎——VirtualMachineEngine

2.通過VirtualMachine::ScheduleLoop啟動了VM的排程執行緒

 

VirtualMachine::ScheduleLoop

 

VM物件只負責條件變數和執行緒管理;而主要業務邏輯處理(包括指令構建、排程、派發和執行等),則由 VirtualMachineEngine

https://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/vm/virtual_machine_engine.h#L47 )物件負責;VM初始化時還開闢了單獨的schedule執行緒用於VM引擎處理排程邏輯,在VirtualMachine::ScheduleLoop

https://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/vm/virtual_machine.cpp#L292 )中:

 

 
void VirtualMachine::ScheduleLoop(const std::function<void()>& Initializer) {  SyncVmModeGuard guard(SyncVmMode::kEnable);  Initializer();  MultiThreadScheduleCtx schedule_ctx{};  while (pending_notifier_.WaitAndClearNotifiedCnt() == kNotifierStatusSuccess) {    OF_PROFILER_RANGE_GUARD("VirtualMachine::ScheduleLoop");    auto start = std::chrono::steady_clock::now();    static constexpr int kWorkingMicroseconds = 1000;    // Every time this thread wakes up, engine_ is scheduled for about `kWorkingMicroseconds`.    // The cost of os thread switching is about 5-10 microseconds. Doing more scheduling in    // a single waiting up can reach higher performance.    do {      do {        const size_t total_inserted = engine_->total_inserted_instruction_cnt();        const size_t total_erased = engine_->total_erased_instruction_cnt();        engine_->Schedule(schedule_ctx);        if (ThreadLocalEnvBool<ONEFLOW_VM_ENABLE_SCHEDULE_YIELD>()            && total_inserted == engine_->total_inserted_instruction_cnt()            && total_erased == engine_->total_erased_instruction_cnt()) {  // nothing handled.          std::this_thread::yield();        }      } while (!engine_->SchedulerThreadUnsafeEmpty());    } while (MicrosecondsFrom(start) < kWorkingMicroseconds);  }  ScheduleUntilVMEmpty(engine_.Mutable(), schedule_ctx);  CHECK_JUST(ForEachThreadCtx(engine_.Mutable(), [&](vm::ThreadCtx* thread_ctx) -> Maybe<void> {    thread_ctx->mut_notifier()->Close();    return Maybe<void>::Ok();  }));  {    std::unique_lock<std::mutex> lock(worker_threads_mutex_);    for (const auto& worker_thread : worker_threads_) { worker_thread->join(); }  }  scheduler_stopped_ = true;}

 

ScheduleLoop 是一個近似於busy loop的while迴圈,pending_notifier_是VM內部維護的成員,實際上是 ScheduleLoop 執行緒的通知/喚醒者,其定義位於 oneflow/oneflow/core/common/notifier.h

 

 
class Notifier final { public:  OF_DISALLOW_COPY_AND_MOVE(Notifier);  Notifier() : notified_cnt_(0), is_closed_(false) {}  ~Notifier() = default;
  NotifierStatus Notify();  NotifierStatus WaitAndClearNotifiedCnt();  void Close();
 private:  size_t notified_cnt_;  std::mutex mutex_;  bool is_closed_;  std::condition_variable cond_;};

 

其主要維護了互斥鎖mutex_、執行緒是否關閉的flag is_closed_、條件變數cond_。忽略執行緒喚醒、超時相關邏輯,ScheduleLoop中最重要的事情是 engine_->Schedule(schedule_ctx) ;

 

 
while (pending_notifier_.WaitAndClearNotifiedCnt() == kNotifierStatusSuccess) {    auto start = std::chrono::steady_clock::now();    ...    do {      do {        ...        engine_->Schedule(schedule_ctx);        ...      } while (!engine_->SchedulerThreadUnsafeEmpty());    } while (MicrosecondsFrom(start) < kWorkingMicroseconds);  }

 

當VM維護的指令佇列不為空時,便不斷喚醒VM引擎執行指令排程邏輯—— engine->Schedule()

 

2.2 VM指令排程

 


   
void VirtualMachineEngine::Schedule(const ScheduleCtx& schedule_ctx) {  // Release finished instructions and try to schedule out instructions in DAG onto ready list.  if (unlikely(mut_active_stream_list()->size())) { ReleaseFinishedInstructions(schedule_ctx); }  // Try run the first barrier instruction.  if (unlikely(mut_barrier_instruction_list()->size())) { TryRunBarrierInstruction(schedule_ctx); }  // Handle pending instructions, and try schedule them to ready list.  // Use thread_unsafe_size to avoid acquiring mutex lock.  // The inconsistency between pending_instruction_list.list_head_.list_head_.container_ and  // pending_instruction_list.list_head_.list_head_.size_ is not a fatal error because  // VirtualMachineEngine::Schedule is always in a buzy loop. All instructions will get handled  // eventually.  //  VirtualMachineEngine::Receive may be less effiencient if the thread safe version  //  `pending_instruction_list().size()` used here, because VirtualMachineEngine::Schedule is more  //  likely to get the mutex lock.  if (unlikely(local_pending_instruction_list().size())) {    HandleLocalPending();  } else if (unlikely(pending_instruction_list().thread_unsafe_size())) {    // MoveTo is under a lock.    mut_pending_instruction_list()->MoveTo(mut_local_pending_instruction_list());    if (local_pending_instruction_list().size()) { HandleLocalPending(); }  }  // dispatch ready instructions and try to schedule out instructions in DAG onto ready list.  if (unlikely(mut_ready_instruction_list()->size())) {    DispatchAndPrescheduleInstructions(schedule_ctx);  }  // handle scheduler probes  if (unlikely(local_probe_list_.size())) {    HandleLocalProbe();  } else if (unlikely(probe_list_.thread_unsafe_size())) {    probe_list_.MoveTo(&local_probe_list_);    if (local_probe_list_.size()) { HandleLocalProbe(); }  }}
 

 

VM引擎維護了一系列指令列表的成員:

 

 
InstructionMutexedList pending_instruction_list_;// local_pending_instruction_list_ should be consider as the cache of pending_instruction_list_.InstructionList local_pending_instruction_list_;ReadyInstructionList ready_instruction_list_;LivelyInstructionList lively_instruction_list_;BarrierInstructionList barrier_instruction_list_;

 

  • pending相關的instruction_list是懸掛/待處理的指令列表;

  • lively相關的instruction_list是活躍的正在執行中的指令列表;

  • ready相關的instruction_list則是已完成準備工作(指令融合、指令DAG構建等)待執行的指令列表;

 

VM引擎Schedule時,會對指令佇列做相應處理,包括:

 

  • 將已完成準備工作的指令放入ready_instruction_list_中維護;

  • 嘗試執行barrier指令列表(barrier_instruction_list_)中的第一條指令;

  • 如果本地pending指令列表(local_pending_instruction_list_)非空,則通過 HandleLocalPending 方法處理這些懸掛指令(指令融合、指令執行DAG圖構建、插入ready列表)

  • 如果ready指令列表非空,則通過 DispatchAndPrescheduleInstructions 方法進行指令派發和預排程處理。

 

這裡重點介紹指令派發相關的 DispatchAndPrescheduleInstructions 方法,其中 DispatchAndPrescheduleInstructions 中最主要的是就是 DispatchInstruction 指令派發方法,這裡的指令派發可以認為實際上就是指令執行

 

2.3 VM指令派發

 

VirtualMachineEngine::DispatchInstruction  

https://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/vm/virtual_machine_engine.cpp#L372 )方法是vm引擎中的核心,其實際完成了指令的派發和實際執行,程式碼如下:

 

 
template<void (VirtualMachineEngine::*OOMHandler)(vm::Stream*, const ScheduleCtx&)>void VirtualMachineEngine::DispatchInstruction(Instruction* instruction,                                               const ScheduleCtx& schedule_ctx) {  auto* stream = instruction->mut_stream();  // Prepare  {    // 指令的Prepare    const auto& ret = TRY(instruction->Prepare());    if (unlikely(!ret.IsOk())) {      // 處理指令Prepare過程中的OOM的邏輯      if (ret.error()->has_out_of_memory_error()) {        // 讓allocator釋放不必要的cacahe,再重新執行指令的Prepare        (this->*OOMHandler)(stream, schedule_ctx);        ...      }    }  }  // 將當前指令放入running_instruction_list  stream->mut_running_instruction_list()->PushBack(instruction);  if (stream->active_stream_hook().empty()) { mut_active_stream_list()->PushBack(stream); }  // Compute  if (OnSchedulerThread(*stream)) {    // StreamPolicy的Run方法觸發指令的實際執行——Compute    stream->stream_policy().Run(instruction);  } else {    stream->mut_thread_ctx()->mut_worker_pending_instruction_list()->PushBack(instruction);    schedule_ctx.OnWorkerLoadPending(stream->mut_thread_ctx());  }}

DispatchInstruction的核心主要有2塊:

 

  • 執行指令的Prepare

  • 執行指令的Compute

 

Prepare負責一些指令執行前的準備;Compute則是實際的指令執行,指令執行並不是直接通過instruction->Run而是在StreamPolicy的Run方法中完成的,這裡又涉及到一個StreamPolicy物件。

 

StreamPolicy::Run

 

StreamPolicy

https://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/vm/stream_policy.h#L46 )是個虛基類:

 

 
class StreamPolicy { public:  virtual ~StreamPolicy() = default;
  virtual ep::Stream* stream() = 0;  virtual vm::Allocator* mut_allocator() = 0;  virtual DeviceType device_type() const = 0;
  virtual void InitInstructionStatus(const Stream& stream,                                     InstructionStatusBuffer* status_buffer) const = 0;  virtual void DeleteInstructionStatus(const Stream& stream,                                       InstructionStatusBuffer* status_buffer) const = 0;  virtual bool QueryInstructionStatusDone(const Stream& stream,                                          const InstructionStatusBuffer& status_buffer) const = 0;  virtual void Run(Instruction* instruction) const = 0;
  virtual bool OnSchedulerThread(StreamType stream_type) const;  virtual bool SupportingTransportInstructions() const = 0;
 protected:  StreamPolicy() = default;};
  • stream()方法返回ep::Stream指標,指向的是針對不同平臺的ep::stream物件。

  • mut_allocator()方法返回一個vm的Allocator指標,用於記憶體分配/釋放。

  • InitInstructionStatus/QueryInstructionStatusDone/DeleteInstructionStatus用於建立/查詢/銷燬指令執行狀態

  • Run方法則是核心,定義了該Stream具體執行時的邏輯。

這裡的ep在oneflow中是execution provider的縮寫,ep從本質上來講就是一個針對不同硬體平臺的executor抽象。

 

StreamPolicy相關的繼承和子類如下:

 

 

 

看一下EpStreamPolicyBase的Run方法( https://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/vm/ep_stream_policy_base.cpp#L41 ):

 

 
void EpStreamPolicyBase::Run(Instruction* instruction) const {  ...  auto* stream = instruction->mut_stream();  EpStreamPolicyBase* ep_stream_policy_base =      dynamic_cast<EpStreamPolicyBase*>(stream->mut_stream_policy());  ...  auto* ep_device = ep_stream_policy_base->GetOrCreateEpDevice();  ep_device->SetAsActiveDevice();  instruction->Compute();  ...}

 

首先獲取了該stream對應的ep device,然後執行了instruction的Compute方法,即指令的實際執行

 

2.4 VM執行執行

 

以OpCall指令為例,看一下op指令的 Compute

https://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/vm/op_call_instruction_policy.cpp#L201 ):

 

 
void OpCallInstructionPolicy::Compute(vm::Instruction* instruction) {  OpCallInstructionUtil::Compute(this, instruction);}

 

OpCallInstructionPolicy方法呼叫了OpCallInstructionUtil的Compute方法:

 

 

上面我們可以看到,在指令Prepare時,做了output tensor記憶體分配;而指令Compute中最重要的方法是:

 

  • TryInitOpKernelStateAndCache——初始化一些kernel計算需要的狀態或快取

  • OpKernelCompute——執行該op對應的kernel,kernel內主要是實際的op計算邏輯

 

 

user kernel統一位於:oneflow/user/kernels目錄下,.cpp通常對應cpu kernel邏輯;.cu為cuda kernel邏輯。到這裡,就會觸發user_kernel的Compute方法,不同op的kernel計算邏輯不同,以rele op為例,實際Compute過程可參考文章《運算元在深度學習框架中的執行及interpreter》的第5小節

 

2.5 VM指令傳送

 

這裡的VM指令傳送,指的是VM外部的指令傳送過程(不是VM內部的指令派發)。上面2.1~2.3小節介紹了VM以及VM引擎的初始化、VM內部指令的排程、派發和實際執行的過程,那麼這些指令是如何傳送到VM的呢?答案是:在1.1小節中提到的PhysicalRun

 

PhysicalRun 最終會觸發 VirtualMachine->Receive 方法,並通過VirtualMachineEngine的Receive方法完成外部指令 -> VM內部的傳送。

 

VirtualMachineEngine的Receive方法( https://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/vm/virtual_machine_engine.cpp#L400 )主要將該指令通過MoveFrom方法push back到指令懸掛列表(pending_instruction_list_)的末尾,從而完成指令的傳送。

 

 
// Returns true if old scheduler_pending_instruction_list is emptyMaybe<bool> VirtualMachineEngine::Receive(InstructionList* compute_instruction_list) {  OF_PROFILER_RANGE_GUARD("vm:Receive");#ifdef OF_ENABLE_PROFILER  INTRUSIVE_UNSAFE_FOR_EACH_PTR(compute_instruction, compute_instruction_list) {    OF_PROFILER_RANGE_GUARD(compute_instruction->DebugName());  }#endif
  bool old_list_empty = mut_pending_instruction_list()->MoveFrom(compute_instruction_list);  return old_list_empty;}

 

小結

 

至此,Op執行相關的流程算是大體串了一遍。一句 flow.relu() 後面會涉及這麼多內容。但這裡其實也只關注了主幹邏輯,忽略了中間大量的細節。

 

流程的梳理只是第一步,還需要從中歸納總結一些概念和概念之間的關係,再結合公開資料反推印證設計理念的落地實現。

 

不過目前對程式碼和設計的瞭解還很膚淺,下面的內容純屬大膽猜測。

 

3.1 UserOpExpr

 

UserOpExpr表示UserOp執行時所需的上下文,其實UserOp只是Op中的一種。下圖展示了不同Op的繼承關係。可以看到tensor從local/global之間的轉換等也都涉及不同的OpExpr。

 

 

 

3.2 Op執行的巨集觀脈絡

 

從上面的類關係圖出發,以核心類為節點,也能看出Op執行流程的巨集觀脈絡。整個流程大體在下面這些角色之間流轉:

 

  • ReluFunctor

  • UserOpExpr

  • Interpreter

  • PhysicalRun

  • VirtualMachine->Receive

  • VirtualMachine->ScheduleLoop ...

 

3.3 虛擬機器執行和排程總結

 

VM -> ScheduleLoop

       VM引擎Schedule

               處理懸掛指令(HandleLocalPending)

               指令派發(DispatchInstruction)

                      準備(instruction->Prepare)

                      執行(StreamPolicy.Run -> instruction->Compute)

               指令預排程

 

VM -> Receive

         VM引擎 -> Receive

               指令入懸掛列表

 

通常,我們習慣在動態圖模式下訓練深度學習網路,使用Python搭建網路,並通過各種op進行前向、反向、loss計算、除錯debug等過程,這些Python程式碼可以看作是動態的op的執行序列。

 

OneFlow虛擬機器將op執行序列抽象成了各種VM指令序列。OneFlow的虛擬機器會對這些op執行序列進行動態翻譯並生成VM指令序列,通過PhysicalRun構造完畢後,動態地將指令傳送至VM的懸掛列表中維護。這些指令或在時間上存在先後順序,或在資料上存在依賴關係,所以懸掛列表中的指令後續會被虛擬機器進行一些指令融合、指令連邊、動態構建指令DAG圖的過程,然後移入就緒列表中維護,等待虛擬機器排程並實際執行。虛擬機器負責維護若干個指令佇列,以及指令在這些佇列之間的狀態轉換。

 

OneFlow虛擬機器還統一了動態圖模式(Eager Mode)和靜態圖模式(Lazy Mode)。靜態圖模式下,通過nn.Graph編譯出深度學習網路的Job,這個Job同樣被虛擬機器抽象成了VM指令並接受虛擬機器的排程和執行。大膽猜測一下,這也為日後動靜轉換、更極致的效能優化埋下了伏筆。

 

參考資料

 

 

其他人都在看

歡迎體驗OneFlow v0.8.0:https://github.com/Oneflow-Inc/oneflow/

 


​​

本文分享自微信公眾號 - OneFlow(OneFlowTechnology)。
如有侵權,請聯絡 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。