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.

helper.cpp 14 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. /**
  2. * \file test/src/helper.cpp
  3. *
  4. * This file is part of MegBrain, a deep learning framework developed by Megvii.
  5. *
  6. * \copyright Copyright (c) 2014-2020 Megvii Inc. All rights reserved.
  7. *
  8. */
  9. #include "./rng_seed.h"
  10. #include "megbrain/test/helper.h"
  11. #include "megbrain/utils/hash.h"
  12. #include "megbrain/utils/debug.h"
  13. #include "megbrain/utils/persistent_cache.h"
  14. #include "megbrain/comp_node_env.h"
  15. #include <atomic>
  16. #include <random>
  17. #include <cmath>
  18. #include <cstring>
  19. #include <cstdlib>
  20. #if MGB_CUDA
  21. #include <cuda.h>
  22. #include <cuda_runtime.h>
  23. #endif
  24. using namespace mgb;
  25. const dt_qint8 UniformRNGDefaultRange<dtype::QuantizedS8>::LO = dt_qint8{-128};
  26. const dt_qint8 UniformRNGDefaultRange<dtype::QuantizedS8>::HI = dt_qint8{127};
  27. bool megdnn::operator == (const TensorLayout &a, const TensorLayout &b) {
  28. if (a.ndim != b.ndim)
  29. return false;
  30. // check all shapes and strides equal, including shape-1 dims
  31. for (size_t i = 0; i < a.ndim; ++ i) {
  32. if (a[i] != b[i] || a.stride[i] != b.stride[i])
  33. return false;
  34. }
  35. return true;
  36. }
  37. uint64_t mgb::next_rand_seed() {
  38. return RNGSeedManager::inst().next_seed();
  39. }
  40. void mgb::set_rand_seed(uint64_t seed) {
  41. RNGSeedManager::inst().set_seed(seed);
  42. }
  43. RNGxorshf::RNGxorshf(uint64_t seed) {
  44. std::mt19937_64 gen(seed);
  45. s[0] = gen();
  46. s[1] = gen();
  47. }
  48. /* ========================== HostTensorGenerator ========================== */
  49. template<typename dtype>
  50. std::shared_ptr<HostTensorND> HostTensorGenerator<
  51. dtype, RandomDistribution::GAUSSIAN>::operator ()(
  52. const TensorShape &shape, CompNode cn) {
  53. if (!cn.valid())
  54. cn = CompNode::load("xpu0");
  55. std::shared_ptr<HostTensorND> ret =
  56. std::make_shared<HostTensorND>(cn, shape, dtype());
  57. auto ptr = ret->ptr<ctype>();
  58. auto mean = m_mean, std = m_std;
  59. for (size_t i = 0, it = shape.total_nr_elems(); i < it; i += 2) {
  60. ctype u1 = ctype((m_rng() + 1.0) / (m_rng.max() + 1.0)),
  61. u2 = ctype((m_rng() + 1.0) / (m_rng.max() + 1.0)),
  62. r = ctype(std * std::sqrt(-2 * std::log(u1))),
  63. theta = ctype(2 * M_PI * u2),
  64. z0 = ctype(r * std::cos(theta) + mean),
  65. z1 = ctype(r * std::sin(theta) + mean);
  66. ptr[i] = z0;
  67. ptr[std::min(i + 1, it - 1)] = z1;
  68. }
  69. return ret;
  70. }
  71. template<typename dtype>
  72. std::shared_ptr<HostTensorND> HostTensorGenerator<
  73. dtype, RandomDistribution::UNIFORM>::operator ()(
  74. const TensorShape &shape, CompNode cn) {
  75. if (!cn.valid())
  76. cn = CompNode::load("xpu0");
  77. std::shared_ptr<HostTensorND> ret =
  78. std::make_shared<HostTensorND>(cn, shape, dtype());
  79. auto ptr = ret->ptr<ctype>();
  80. double scale = (m_hi - m_lo) / (m_rng.max() + 1.0);
  81. for (size_t i = 0, it = shape.total_nr_elems(); i < it; ++ i) {
  82. ptr[i] = m_rng() * scale + m_lo;
  83. }
  84. return ret;
  85. }
  86. template<typename dtype>
  87. std::shared_ptr<HostTensorND> HostTensorGenerator<
  88. dtype, RandomDistribution::CONSTANT>::operator ()(
  89. const TensorShape &shape, CompNode cn) {
  90. if (!cn.valid())
  91. cn = CompNode::load("xpu0");
  92. std::shared_ptr<HostTensorND> ret =
  93. std::make_shared<HostTensorND>(cn, shape, dtype());
  94. auto ptr = ret->ptr<ctype>();
  95. for (size_t i = 0, it = shape.total_nr_elems(); i < it; ++ i) {
  96. ptr[i] = m_default_val;
  97. }
  98. return ret;
  99. }
  100. template<typename dtype>
  101. std::shared_ptr<HostTensorND> HostTensorGenerator<
  102. dtype, RandomDistribution::CONSECUTIVE>::operator ()(
  103. const TensorShape &shape, CompNode cn) {
  104. if (!cn.valid())
  105. cn = CompNode::load("xpu0");
  106. std::shared_ptr<HostTensorND> ret =
  107. std::make_shared<HostTensorND>(cn, shape, dtype());
  108. auto ptr = ret->ptr<ctype>();
  109. for (size_t i = 0, it = shape.total_nr_elems(); i < it; ++ i) {
  110. ptr[i] = m_val + i * m_delta;
  111. }
  112. return ret;
  113. }
  114. // explicit instantialization of HostTensorGenerator
  115. namespace mgb {
  116. template class HostTensorGenerator<
  117. dtype::Float32, RandomDistribution::GAUSSIAN>;
  118. template class HostTensorGenerator<
  119. dtype::Float32, RandomDistribution::UNIFORM>;
  120. template class HostTensorGenerator<
  121. dtype::Float32, RandomDistribution::CONSTANT>;
  122. template class HostTensorGenerator<
  123. dtype::Float32, RandomDistribution::CONSECUTIVE>;
  124. template class HostTensorGenerator<
  125. dtype::Float16, RandomDistribution::GAUSSIAN>;
  126. template class HostTensorGenerator<
  127. dtype::Int8, RandomDistribution::UNIFORM>;
  128. template class HostTensorGenerator<
  129. dtype::Int8, RandomDistribution::CONSTANT>;
  130. template class HostTensorGenerator<
  131. dtype::Int8, RandomDistribution::CONSECUTIVE>;
  132. template class HostTensorGenerator<
  133. dtype::Uint8, RandomDistribution::UNIFORM>;
  134. template class HostTensorGenerator<
  135. dtype::Uint8, RandomDistribution::CONSTANT>;
  136. template class HostTensorGenerator<
  137. dtype::Int16, RandomDistribution::UNIFORM>;
  138. template class HostTensorGenerator<
  139. dtype::Int16, RandomDistribution::CONSTANT>;
  140. template class HostTensorGenerator<
  141. dtype::Int32, RandomDistribution::UNIFORM>;
  142. template class HostTensorGenerator<
  143. dtype::Int32, RandomDistribution::CONSTANT>;
  144. std::shared_ptr<HostTensorND>
  145. HostTensorGenerator<dtype::Bool, RandomDistribution::UNIFORM>::
  146. operator()(const TensorShape& shape, CompNode cn) {
  147. if (!cn.valid())
  148. cn = CompNode::load("xpu0");
  149. auto dtype = dtype::Bool();
  150. std::shared_ptr<HostTensorND> ret =
  151. std::make_shared<HostTensorND>(cn, shape, dtype);
  152. auto ptr = ret->ptr<dt_bool>();
  153. for (size_t i = 0, it = shape.total_nr_elems(); i < it; ++i) {
  154. ptr[i] = (i % 2 == 1);
  155. }
  156. return ret;
  157. }
  158. std::shared_ptr<HostTensorND>
  159. HostTensorGenerator<dtype::QuantizedS8, RandomDistribution::UNIFORM>::
  160. operator()(const TensorShape& shape, CompNode cn) {
  161. if (!cn.valid())
  162. cn = CompNode::load("xpu0");
  163. auto dtype = dtype::QuantizedS8(m_scale);
  164. auto param = dtype.param();
  165. std::shared_ptr<HostTensorND> ret =
  166. std::make_shared<HostTensorND>(cn, shape, dtype);
  167. auto ptr = ret->ptr<dt_qint8>();
  168. double scale = (param.dequantize(m_hi) - param.dequantize(m_lo)) /
  169. (m_rng.max() + 1.0);
  170. for (size_t i = 0, it = shape.total_nr_elems(); i < it; ++i) {
  171. ptr[i] = param.quantize(m_rng() * scale + param.dequantize(m_lo));
  172. }
  173. return ret;
  174. }
  175. }
  176. ::testing::AssertionResult mgb::__assert_float_equal(
  177. const char *expr0, const char *expr1, const char * /*expr_maxerr*/,
  178. float v0, float v1, float maxerr) {
  179. float err = fabs(v0 - v1) / std::max<float>(
  180. 1, std::min(fabs(v0), fabs(v1)));
  181. if (std::isfinite(v0) && std::isfinite(v1) && err < maxerr) {
  182. return ::testing::AssertionSuccess();
  183. }
  184. return ::testing::AssertionFailure() << ssprintf(
  185. "Value of: %s\n"
  186. " Actual: %.6g\n"
  187. "Expected: %s\n"
  188. "Which is: %.6g\n"
  189. " Error: %.4e", expr1, v1, expr0, v0, err);
  190. }
  191. ::testing::AssertionResult mgb::__assert_tensor_equal(
  192. const char *expr0, const char *expr1, const char * /*expr_maxerr*/,
  193. const HostTensorND &v0, const HostTensorND &v1, float maxerr) {
  194. auto ret = debug::compare_tensor_value(v0, expr0, v1, expr1, maxerr);
  195. if (ret.valid())
  196. return ::testing::AssertionFailure() << ret.val();
  197. return ::testing::AssertionSuccess();
  198. }
  199. ::testing::AssertionResult mgb::__assert_shape_equal(const TensorShape& v0,
  200. const TensorShape& v1) {
  201. if (v0.eq_shape(v1))
  202. return ::testing::AssertionSuccess()
  203. << v0.to_string() << " == " << v1.to_string();
  204. else
  205. return ::testing::AssertionFailure()
  206. << v0.to_string() << " != " << v1.to_string();
  207. }
  208. #if WIN32
  209. #include <io.h>
  210. #include <fcntl.h>
  211. #include <direct.h>
  212. #define getcwd _getcwd
  213. namespace {
  214. auto mkdir(const char *path, int) {
  215. return _mkdir(path);
  216. }
  217. int mkstemp(char *tpl){
  218. tpl = _mktemp(tpl);
  219. mgb_assert(tpl);
  220. auto fd = _open(tpl, _O_TEMPORARY | _O_RDWR);
  221. mgb_assert(fd > 0, "failed to open %s: %s", tpl, strerror(errno));
  222. return fd;
  223. }
  224. }
  225. #else
  226. #include <unistd.h>
  227. #include <sys/stat.h>
  228. #include <sys/types.h>
  229. #endif
  230. NamedTemporaryFile::NamedTemporaryFile()
  231. {
  232. char name[256];
  233. strcpy(name, output_file("mgb-test-XXXXXX", false).c_str());
  234. m_fd = mkstemp(name);
  235. mgb_throw_if(m_fd == -1, MegBrainError,
  236. "failed to open temp file `%s': %m", name);
  237. m_fpath = name;
  238. mgb_log_debug("opened temporary file: %s", name);
  239. }
  240. NamedTemporaryFile::~NamedTemporaryFile() {
  241. #ifdef WIN32
  242. _unlink(m_fpath.c_str());
  243. #else
  244. unlink(m_fpath.c_str());
  245. #endif
  246. }
  247. #if defined(IOS)
  248. #pragma message "build test on iOS; need ios_get_mgb_output_dir() to be defined"
  249. extern "C" void ios_get_mgb_output_dir(char **dir);
  250. #endif
  251. std::string mgb::output_file(const std::string &fname, bool check_writable) {
  252. static std::string cwd;
  253. static std::mutex cwd_mtx;
  254. MGB_LOCK_GUARD(cwd_mtx);
  255. if (cwd.empty()) {
  256. #if defined(IOS)
  257. char *buf = nullptr;
  258. ios_get_mgb_output_dir(&buf);
  259. #else
  260. auto buf = getcwd(nullptr, 0);
  261. #endif
  262. mgb_assert(buf);
  263. cwd = buf;
  264. free(buf);
  265. cwd.append("/output");
  266. mgb_log("use test output dir: %s", cwd.c_str());
  267. mkdir(cwd.c_str(), 0755);
  268. }
  269. if (fname.empty())
  270. return cwd;
  271. auto ret = cwd + "/" + fname;
  272. if (check_writable) {
  273. FILE *fout = fopen(ret.c_str(), "w");
  274. mgb_assert(fout, "failed to open %s: %s", ret.c_str(), strerror(errno));
  275. fclose(fout);
  276. }
  277. return ret;
  278. }
  279. std::vector<CompNode> mgb::load_multiple_xpus(size_t num) {
  280. auto cn0 = CompNode::load("xpu0");
  281. if (CompNode::get_device_count(cn0.device_type()) < num) {
  282. cn0 = CompNode::load("cpu0");
  283. }
  284. std::vector<CompNode> ret{cn0};
  285. auto loc = cn0.locator();
  286. for (size_t i = 1; i < num; ++ i) {
  287. loc.device = i;
  288. ret.push_back(CompNode::load(loc));
  289. }
  290. return ret;
  291. }
  292. bool mgb::check_gpu_available(size_t num) {
  293. if (CompNode::get_device_count(CompNode::DeviceType::CUDA) < num) {
  294. mgb_log_warn("skip test case that requires %zu GPU(s)", num);
  295. return false;
  296. }
  297. return true;
  298. }
  299. bool mgb::check_cambricon_device_available(size_t num) {
  300. if (CompNode::get_device_count(CompNode::DeviceType::CAMBRICON) < num) {
  301. mgb_log_warn("skip test case that requires %zu cambricon device(s)",
  302. num);
  303. return false;
  304. }
  305. return true;
  306. }
  307. bool mgb::check_device_type_avaiable(CompNode::DeviceType device_type) {
  308. switch (device_type) {
  309. case mgb::CompNode::DeviceType::CUDA:
  310. case mgb::CompNode::DeviceType::CPU:
  311. case mgb::CompNode::DeviceType::CAMBRICON:
  312. case mgb::CompNode::DeviceType::ATLAS:
  313. case mgb::CompNode::DeviceType::MULTITHREAD:
  314. return true;
  315. default:
  316. return false;
  317. }
  318. return false;
  319. }
  320. bool mgb::check_compute_capability(int major, int minor) {
  321. #if MGB_CUDA
  322. int dev;
  323. MGB_CUDA_CHECK(cudaGetDevice(&dev));
  324. cudaDeviceProp prop;
  325. MGB_CUDA_CHECK(cudaGetDeviceProperties(&prop, dev));
  326. return prop.major > major || (prop.major == major && prop.minor >= minor);
  327. #else
  328. MGB_MARK_USED_VAR(major);
  329. MGB_MARK_USED_VAR(minor);
  330. return false;
  331. #endif
  332. }
  333. void mgb::write_tensor_to_file(const HostTensorND &hv,
  334. const char *fname, char mode) {
  335. mgb_assert(hv.layout().is_contiguous());
  336. char modefull[] = {mode, 'b', '\x00'};
  337. FILE *fout = fopen(fname, modefull);
  338. mgb_assert(fout, "failed to open %s: %s", fname, strerror(errno));
  339. fprintf(fout, "%s %zu", hv.dtype().name(), hv.shape().ndim);
  340. for (size_t i = 0; i < hv.shape().ndim; ++ i) {
  341. fprintf(fout, " %zu", hv.shape(i));
  342. }
  343. fprintf(fout, "\n");
  344. auto size = hv.layout().span().dist_byte();
  345. auto wr = fwrite(hv.raw_ptr(), 1, size, fout);
  346. mgb_assert(size == wr);
  347. mgb_log("write tensor: %zu bytes (%s) to %s", size,
  348. hv.shape().to_string().c_str(), fname);
  349. fclose(fout);
  350. }
  351. cg::ComputingGraph::OutputSpecItem
  352. mgb::make_callback_copy(SymbolVar dev, HostTensorND &host, bool sync) {
  353. auto cb = [sync, &host](DeviceTensorND &d) {
  354. host.copy_from(d);
  355. if (sync) {
  356. host.sync();
  357. }
  358. };
  359. return {dev, cb};
  360. }
  361. /* ========================== PersistentCacheHook ========================== */
  362. class PersistentCacheHook::HookedImpl final : public PersistentCache {
  363. GetHook m_on_get;
  364. public:
  365. std::shared_ptr<PersistentCache> orig_impl;
  366. HookedImpl(GetHook on_get) : m_on_get{std::move(on_get)} {}
  367. Maybe<Blob> get(const std::string& category, const Blob& key) override {
  368. auto ret = orig_impl->get(category, key);
  369. m_on_get(category, key.ptr, key.size, ret.valid() ? ret->ptr : 0,
  370. ret.valid() ? ret->size : 0);
  371. return ret;
  372. }
  373. void put(const std::string& category, const Blob& key,
  374. const Blob& value) override {
  375. orig_impl->put(category, key, value);
  376. }
  377. };
  378. PersistentCacheHook::PersistentCacheHook(GetHook on_get)
  379. : m_impl{std::make_shared<HookedImpl>(std::move(on_get))} {
  380. m_impl->orig_impl = PersistentCache::set_impl(m_impl);
  381. }
  382. PersistentCacheHook::~PersistentCacheHook() {
  383. PersistentCache::set_impl(std::move(m_impl->orig_impl));
  384. }
  385. #if !MGB_ENABLE_EXCEPTION
  386. #pragma message "some tests would be disabled because exception is disabled"
  387. #endif
  388. // vim: syntax=cpp.doxygen foldmethod=marker foldmarker=f{{{,f}}}

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