OneFlow原始碼解析:運算元指令在虛擬機器中的執行
撰文|鄭建華、趙露陽
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中的構建。而PhysicalRun(http://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/framework/instructions_builder.h#L160)在 oneflow/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
(http://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
(http://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方法
(http://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
(http://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
(http://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
( http://github.com/Oneflow-Inc/oneflow/blob/88f147d50e75d1644e552ed445dd58f9b5121ea5/oneflow/core/vm/virtual_machine_engine.h#L47 )物件負責;VM初始化時還開闢了單獨的schedule執行緒用於VM引擎處理排程邏輯,在VirtualMachine::ScheduleLoop
( http://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
( http://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
( http://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方法( http://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
( http://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方法( http://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 empty
Maybe<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;
}
3
小結
至此,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原始碼:
http://github.com/Oneflow-Inc/oneflow/tree/88f147d50e75d1644e552ed445dd58f9b5121ea5
其他人都在看
歡迎體驗OneFlow v0.8.0:http://github.com/Oneflow-Inc/oneflow/
本文分享自微信公眾號 - OneFlow(OneFlowTechnology)。
如有侵權,請聯絡 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。
- Hugging Face:成為機器學習界的“GitHub”
- 從Core Dump中提取CUDA的報錯資訊
- OneFlow原始碼解析:Tensor型別體系與Local Tensor
- 逆向工程:揭示Google Colab未公開的祕密
- 英偉達首席科學家:深度學習硬體的過去、現在和未來
- OpenMLDB OneFlow:手把手教你快速連結特徵工程到模型訓練
- GPU加速Pinterest推薦模型,引數量增加100倍,使用者活躍度提高16%
- 左益豪:用程式碼創造一個新世界|OneFlow U
- OneFlow原始碼解析:運算元指令在虛擬機器中的執行
- OneFlow原始碼解析:Op、Kernel與直譯器
- 18張圖,直觀理解神經網路、流形和拓撲
- 一種分散式深度學習程式設計新正規化:Global Tensor
- OneFlow v0.8.0正式釋出
- OneFlow原始碼一覽:GDB編譯除錯
- 大模型訓練難於上青天?效率超群、易用的“李白”模型庫來了
- 平行計算的量化模型及其在深度學習引擎裡的應用
- Geoffrey Hinton:我的五十年深度學習生涯與研究心法
- 圖解OneFlow的學習率調整策略
- 鍾珊珊:被爆錘後的工程師會起飛|OneFlow U
- 深度學習概述:從基礎概念、計算步驟到調優方法