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 16 kB

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