You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

comp_node.h 25 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763
  1. /**
  2. * \file src/core/include/megbrain/comp_node.h
  3. * MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
  4. *
  5. * Copyright (c) 2014-2020 Megvii Inc. All rights reserved.
  6. *
  7. * Unless required by applicable law or agreed to in writing,
  8. * software distributed under the License is distributed on an
  9. * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. */
  11. #pragma once
  12. #include "megbrain/utils/hash.h"
  13. #include "megbrain/utils/enum_class_bit.h"
  14. #include "megbrain/utils/metahelper.h"
  15. #include "megbrain/utils/thin/hash_table.h"
  16. #include "megbrain/utils/thread.h"
  17. #include "megbrain/utils/thin/function.h"
  18. #include "megdnn/thin/function.h"
  19. #include <cstddef>
  20. #include <string>
  21. #include <memory>
  22. namespace mgb {
  23. // forward declaration; defined in comp_node_env.h
  24. class CompNodeEnv;
  25. namespace cg {
  26. class ComputingGraph;
  27. }
  28. class CompNodeSeqRecorder;
  29. /*!
  30. * \brief identifier for a memory node
  31. *
  32. * MemNode is comparable. CompNodes with the same MemNode can access memory of
  33. * each other directly
  34. */
  35. class MemNode {
  36. const void* m_id = nullptr;
  37. public:
  38. MemNode() = default;
  39. explicit MemNode(const void *id):
  40. m_id{id}
  41. {}
  42. bool operator == (const MemNode &rhs) const {
  43. return m_id == rhs.m_id;
  44. }
  45. bool operator != (const MemNode &rhs) const {
  46. return m_id != rhs.m_id;
  47. }
  48. operator bool() const {
  49. return m_id != nullptr;
  50. }
  51. };
  52. /*!
  53. * \brief abstraction of a streaming computing resource on localhost (a
  54. * thread on CPU, a cuda stream, etc.)
  55. *
  56. * Note that most of the operations are asynchronous with respect to the caller
  57. * thread
  58. */
  59. class CompNode {
  60. public:
  61. //! computing device type
  62. enum class DeviceType {
  63. //! for "xpu" comp node that would mapped to available cn on
  64. //! current system
  65. UNSPEC = 0,
  66. CUDA = 1,
  67. CPU = 2,
  68. CAMBRICON = 3,
  69. ROCM = 8,
  70. ATLAS = 9,
  71. MULTITHREAD,
  72. MAX_DEVICE_ID,
  73. };
  74. static constexpr size_t NR_DEVICE_TYPE =
  75. static_cast<size_t>(DeviceType::MAX_DEVICE_ID);
  76. /*!
  77. * \brief an identifier to specify a computing node
  78. *
  79. * Note: logical locator is directly parsed from a string identifier
  80. * given by user; it should be translated to physical locator by calling
  81. * to_physical() before actual use.
  82. *
  83. * Unless explicitly specified otherwise, all locators are physical
  84. * locators.
  85. */
  86. struct Locator {
  87. /*!
  88. * \brief special device number for the "cpu default" comp node,
  89. * which dispatches all tasks in the caller thread
  90. */
  91. static constexpr int DEVICE_CPU_DEFAULT = -1024;
  92. /*!
  93. * \brief special device number for the "multithread_default"
  94. * comp node, which dispatches all tasks to thread pool and the
  95. * caller thread is the main thread of thread pool
  96. */
  97. static constexpr int DEVICE_MULTITHREAD_DEFAULT = -1025;
  98. DeviceType type = DeviceType::UNSPEC;
  99. /*!
  100. * corresponding to a physical computing device; memories between
  101. * different devices are not shared.
  102. *
  103. * device == -1 means logical default device (maps to 0 by default,
  104. * and can be changed by set_device_map)
  105. */
  106. int device = -1;
  107. //! multiple streams can execute on one computing device and share
  108. //! memory, when compnode type is multithread the field also stand
  109. //! for nr_threads
  110. union {
  111. int stream = 0;
  112. int nr_threads;
  113. };
  114. /*!
  115. * \brief parse a string identifier
  116. *
  117. * currently supported ID format: (gpu|cpu)<n>[:m] where n is the
  118. * device number, possibly with m as the stream id.
  119. */
  120. static Locator parse(const std::string& id);
  121. /*!
  122. * \brief set mapping between device numbers of a device type
  123. */
  124. static void set_device_map(DeviceType type, int from, int to);
  125. /*!
  126. * \brief set the actual device type to be used for
  127. * DeviceType::UNSPEC
  128. */
  129. static void set_unspec_device_type(DeviceType type);
  130. /*!
  131. * \brief get corresponding physical Locator
  132. *
  133. * DeviceType::UNSPEC would be resolved, and device map would be
  134. * applied on device number
  135. */
  136. Locator to_physical() const;
  137. /*!
  138. * \brief get string description of this locator that can be parsed
  139. * again
  140. */
  141. std::string to_string() const;
  142. bool operator == (const Locator &rhs) const {
  143. return type == rhs.type && device == rhs.device &&
  144. stream == rhs.stream;
  145. }
  146. };
  147. //! predefined special streams
  148. struct Stream {
  149. static constexpr int
  150. COPY = -1,
  151. REMOTE_SEND = -2,
  152. LOOP_SWAP = -3;
  153. };
  154. CompNode() = default;
  155. /*!
  156. * \brief manually destroy all comp node resources
  157. */
  158. static void finalize();
  159. /*!
  160. * \brief load a computing node from logical locator ID;
  161. * \see Locator::parse
  162. */
  163. static CompNode load(const std::string& id) {
  164. return load(Locator::parse(id));
  165. }
  166. /*!
  167. * \brief create a CompNode object from **logical** locator
  168. */
  169. static CompNode load(const Locator& locator) {
  170. return load(locator.to_physical(), locator);
  171. }
  172. static CompNode load(const Locator& locator_physical,
  173. const Locator& locator_logical);
  174. /* =================== memory management ======================== */
  175. /*!
  176. * \brief allocate memory on this computing node
  177. *
  178. * Note: allocation of device memory is synchronous with the host,
  179. * meaning that the memory can be used immediately; however deallocation
  180. * is asynchronous to ensure that the memory can be used by
  181. * already-launched kernels on the computing node.
  182. *
  183. * Exception should be raised if allocation fails.
  184. */
  185. void *alloc_device(size_t size) const;
  186. //! deallocate device buffer; see alloc_device() for more details
  187. void free_device(void *ptr) const;
  188. /*!
  189. * \brief allocate memory on host that is associated with the device,
  190. * which may accelerate I/O
  191. *
  192. * Both allocation and deallocation on host are synchronous.
  193. */
  194. void *alloc_host(size_t size) const;
  195. void free_host(void *ptr) const;
  196. //! copy from underlying device to host
  197. void copy_to_host(
  198. void *host_ptr, const void *device_ptr, size_t size) const {
  199. return m_impl->copy_to_host(host_ptr, device_ptr, size);
  200. }
  201. //! copy from host to underlying device
  202. void copy_to_device(
  203. void *device_ptr, const void *host_ptr, size_t size) const {
  204. return m_impl->copy_to_device(device_ptr, host_ptr, size);
  205. }
  206. /*!
  207. * \brief copy from this device to another device; would use the
  208. * computing resource on dest_node
  209. * \param src source memory that must be allocated on this device
  210. */
  211. void peer_copy_to(CompNode dest_node, void *dest,
  212. const void *src, size_t size) const {
  213. return m_impl->peer_copy_to(
  214. reinterpret_cast<Impl*>(dest_node.m_impl), dest, src, size);
  215. }
  216. //! get alignment requiement in bytes; guaranteed to be power of 2
  217. size_t get_mem_addr_alignment() const {
  218. return m_impl->get_mem_addr_alignment();
  219. }
  220. /*!
  221. * \brief get the size of the paddings which must be reserved at the
  222. * end of memory chunk; guaranteed to be power of 2
  223. */
  224. size_t get_mem_padding() const {
  225. size_t padding = m_impl->get_mem_padding();
  226. mgb_assert(!(padding & (padding - 1)),
  227. "mem padding should be power of 2");
  228. return padding;
  229. }
  230. /*!
  231. * \brief release consecutive free chunks on all devices to defragment;
  232. * see DevMemAlloc::try_coalesce_free
  233. */
  234. static void try_coalesce_all_free_memory();
  235. /*
  236. * \brief specifies how to pre-allocate from raw dev allocator
  237. *
  238. */
  239. static void set_prealloc_config(size_t alignment, size_t min_req,
  240. size_t max_overhead, double growth_factor,
  241. DeviceType device_type);
  242. /* =================== synchronization ======================== */
  243. class Event;
  244. class EventPool;
  245. std::unique_ptr<Event> create_event(size_t flags = 0) const {
  246. return m_impl->create_event(flags);
  247. }
  248. //! wait for an event created on another CompNode
  249. inline void device_wait_event(Event &event) const;
  250. /*!
  251. * \brief block host thread to wait for all previous operations on this
  252. * computing node to finish
  253. */
  254. void sync() const {
  255. return m_impl->sync();
  256. }
  257. /*!
  258. * \brief synchronize all computing nodes
  259. */
  260. static void sync_all();
  261. /* =================== misc ======================== */
  262. /*!
  263. * \brief get id of underlying memory node; comp nodes that share the
  264. * same mem node can access memory allocated by each other.
  265. */
  266. MemNode mem_node() const {
  267. return m_impl->mem_node();
  268. }
  269. bool operator == (const CompNode &rhs) const {
  270. return m_impl == rhs.m_impl;
  271. }
  272. bool operator != (const CompNode &rhs) const {
  273. return !this->operator==(rhs);
  274. }
  275. bool valid() const {
  276. return m_impl;
  277. }
  278. //! get total and free memory on the computing device in bytes
  279. std::pair<size_t, size_t> get_mem_status_bytes() const {
  280. return m_impl->get_mem_status_bytes();
  281. }
  282. //! change to another stream on the same memory node
  283. CompNode change_stream(int dest_stream) const;
  284. //! get string representation of physical device
  285. std::string to_string() const {
  286. return m_impl ? m_impl->locator().to_string() : "invalid";
  287. }
  288. //! get string representation of logical device
  289. std::string to_string_logical() const {
  290. return m_impl ? m_impl->locator_logical().to_string() : "invalid";
  291. }
  292. uint64_t get_uid() {
  293. return m_impl->get_uid();
  294. }
  295. //! get the physical locator that created this comp node
  296. Locator locator() const {
  297. return m_impl->locator();
  298. }
  299. //! get the logical locator that created this comp node
  300. Locator locator_logical() const {
  301. return m_impl->locator_logical();
  302. }
  303. //! see CompNodeEnv::activate
  304. void activate() const;
  305. //! get device type of this comp node
  306. DeviceType device_type() const;
  307. /*!
  308. * \brief check for error on the asynchronous computing stream
  309. *
  310. * This is used for devices with limited error handling such as CUDA.
  311. *
  312. * It will return MegBrainError with error messages rather than
  313. * directly throw exception; return nullptr if no error.
  314. */
  315. MGB_WARN_UNUSED_RESULT
  316. std::unique_ptr<MegBrainError> check_async_error() const;
  317. /*!
  318. * \brief create a CompNodeSeqRecorder associated with this computing
  319. * node
  320. *
  321. * Note: the implementation must be thread safe: simultaneous calls to
  322. * create_seq_recorder() must block until existing CompNodeSeqRecorder
  323. * objects are either destructed or stopped.
  324. *
  325. * \return the recorder object; nullptr is returned if recording is not
  326. * supported
  327. */
  328. std::unique_ptr<CompNodeSeqRecorder> create_seq_recorder(
  329. cg::ComputingGraph* cg) {
  330. return m_impl->create_seq_recorder(cg);
  331. }
  332. /*!
  333. * insert callback into current compute stream.
  334. * The callack is to be called after all currently enqueued
  335. * iterms in the stream have completed. And the later tasks
  336. * in the stream must wait for the callback to finish.
  337. */
  338. void add_callback(megdnn::thin_function<void()>&& cb) {
  339. return m_impl->add_callback(std::move(cb));
  340. }
  341. enum class Flag : uint32_t {
  342. //! Whether computing recorder is supported on this comp node (i.e.
  343. //! whether non-zero comp_node_seq_record_level is allowed)
  344. SUPPORT_RECORDER = 1 << 0,
  345. //! Whether dynamic memory allocation is supported in seq recorder.
  346. //! If this flag is not setted, ComputingSequence::do_execute()
  347. //! would skip the warm up and allow seq recorder to start
  348. //! immediately
  349. RECORDER_SUPPORT_DYNAMIC_ALLOC = 1 << 1,
  350. //! Whether the capacity of the asynchronous execution queue on this
  351. //! comp node is limited.
  352. //! If this flag is set, tasks on multiple comp nodes would be
  353. //! dispatched from multiple cpu threads.
  354. //! \see ComputingGraph::Options::async_exec_level
  355. QUEUE_LIMITED = 1 << 2,
  356. //! Whether this comp node supports copy stream, so computation and
  357. //! I/O can be parallelized
  358. HAS_COPY_STREAM = 1 << 3,
  359. //! Destructing an event is unsafe if the comp node is not
  360. //! synchronized; setting this flag would cause computing sequence
  361. //! to sync the comp node in its dtor.
  362. EVENT_DTOR_UNSAFE = 1 << 4,
  363. //! CompNode is available even there is no thread support, i.e.
  364. //! MGB_HAVE_THREAD=0. Usually this means that execution on the
  365. //! CompNode is synchronous, i.e. behaves like cpu:default
  366. SUPPORT_NO_THREAD = 1 << 5,
  367. };
  368. bool contain_flag(Flag flag) {
  369. return contain_flag(device_type(), flag);
  370. }
  371. static bool contain_flag(DeviceType device_type, Flag flag);
  372. using UnorderedSet = ThinHashSet<CompNode>;
  373. template<typename T>
  374. using UnorderedMap = ThinHashMap<CompNode, T>;
  375. //! apply function to each initialized comp node
  376. static void foreach(thin_function<void(CompNode)> callback);
  377. //! get total number of specific devices on this system
  378. static size_t get_device_count(DeviceType type, bool warn=true);
  379. /* =================== specialized ======================== */
  380. //! get default CPU comp node
  381. // implemented in comp_node/cpu/comp_node.cpp
  382. static CompNode default_cpu();
  383. /*!
  384. * \brief set whether to enable affinity setting for CPU comp nodes
  385. *
  386. * If enabled, computation on cpux would be bound to the x'th CPU.
  387. *
  388. * This is disabled by default.
  389. *
  390. * (implemented in comp_node/cpu/comp_node.cpp)
  391. *
  392. * \return original setting
  393. */
  394. static bool enable_affinity_for_cpu(bool flag);
  395. protected:
  396. //! ImplBase with env(); defined in CompNodeEnv
  397. class Impl;
  398. class ImplBase: public NonCopyableObj, public DynTypeObj {
  399. public:
  400. typedef void (*free_func_t)(ImplBase* self, void* ptr);
  401. //! memory free might be called after finalize(); so we should
  402. //! not rely on virtual function for this
  403. const free_func_t free_device;
  404. const free_func_t free_host;
  405. virtual void* alloc_device(size_t size) = 0;
  406. virtual void *alloc_host(size_t size) = 0;
  407. virtual void copy_to_host(void *host_ptr,
  408. const void *device_ptr, size_t size) = 0;
  409. virtual void copy_to_device(void *device_ptr,
  410. const void *host_ptr, size_t size) = 0;
  411. virtual void peer_copy_to(
  412. Impl *dest_impl, void *dest,
  413. const void *src, size_t size) = 0;
  414. virtual size_t get_mem_addr_alignment() = 0;
  415. virtual size_t get_mem_padding();
  416. virtual std::unique_ptr<Event> create_event(size_t flags) = 0;
  417. virtual void sync() = 0;
  418. virtual MemNode mem_node() = 0;
  419. virtual std::pair<size_t, size_t> get_mem_status_bytes() = 0;
  420. virtual Locator locator() = 0;
  421. virtual Locator locator_logical() = 0;
  422. virtual std::unique_ptr<CompNodeSeqRecorder>
  423. create_seq_recorder(cg::ComputingGraph* cg);
  424. virtual void add_callback(megdnn::thin_function<void()>&&);
  425. virtual uint64_t get_uid() {
  426. mgb_throw(MegBrainError, "get_uid is not impl yet");
  427. };
  428. protected:
  429. ImplBase(free_func_t fd, free_func_t fh)
  430. : free_device{fd}, free_host{fh} {}
  431. ~ImplBase() = default;
  432. };
  433. //! implementations are allocated statically, so no memory management
  434. //! is needed
  435. ImplBase *m_impl = nullptr;
  436. friend class CompNodeEnv;
  437. friend struct HashTrait<CompNode>;
  438. friend class CompNodeImplHelper;
  439. public:
  440. CompNode(ImplBase* impl) : m_impl{impl} {}
  441. };
  442. MGB_DEF_ENUM_CLASS_BIT_OPR(CompNode::Flag)
  443. /*!
  444. * \brief record computation operations on a computing node
  445. *
  446. * This is used for fast execution of an identical computation sequence where
  447. * only input/output data differ.
  448. *
  449. * When this object is created from a comp node, recording starts immediately.
  450. * Call stop() when computation finishes, and call replay() when it needs to be
  451. * re-executed.
  452. *
  453. * Implementations should consider thread safe in comp_node, in order to support
  454. * multi threads reording in the same comp_node simultaneously, using thread
  455. * local recorder in comp_node.
  456. *
  457. * Note. When recording is over, the recorder is independent with comp_node, so
  458. * the task dispatched into recorder should not related to the comp_node
  459. * methord, and the thread of recorder replay is the user thread.
  460. */
  461. class CompNodeSeqRecorder {
  462. public:
  463. virtual ~CompNodeSeqRecorder() noexcept = default;
  464. /*!
  465. * \brief Enter fake-exec mode
  466. *
  467. * Memory allocation/free is only allowed in fake-exec mode, and kernels
  468. * should not be actually recorded in this mode.
  469. *
  470. * This should be paired with exit_fake_exec()
  471. */
  472. virtual void enter_fake_exec(const CompNode& comp_node) = 0;
  473. //! Exit fake-exec mode
  474. virtual void exit_fake_exec(const CompNode& comp_node) = 0;
  475. virtual void stop(const CompNode& comp_node) = 0;
  476. virtual void replay() = 0;
  477. };
  478. /*!
  479. * \brief event associated with a CompNode node, used for cross-device
  480. * synchronization
  481. */
  482. class CompNode::Event: public NonCopyableObj {
  483. protected:
  484. static int sm_cpu_sync_level;
  485. //! flags when this event is created
  486. size_t const m_create_flags;
  487. Event(size_t create_flags):
  488. m_create_flags{create_flags}
  489. {
  490. }
  491. public:
  492. enum Flags {
  493. NEED_TIMER = 1
  494. };
  495. virtual ~Event() = default;
  496. /*!
  497. * \brief record this event on the comp node that creates it
  498. *
  499. * Note that if a comp node is recorded multiple times, then subsequent
  500. * calls would overwrite its internal state and other methods that
  501. * examine the status would only examine the completion of the most
  502. * recent call to record().
  503. */
  504. virtual void record() = 0;
  505. //! whether this event has finished; it must has been recorded
  506. virtual bool finished() = 0;
  507. //! block the host thread (caller thread) to wait for this event
  508. virtual void host_wait() = 0;
  509. //! get elapsed time in seconds from this to another event; the events
  510. //! must be finished
  511. virtual double elapsed_time_until(Event &end) = 0;
  512. //! record an action on another comp node so it would wait for this
  513. //! event
  514. virtual void device_wait_by(CompNode cn) = 0;
  515. //! get the comp node to which this event is associated
  516. virtual CompNode comp_node() const = 0;
  517. //! flags when this event is created
  518. size_t create_flags() const {
  519. return m_create_flags;
  520. }
  521. /*!
  522. * \brief set CPU resource usage level when performing synchronization
  523. * \param level CPU waiting level:
  524. * 0. condition var (the default)
  525. * 1. busy wait with yield
  526. * 2. busy wait
  527. */
  528. static void set_cpu_sync_level(int level) {
  529. sm_cpu_sync_level = level;
  530. }
  531. };
  532. /*!
  533. * \brief pool of events that can be reused
  534. */
  535. class CompNode::EventPool {
  536. CompNode m_cn;
  537. std::vector<std::unique_ptr<CompNode::Event>> m_allocated;
  538. std::vector<CompNode::Event*> m_free;
  539. Spinlock m_lock;
  540. size_t m_flags;
  541. public:
  542. explicit EventPool(CompNode cn, size_t flags = 0);
  543. ~EventPool();
  544. CompNode::Event* alloc();
  545. void free(CompNode::Event *ev);
  546. //! assert that all allocated events have been freed
  547. void assert_all_freed();
  548. };
  549. void CompNode::device_wait_event(Event &event) const {
  550. event.device_wait_by(*this);
  551. }
  552. template<>
  553. struct HashTrait<CompNode> {
  554. static size_t eval(const CompNode &val) {
  555. static_assert(sizeof(size_t) == sizeof(void*), "bad hash type");
  556. return reinterpret_cast<size_t>(static_cast<void*>(val.m_impl));
  557. }
  558. };
  559. namespace comp_node_detail {
  560. /*!
  561. * \brief an inplace doubly linked list for efficient inserting/deleting
  562. *
  563. * Note: do not use this directly; it is only for CompNodeDepedentObject
  564. */
  565. class DepedentObjList {
  566. class Sentinel;
  567. struct StaticInfo;
  568. static StaticInfo sm_info;
  569. DepedentObjList *m_prev = nullptr, *m_next = nullptr;
  570. static void link(DepedentObjList* a, DepedentObjList* b) {
  571. a->m_next = b;
  572. b->m_prev = a;
  573. }
  574. protected:
  575. virtual std::shared_ptr<void> callback() = 0;
  576. ~DepedentObjList() = default;
  577. static void add(DepedentObjList* ptr);
  578. static void remove(DepedentObjList* ptr);
  579. public:
  580. static void invoke_callback_and_clean();
  581. };
  582. } // namespace comp_node_detail
  583. /*!
  584. * \brief base class for objects that depend on CompNode
  585. *
  586. * There is a CompNode::finalize() method that destorys all global comp nodes.
  587. * Therefore objects that depend on CompNode should all be marked as invalid at
  588. * that time.
  589. *
  590. * CompNode::finalize() is called in atexit() because some external libraries
  591. * that CompNode depends on seems to be registering exit handlers. It is also
  592. * impractical to require a correct destruction order because, for example, in
  593. * python atexit() handlers are invoked before global python objects get
  594. * reclaimed.
  595. *
  596. * As a result we give up enforcing a correct destruction order, but rather
  597. * require all CompNode-dependent objects to derive from this class so they can
  598. * get notified possibly do most of the cleanup when CompNode is finalized.
  599. */
  600. class CompNodeDepedentObject : private comp_node_detail::DepedentObjList {
  601. //! 1: in on_comp_node_finalize(); 2: after on_comp_node_finalize()
  602. int m_state = 0;
  603. std::shared_ptr<void> callback() override final;
  604. protected:
  605. CompNodeDepedentObject() { add(this); }
  606. ~CompNodeDepedentObject() { remove(this); }
  607. /*!
  608. * \brief overwritten by subclasses to perform clean up jobs
  609. *
  610. * Note: in case the object has nested objects which hold a reference to the
  611. * object itself, a reference to this object must be kept so it would not be
  612. * released during the call of on_comp_node_finalize().
  613. */
  614. virtual std::shared_ptr<void> on_comp_node_finalize() = 0;
  615. //! exception would thrown if on_comp_node_finalize() has been called (do
  616. //! not raise if invoked from on_comp_node_finalize())
  617. void check_not_finalized() const;
  618. //! whether on_comp_node_finalize() has been called (true when invoked
  619. //! from on_comp_node_finalize())
  620. bool is_finalized() const { return m_state; }
  621. };
  622. } // namespace mgb
  623. // vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}}

MegEngine 安装包中集成了使用 GPU 运行代码所需的 CUDA 环境,不用区分 CPU 和 GPU 版。 如果想要运行 GPU 程序,请确保机器本身配有 GPU 硬件设备并安装好驱动。 如果你想体验在云端 GPU 算力平台进行深度学习开发的感觉,欢迎访问 MegStudio 平台