memalloc.c 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911
  1. /* StarPU --- Runtime system for heterogeneous multicore architectures.
  2. *
  3. * Copyright (C) 2008-2021 Université de Bordeaux, CNRS (LaBRI UMR 5800), Inria
  4. * Copyright (C) 2018 Federal University of Rio Grande do Sul (UFRGS)
  5. *
  6. * StarPU is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU Lesser General Public License as published by
  8. * the Free Software Foundation; either version 2.1 of the License, or (at
  9. * your option) any later version.
  10. *
  11. * StarPU is distributed in the hope that it will be useful, but
  12. * WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  14. *
  15. * See the GNU Lesser General Public License in COPYING.LGPL for more details.
  16. */
  17. #include <datawizard/memory_manager.h>
  18. #include <datawizard/memory_nodes.h>
  19. #include <datawizard/memalloc.h>
  20. #include <datawizard/footprint.h>
  21. #include <core/disk.h>
  22. #include <core/topology.h>
  23. #include <starpu.h>
  24. #include <common/uthash.h>
  25. /* When reclaiming memory to allocate, we reclaim data_size_coefficient*data_size */
  26. const unsigned starpu_memstrategy_data_size_coefficient=2;
  27. /* Minimum percentage of available memory in each node */
  28. static unsigned minimum_p;
  29. static unsigned target_p;
  30. /* Minimum percentage of number of clean buffer in each node */
  31. static unsigned minimum_clean_p;
  32. static unsigned target_clean_p;
  33. /* Whether CPU memory has been explicitly limited by user */
  34. static int limit_cpu_mem;
  35. /* This per-node RW-locks protect mc_list and memchunk_cache entries */
  36. /* Note: handle header lock is always taken before this (normal add/remove case) */
  37. static struct _starpu_spinlock mc_lock[STARPU_MAXNODES];
  38. /* Potentially in use memory chunks. The beginning of the list is clean (home
  39. * node has a copy of the data, or the data is being transferred there), the
  40. * remainder of the list may not be clean. */
  41. static struct _starpu_mem_chunk_list mc_list[STARPU_MAXNODES];
  42. /* This is a shortcut inside the mc_list to the first potentially dirty MC. All
  43. * MC before this are clean, MC before this only *may* be clean. */
  44. static struct _starpu_mem_chunk *mc_dirty_head[STARPU_MAXNODES];
  45. /* TODO: introduce head of data to be evicted */
  46. /* Number of elements in mc_list, number of elements in the clean part of
  47. * mc_list plus the non-automatically allocated elements (which are thus always
  48. * considered as clean) */
  49. static unsigned mc_nb[STARPU_MAXNODES], mc_clean_nb[STARPU_MAXNODES];
  50. /* TODO: no home doesn't mean always clean, should push to larger memory nodes */
  51. #define MC_LIST_PUSH_BACK(node, mc) do { \
  52. _starpu_mem_chunk_list_push_back(&mc_list[node], mc); \
  53. if ((mc)->clean || (mc)->home) \
  54. /* This is clean */ \
  55. mc_clean_nb[node]++; \
  56. else if (!mc_dirty_head[node]) \
  57. /* This is the only dirty element for now */ \
  58. mc_dirty_head[node] = mc; \
  59. mc_nb[node]++; \
  60. } while(0)
  61. /* Put new clean mc at the end of the clean part of mc_list, i.e. just before mc_dirty_head (if any) */
  62. #define MC_LIST_PUSH_CLEAN(node, mc) do { \
  63. if (mc_dirty_head[node]) \
  64. _starpu_mem_chunk_list_insert_before(&mc_list[node], mc, mc_dirty_head[node]); \
  65. else \
  66. _starpu_mem_chunk_list_push_back(&mc_list[node], mc); \
  67. /* This is clean */ \
  68. mc_clean_nb[node]++; \
  69. mc_nb[node]++; \
  70. } while (0)
  71. #define MC_LIST_ERASE(node, mc) do { \
  72. if ((mc)->clean || (mc)->home) \
  73. mc_clean_nb[node]--; /* One clean element less */ \
  74. if ((mc) == mc_dirty_head[node]) \
  75. /* This was the dirty head */ \
  76. mc_dirty_head[node] = _starpu_mem_chunk_list_next((mc)); \
  77. /* One element less */ \
  78. mc_nb[node]--; \
  79. /* Remove element */ \
  80. _starpu_mem_chunk_list_erase(&mc_list[node], (mc)); \
  81. /* Notify whoever asked for it */ \
  82. if ((mc)->remove_notify) \
  83. { \
  84. *((mc)->remove_notify) = NULL; \
  85. (mc)->remove_notify = NULL; \
  86. } \
  87. } while (0)
  88. /* Explicitly caches memory chunks that can be reused */
  89. struct mc_cache_entry
  90. {
  91. UT_hash_handle hh;
  92. struct _starpu_mem_chunk_list list;
  93. uint32_t footprint;
  94. };
  95. static struct mc_cache_entry *mc_cache[STARPU_MAXNODES];
  96. static int mc_cache_nb[STARPU_MAXNODES];
  97. static starpu_ssize_t mc_cache_size[STARPU_MAXNODES];
  98. /* Whether some thread is currently tidying this node */
  99. static unsigned tidying[STARPU_MAXNODES];
  100. /* Whether some thread is currently reclaiming memory for this node */
  101. static unsigned reclaiming[STARPU_MAXNODES];
  102. /* This records that we tried to prefetch data but went out of memory, so will
  103. * probably fail again to prefetch data, thus not trace each and every
  104. * attempt. */
  105. static volatile int prefetch_out_of_memory[STARPU_MAXNODES];
  106. int _starpu_is_reclaiming(unsigned node)
  107. {
  108. STARPU_ASSERT(node < STARPU_MAXNODES);
  109. return tidying[node] || reclaiming[node];
  110. }
  111. /* Whether this memory node can evict data to another node */
  112. static unsigned evictable[STARPU_MAXNODES];
  113. static int can_evict(unsigned node)
  114. {
  115. return evictable[node];
  116. }
  117. /* Called after initializing the set of memory nodes */
  118. /* We use an accelerator -> CPU RAM -> disk storage hierarchy */
  119. void _starpu_mem_chunk_init_last(void)
  120. {
  121. unsigned disk = 0;
  122. unsigned nnodes = starpu_memory_nodes_get_count(), i;
  123. for (i = 0; i < nnodes; i++)
  124. {
  125. enum starpu_node_kind kind = starpu_node_get_kind(i);
  126. if (kind == STARPU_DISK_RAM)
  127. /* Some disk, will be able to evict RAM */
  128. /* TODO: disk hierarchy */
  129. disk = 1;
  130. else if (kind != STARPU_CPU_RAM)
  131. /* This is an accelerator, we can evict to main RAM */
  132. evictable[i] = 1;
  133. }
  134. if (disk)
  135. for (i = 0; i < nnodes; i++)
  136. {
  137. enum starpu_node_kind kind = starpu_node_get_kind(i);
  138. if (kind == STARPU_CPU_RAM)
  139. evictable[i] = 1;
  140. }
  141. }
  142. /* A disk was registered, RAM is now evictable */
  143. void _starpu_mem_chunk_disk_register(unsigned disk_memnode)
  144. {
  145. (void) disk_memnode;
  146. unsigned nnodes = starpu_memory_nodes_get_count(), i;
  147. for (i = 0; i < nnodes; i++)
  148. {
  149. enum starpu_node_kind kind = starpu_node_get_kind(i);
  150. if (kind == STARPU_CPU_RAM)
  151. evictable[i] = 1;
  152. }
  153. }
  154. static int get_better_disk_can_accept_size(starpu_data_handle_t handle, unsigned node);
  155. static int choose_target(starpu_data_handle_t handle, unsigned node);
  156. void _starpu_init_mem_chunk_lists(void)
  157. {
  158. unsigned i;
  159. for (i = 0; i < STARPU_MAXNODES; i++)
  160. {
  161. _starpu_spin_init(&mc_lock[i]);
  162. _starpu_mem_chunk_list_init(&mc_list[i]);
  163. STARPU_HG_DISABLE_CHECKING(mc_cache_size[i]);
  164. STARPU_HG_DISABLE_CHECKING(mc_nb[i]);
  165. STARPU_HG_DISABLE_CHECKING(mc_clean_nb[i]);
  166. STARPU_HG_DISABLE_CHECKING(prefetch_out_of_memory[i]);
  167. }
  168. /* We do not enable forcing available memory by default, since
  169. this makes StarPU spuriously free data when prefetching fills the
  170. memory. Clean buffers should be enough to be able to allocate data
  171. easily anyway. */
  172. minimum_p = starpu_get_env_number_default("STARPU_MINIMUM_AVAILABLE_MEM", 0);
  173. target_p = starpu_get_env_number_default("STARPU_TARGET_AVAILABLE_MEM", 0);
  174. minimum_clean_p = starpu_get_env_number_default("STARPU_MINIMUM_CLEAN_BUFFERS", 5);
  175. target_clean_p = starpu_get_env_number_default("STARPU_TARGET_CLEAN_BUFFERS", 10);
  176. limit_cpu_mem = starpu_get_env_number("STARPU_LIMIT_CPU_MEM");
  177. }
  178. void _starpu_deinit_mem_chunk_lists(void)
  179. {
  180. unsigned i;
  181. for (i = 0; i < STARPU_MAXNODES; i++)
  182. {
  183. struct mc_cache_entry *entry=NULL, *tmp=NULL;
  184. STARPU_ASSERT(mc_nb[i] == 0);
  185. STARPU_ASSERT(mc_clean_nb[i] == 0);
  186. STARPU_ASSERT(mc_dirty_head[i] == NULL);
  187. HASH_ITER(hh, mc_cache[i], entry, tmp)
  188. {
  189. STARPU_ASSERT (_starpu_mem_chunk_list_empty(&entry->list));
  190. HASH_DEL(mc_cache[i], entry);
  191. free(entry);
  192. }
  193. STARPU_ASSERT(mc_cache_nb[i] == 0);
  194. STARPU_ASSERT(mc_cache_size[i] == 0);
  195. _starpu_spin_destroy(&mc_lock[i]);
  196. }
  197. }
  198. /*
  199. * Manipulate subtrees
  200. */
  201. static void unlock_all_subtree(starpu_data_handle_t handle)
  202. {
  203. /* lock all sub-subtrees children
  204. * Note that this is done in the reverse order of the
  205. * lock_all_subtree so that we avoid deadlock */
  206. unsigned i;
  207. for (i =0; i < handle->nchildren; i++)
  208. {
  209. unsigned child = handle->nchildren - 1 - i;
  210. starpu_data_handle_t child_handle = starpu_data_get_child(handle, child);
  211. unlock_all_subtree(child_handle);
  212. }
  213. _starpu_spin_unlock(&handle->header_lock);
  214. }
  215. static int lock_all_subtree(starpu_data_handle_t handle)
  216. {
  217. int child;
  218. /* lock parent */
  219. if (_starpu_spin_trylock(&handle->header_lock))
  220. /* the handle is busy, abort */
  221. return 0;
  222. /* lock all sub-subtrees children */
  223. for (child = 0; child < (int) handle->nchildren; child++)
  224. {
  225. if (!lock_all_subtree(starpu_data_get_child(handle, child)))
  226. {
  227. /* Some child is busy, abort */
  228. while (--child >= 0)
  229. /* Unlock what we have already uselessly locked */
  230. unlock_all_subtree(starpu_data_get_child(handle, child));
  231. return 0;
  232. }
  233. }
  234. return 1;
  235. }
  236. static unsigned may_free_handle(starpu_data_handle_t handle, unsigned node)
  237. {
  238. /* we only free if no one refers to the leaf */
  239. uint32_t refcnt = _starpu_get_data_refcnt(handle, node);
  240. if (refcnt)
  241. return 0;
  242. if (handle->current_mode == STARPU_W)
  243. {
  244. if (handle->write_invalidation_req)
  245. /* Some request is invalidating it anyway */
  246. return 0;
  247. unsigned n;
  248. for (n = 0; n < STARPU_MAXNODES; n++)
  249. if (_starpu_get_data_refcnt(handle, n))
  250. /* Some task is writing to the handle somewhere */
  251. return 0;
  252. }
  253. /* no problem was found */
  254. return 1;
  255. }
  256. static unsigned may_free_subtree(starpu_data_handle_t handle, unsigned node)
  257. {
  258. if (!may_free_handle(handle, node))
  259. return 0;
  260. /* look into all sub-subtrees children */
  261. unsigned child;
  262. for (child = 0; child < handle->nchildren; child++)
  263. {
  264. unsigned res;
  265. starpu_data_handle_t child_handle = starpu_data_get_child(handle, child);
  266. res = may_free_subtree(child_handle, node);
  267. if (!res)
  268. return 0;
  269. }
  270. /* no problem was found */
  271. return 1;
  272. }
  273. /* Warn: this releases the header lock of the handle during the transfer
  274. * The handle may thus unexpectedly disappear. This returns 1 in that case.
  275. */
  276. static int STARPU_ATTRIBUTE_WARN_UNUSED_RESULT transfer_subtree_to_node(starpu_data_handle_t handle, unsigned src_node,
  277. unsigned dst_node)
  278. {
  279. STARPU_ASSERT(dst_node != src_node);
  280. if (handle->nchildren == 0)
  281. {
  282. struct _starpu_data_replicate *src_replicate = &handle->per_node[src_node];
  283. struct _starpu_data_replicate *dst_replicate = &handle->per_node[dst_node];
  284. /* this is a leaf */
  285. while (src_replicate->state == STARPU_OWNER)
  286. {
  287. /* This is the only copy, push it to destination */
  288. struct _starpu_data_request *r;
  289. r = _starpu_create_request_to_fetch_data(handle, dst_replicate, STARPU_R, STARPU_FETCH, 0, NULL, NULL, 0, "transfer_subtree_to_node");
  290. /* There is no way we don't need a request, since
  291. * source is OWNER, destination can't be having it */
  292. STARPU_ASSERT(r);
  293. /* Keep the handle alive while we are working on it */
  294. handle->busy_count++;
  295. _starpu_spin_unlock(&handle->header_lock);
  296. _starpu_wait_data_request_completion(r, 1);
  297. _starpu_spin_lock(&handle->header_lock);
  298. handle->busy_count--;
  299. if (_starpu_data_check_not_busy(handle))
  300. /* Actually disappeared, abort completely */
  301. return -1;
  302. if (!may_free_subtree(handle, src_node))
  303. /* Oops, while we released the header lock, a
  304. * task got in, abort. */
  305. return 0;
  306. }
  307. STARPU_ASSERT(may_free_subtree(handle, src_node));
  308. if (src_replicate->state == STARPU_SHARED)
  309. {
  310. unsigned i;
  311. unsigned last = 0;
  312. unsigned cnt = 0;
  313. /* some other node may have the copy */
  314. if (src_replicate->state != STARPU_INVALID)
  315. _STARPU_TRACE_DATA_STATE_INVALID(handle, src_node);
  316. src_replicate->state = STARPU_INVALID;
  317. /* count the number of copies */
  318. for (i = 0; i < STARPU_MAXNODES; i++)
  319. {
  320. if (handle->per_node[i].state == STARPU_SHARED)
  321. {
  322. cnt++;
  323. last = i;
  324. }
  325. }
  326. STARPU_ASSERT(cnt > 0);
  327. if (cnt == 1)
  328. {
  329. if (handle->per_node[last].state != STARPU_OWNER)
  330. _STARPU_TRACE_DATA_STATE_OWNER(handle, last);
  331. handle->per_node[last].state = STARPU_OWNER;
  332. }
  333. }
  334. else
  335. STARPU_ASSERT(src_replicate->state == STARPU_INVALID);
  336. /* Already dropped by somebody, in which case there is nothing to be done */
  337. }
  338. else
  339. {
  340. /* transfer all sub-subtrees children */
  341. unsigned child;
  342. for (child = 0; child < handle->nchildren; child++)
  343. {
  344. starpu_data_handle_t child_handle = starpu_data_get_child(handle, child);
  345. int res = transfer_subtree_to_node(child_handle, src_node, dst_node);
  346. if (res == 0)
  347. return 0;
  348. /* There is no way children have disappeared since we
  349. * keep the parent lock held */
  350. STARPU_ASSERT(res != -1);
  351. }
  352. }
  353. /* Success! */
  354. return 1;
  355. }
  356. static void notify_handle_children(starpu_data_handle_t handle, struct _starpu_data_replicate *replicate, unsigned node)
  357. {
  358. unsigned child;
  359. replicate->allocated = 0;
  360. /* XXX why do we need that ? */
  361. replicate->automatically_allocated = 0;
  362. for (child = 0; child < handle->nchildren; child++)
  363. {
  364. /* Notify children that their buffer has been deallocated too */
  365. starpu_data_handle_t child_handle = starpu_data_get_child(handle, child);
  366. notify_handle_children(child_handle, &child_handle->per_node[node], node);
  367. }
  368. }
  369. static size_t free_memory_on_node(struct _starpu_mem_chunk *mc, unsigned node)
  370. {
  371. size_t freed = 0;
  372. STARPU_ASSERT(mc->ops);
  373. STARPU_ASSERT(mc->ops->free_data_on_node);
  374. starpu_data_handle_t handle = mc->data;
  375. struct _starpu_data_replicate *replicate = mc->replicate;
  376. if (handle)
  377. _starpu_spin_checklocked(&handle->header_lock);
  378. if (mc->automatically_allocated &&
  379. (!handle || replicate->refcnt == 0))
  380. {
  381. void *data_interface;
  382. if (handle)
  383. STARPU_ASSERT(replicate->allocated);
  384. #if defined(STARPU_USE_CUDA) && defined(STARPU_HAVE_CUDA_MEMCPY_PEER) && !defined(STARPU_SIMGRID)
  385. if (starpu_node_get_kind(node) == STARPU_CUDA_RAM)
  386. {
  387. /* To facilitate the design of interface, we set the
  388. * proper CUDA device in case it is needed. This avoids
  389. * having to set it again in the free method of each
  390. * interface. */
  391. starpu_cuda_set_device(starpu_memory_node_get_devid(node));
  392. }
  393. #endif
  394. if (handle)
  395. data_interface = replicate->data_interface;
  396. else
  397. data_interface = mc->chunk_interface;
  398. STARPU_ASSERT(data_interface);
  399. if (handle && (starpu_node_get_kind(node) == STARPU_CPU_RAM))
  400. _starpu_data_unregister_ram_pointer(handle, node);
  401. _STARPU_TRACE_START_FREE(node, mc->size, handle);
  402. mc->ops->free_data_on_node(data_interface, node);
  403. _STARPU_TRACE_END_FREE(node, handle);
  404. if (handle)
  405. notify_handle_children(handle, replicate, node);
  406. freed = mc->size;
  407. if (handle)
  408. STARPU_ASSERT(replicate->refcnt == 0);
  409. }
  410. return freed;
  411. }
  412. /* mc_lock is held */
  413. static size_t do_free_mem_chunk(struct _starpu_mem_chunk *mc, unsigned node)
  414. {
  415. size_t size;
  416. starpu_data_handle_t handle = mc->data;
  417. if (handle)
  418. {
  419. _starpu_spin_checklocked(&handle->header_lock);
  420. mc->size = _starpu_data_get_alloc_size(handle);
  421. }
  422. if (mc->replicate)
  423. mc->replicate->mc=NULL;
  424. /* free the actual buffer */
  425. size = free_memory_on_node(mc, node);
  426. /* remove the mem_chunk from the list */
  427. MC_LIST_ERASE(node, mc);
  428. _starpu_mem_chunk_delete(mc);
  429. return size;
  430. }
  431. /* We assume that mc_lock[node] is taken. is_already_in_mc_list indicates
  432. * that the mc is already in the list of buffers that are possibly used, and
  433. * therefore not in the cache. */
  434. static void reuse_mem_chunk(unsigned node, struct _starpu_data_replicate *new_replicate, struct _starpu_mem_chunk *mc, unsigned is_already_in_mc_list)
  435. {
  436. void *data_interface;
  437. /* we found an appropriate mem chunk: so we get it out
  438. * of the "to free" list, and reassign it to the new
  439. * piece of data */
  440. struct _starpu_data_replicate *old_replicate = mc->replicate;
  441. if (old_replicate)
  442. {
  443. _starpu_data_unregister_ram_pointer(old_replicate->handle, node);
  444. old_replicate->allocated = 0;
  445. old_replicate->automatically_allocated = 0;
  446. old_replicate->initialized = 0;
  447. data_interface = old_replicate->data_interface;
  448. }
  449. else
  450. data_interface = mc->chunk_interface;
  451. STARPU_ASSERT(new_replicate->data_interface);
  452. STARPU_ASSERT(data_interface);
  453. memcpy(new_replicate->data_interface, data_interface, mc->size_interface);
  454. if (!old_replicate)
  455. {
  456. /* Free the copy that we made */
  457. free(mc->chunk_interface);
  458. mc->chunk_interface = NULL;
  459. }
  460. /* XXX: We do not actually reuse the mc at the moment, only the interface */
  461. /* mc->data = new_replicate->handle; */
  462. /* mc->footprint, mc->ops, mc->size_interface, mc->automatically_allocated should be
  463. * unchanged ! */
  464. /* remove the mem chunk from the list of active memory chunks, register_mem_chunk will put it back later */
  465. if (is_already_in_mc_list)
  466. MC_LIST_ERASE(node, mc);
  467. free(mc);
  468. }
  469. int starpu_data_can_evict(starpu_data_handle_t handle, unsigned node, enum starpu_is_prefetch is_prefetch)
  470. {
  471. STARPU_ASSERT(node < STARPU_MAXNODES);
  472. /* This data should be written through to this node, avoid dropping it! */
  473. if (node < sizeof(handle->wt_mask) * 8 && handle->wt_mask & (1<<node))
  474. return 0;
  475. /* This data was registered from this node, we will not be able to drop it anyway */
  476. if ((int) node == handle->home_node)
  477. return 0;
  478. /* This data cannnot be pushed outside CPU memory */
  479. if (!handle->ooc && starpu_node_get_kind(node) == STARPU_CPU_RAM
  480. && starpu_memory_nodes_get_numa_count() == 1)
  481. return 0;
  482. if (is_prefetch >= STARPU_TASK_PREFETCH && handle->per_node[node].nb_tasks_prefetch)
  483. /* We have not finished executing the tasks this was prefetched for */
  484. return 0;
  485. if (!may_free_handle(handle, node))
  486. /* Somebody refers to it */
  487. return 0;
  488. return 1;
  489. }
  490. /* This function is called for memory chunks that are possibly in used (ie. not
  491. * in the cache). They should therefore still be associated to a handle. */
  492. /* mc_lock is held and may be temporarily released! */
  493. static size_t try_to_throw_mem_chunk(struct _starpu_mem_chunk *mc, unsigned node, struct _starpu_data_replicate *replicate, unsigned is_already_in_mc_list, enum starpu_is_prefetch is_prefetch)
  494. {
  495. size_t freed = 0;
  496. starpu_data_handle_t handle;
  497. handle = mc->data;
  498. STARPU_ASSERT(handle);
  499. if (!starpu_data_can_evict(handle, node, is_prefetch))
  500. return 0;
  501. /* REDUX memchunk */
  502. if (mc->relaxed_coherency == 2)
  503. {
  504. /* TODO: reduce it back to e.g. main memory */
  505. }
  506. else
  507. /* Either it's a "relaxed coherency" memchunk (SCRATCH), or it's a
  508. * memchunk that could be used with filters. */
  509. if (mc->relaxed_coherency == 1)
  510. {
  511. STARPU_ASSERT(mc->replicate);
  512. if (_starpu_spin_trylock(&handle->header_lock))
  513. /* Handle is busy, abort */
  514. return 0;
  515. if (mc->replicate->refcnt == 0)
  516. {
  517. /* Note that there is no need to transfer any data or
  518. * to update the status in terms of MSI protocol
  519. * because this memchunk is associated to a replicate
  520. * in "relaxed coherency" mode. */
  521. if (replicate)
  522. {
  523. /* Reuse for this replicate */
  524. reuse_mem_chunk(node, replicate, mc, is_already_in_mc_list);
  525. freed = 1;
  526. }
  527. else
  528. {
  529. /* Free */
  530. freed = do_free_mem_chunk(mc, node);
  531. }
  532. }
  533. _starpu_spin_unlock(&handle->header_lock);
  534. }
  535. else if (lock_all_subtree(handle))
  536. /* try to lock all the subtree */
  537. {
  538. if (!(replicate && handle->per_node[node].state == STARPU_OWNER))
  539. {
  540. /* check if they are all "free" */
  541. if (may_free_subtree(handle, node))
  542. {
  543. int target = -1;
  544. /* XXX Considering only owner to invalidate */
  545. STARPU_ASSERT(handle->per_node[node].refcnt == 0);
  546. /* in case there was nobody using that buffer, throw it
  547. * away after writing it back to main memory */
  548. /* choose the best target */
  549. target = choose_target(handle, node);
  550. if (target != -1 &&
  551. /* Only reuse memchunks which are easy to throw
  552. * away (which is likely thanks to periodic tidying).
  553. * If there are none, we prefer to let generic eviction
  554. * perhaps find other kinds of memchunks which will be
  555. * earlier in LRU, and easier to throw away. */
  556. !(replicate && handle->per_node[node].state == STARPU_OWNER))
  557. {
  558. int res;
  559. /* Should have been avoided in our caller */
  560. STARPU_ASSERT(!mc->remove_notify);
  561. mc->remove_notify = &mc;
  562. _starpu_spin_unlock(&mc_lock[node]);
  563. #ifdef STARPU_MEMORY_STATS
  564. if (handle->per_node[node].state == STARPU_OWNER)
  565. _starpu_memory_handle_stats_invalidated(handle, node);
  566. #endif
  567. _STARPU_TRACE_START_WRITEBACK(node, handle);
  568. /* Note: this may need to allocate data etc.
  569. * and thus release the header lock, take
  570. * mc_lock, etc. */
  571. res = transfer_subtree_to_node(handle, node, target);
  572. _STARPU_TRACE_END_WRITEBACK(node, handle);
  573. #ifdef STARPU_MEMORY_STATS
  574. _starpu_memory_handle_stats_loaded_owner(handle, target);
  575. #endif
  576. _starpu_spin_lock(&mc_lock[node]);
  577. if (!mc)
  578. {
  579. if (res == -1)
  580. {
  581. /* handle disappeared, abort without unlocking it */
  582. return 0;
  583. }
  584. }
  585. else
  586. {
  587. STARPU_ASSERT(mc->remove_notify == &mc);
  588. mc->remove_notify = NULL;
  589. if (res == -1)
  590. {
  591. /* handle disappeared, abort without unlocking it */
  592. return 0;
  593. }
  594. if (res == 1)
  595. {
  596. /* mc is still associated with the old
  597. * handle, now free it.
  598. */
  599. if (handle->per_node[node].refcnt == 0)
  600. {
  601. /* And still nobody on it, now the actual buffer may be reused or freed */
  602. if (replicate)
  603. {
  604. /* Reuse for this replicate */
  605. reuse_mem_chunk(node, replicate, mc, is_already_in_mc_list);
  606. freed = 1;
  607. }
  608. else
  609. {
  610. /* Free */
  611. freed = do_free_mem_chunk(mc, node);
  612. }
  613. }
  614. }
  615. }
  616. }
  617. }
  618. }
  619. /* unlock the tree */
  620. unlock_all_subtree(handle);
  621. }
  622. return freed;
  623. }
  624. static int _starpu_data_interface_compare(void *data_interface_a, struct starpu_data_interface_ops *ops_a,
  625. void *data_interface_b, struct starpu_data_interface_ops *ops_b)
  626. {
  627. if (ops_a->interfaceid != ops_b->interfaceid)
  628. return -1;
  629. int ret;
  630. if (ops_a->alloc_compare)
  631. ret = ops_a->alloc_compare(data_interface_a, data_interface_b);
  632. else
  633. ret = ops_a->compare(data_interface_a, data_interface_b);
  634. return ret;
  635. }
  636. #ifdef STARPU_USE_ALLOCATION_CACHE
  637. /* This function must be called with mc_lock[node] taken */
  638. static struct _starpu_mem_chunk *_starpu_memchunk_cache_lookup_locked(unsigned node, starpu_data_handle_t handle, uint32_t footprint)
  639. {
  640. /* go through all buffers in the cache */
  641. struct mc_cache_entry *entry;
  642. HASH_FIND(hh, mc_cache[node], &footprint, sizeof(footprint), entry);
  643. if (!entry)
  644. /* No data with that footprint */
  645. return NULL;
  646. struct _starpu_mem_chunk *mc;
  647. for (mc = _starpu_mem_chunk_list_begin(&entry->list);
  648. mc != _starpu_mem_chunk_list_end(&entry->list);
  649. mc = _starpu_mem_chunk_list_next(mc))
  650. {
  651. /* Is that a false hit ? (this is _very_ unlikely) */
  652. if (_starpu_data_interface_compare(handle->per_node[node].data_interface, handle->ops, mc->chunk_interface, mc->ops) != 1)
  653. continue;
  654. /* Cache hit */
  655. /* Remove from the cache */
  656. _starpu_mem_chunk_list_erase(&entry->list, mc);
  657. mc_cache_nb[node]--;
  658. STARPU_ASSERT_MSG(mc_cache_nb[node] >= 0, "allocation cache for node %u has %d objects??", node, mc_cache_nb[node]);
  659. mc_cache_size[node] -= mc->size;
  660. STARPU_ASSERT_MSG(mc_cache_size[node] >= 0, "allocation cache for node %u has %ld bytes??", node, (long) mc_cache_size[node]);
  661. return mc;
  662. }
  663. /* This is a cache miss */
  664. return NULL;
  665. }
  666. /* this function looks for a memory chunk that matches a given footprint in the
  667. * list of mem chunk that need to be freed. */
  668. static int try_to_find_reusable_mc(unsigned node, starpu_data_handle_t data, struct _starpu_data_replicate *replicate, uint32_t footprint)
  669. {
  670. struct _starpu_mem_chunk *mc;
  671. int success = 0;
  672. _starpu_spin_lock(&mc_lock[node]);
  673. /* go through all buffers in the cache */
  674. mc = _starpu_memchunk_cache_lookup_locked(node, data, footprint);
  675. if (mc)
  676. {
  677. /* We found an entry in the cache so we can reuse it */
  678. reuse_mem_chunk(node, replicate, mc, 0);
  679. success = 1;
  680. }
  681. _starpu_spin_unlock(&mc_lock[node]);
  682. return success;
  683. }
  684. #endif
  685. /* this function looks for a memory chunk that matches a given footprint in the
  686. * list of mem chunk that are not important */
  687. static int try_to_reuse_not_important_mc(unsigned node, starpu_data_handle_t data, struct _starpu_data_replicate *replicate, uint32_t footprint, enum starpu_is_prefetch is_prefetch)
  688. {
  689. struct _starpu_mem_chunk *mc, *orig_next_mc, *next_mc;
  690. int success = 0;
  691. _starpu_spin_lock(&mc_lock[node]);
  692. restart:
  693. /* now look for some non essential data in the active list */
  694. for (mc = _starpu_mem_chunk_list_begin(&mc_list[node]);
  695. mc != _starpu_mem_chunk_list_end(&mc_list[node]) && !success;
  696. mc = next_mc)
  697. {
  698. /* there is a risk that the memory chunk is freed before next
  699. * iteration starts: so we compute the next element of the list
  700. * now */
  701. orig_next_mc = next_mc = _starpu_mem_chunk_list_next(mc);
  702. if (mc->remove_notify)
  703. /* Somebody already working here, skip */
  704. continue;
  705. if (!mc->data->is_not_important)
  706. /* Important data, skip */
  707. continue;
  708. if (mc->footprint != footprint || _starpu_data_interface_compare(data->per_node[node].data_interface, data->ops, mc->data->per_node[node].data_interface, mc->ops) != 1)
  709. /* Not the right type of interface, skip */
  710. continue;
  711. if (next_mc)
  712. {
  713. if (next_mc->remove_notify)
  714. /* Somebody already working here, skip */
  715. continue;
  716. next_mc->remove_notify = &next_mc;
  717. }
  718. /* Note: this may unlock mc_list! */
  719. success = try_to_throw_mem_chunk(mc, node, replicate, 1, is_prefetch);
  720. if (orig_next_mc)
  721. {
  722. if (!next_mc)
  723. /* Oops, somebody dropped the next item while we were
  724. * not keeping the mc_lock. Restart from the beginning
  725. * of the list */
  726. goto restart;
  727. else
  728. {
  729. STARPU_ASSERT(next_mc->remove_notify == &next_mc);
  730. next_mc->remove_notify = NULL;
  731. }
  732. }
  733. }
  734. _starpu_spin_unlock(&mc_lock[node]);
  735. return success;
  736. }
  737. /*
  738. * Try to find a buffer currently in use on the memory node which has the given
  739. * footprint.
  740. */
  741. static int try_to_reuse_potentially_in_use_mc(unsigned node, starpu_data_handle_t handle, struct _starpu_data_replicate *replicate, uint32_t footprint, enum starpu_is_prefetch is_prefetch)
  742. {
  743. struct _starpu_mem_chunk *mc, *next_mc, *orig_next_mc;
  744. int success = 0;
  745. if (is_prefetch >= STARPU_IDLEFETCH)
  746. /* Do not evict a MC just for an idle fetch */
  747. return 0;
  748. /*
  749. * We have to unlock mc_lock before locking header_lock, so we have
  750. * to be careful with the list. We try to do just one pass, by
  751. * remembering the next mc to be tried. If it gets dropped, we restart
  752. * from zero. So we continue until we go through the whole list without
  753. * finding anything to free.
  754. */
  755. _starpu_spin_lock(&mc_lock[node]);
  756. restart:
  757. for (mc = _starpu_mem_chunk_list_begin(&mc_list[node]);
  758. mc != _starpu_mem_chunk_list_end(&mc_list[node]) && !success;
  759. mc = next_mc)
  760. {
  761. /* mc hopefully gets out of the list, we thus need to prefetch
  762. * the next element */
  763. orig_next_mc = next_mc = _starpu_mem_chunk_list_next(mc);
  764. if (mc->remove_notify)
  765. /* Somebody already working here, skip */
  766. continue;
  767. if (mc->footprint != footprint || _starpu_data_interface_compare(handle->per_node[node].data_interface, handle->ops, mc->data->per_node[node].data_interface, mc->ops) != 1)
  768. /* Not the right type of interface, skip */
  769. continue;
  770. if (next_mc)
  771. {
  772. if (next_mc->remove_notify)
  773. /* Somebody already working here, skip */
  774. continue;
  775. next_mc->remove_notify = &next_mc;
  776. }
  777. /* Note: this may unlock mc_list! */
  778. success = try_to_throw_mem_chunk(mc, node, replicate, 1, is_prefetch);
  779. if (orig_next_mc)
  780. {
  781. if (!next_mc)
  782. /* Oops, somebody dropped the next item while we were
  783. * not keeping the mc_lock. Restart from the beginning
  784. * of the list */
  785. goto restart;
  786. else
  787. {
  788. STARPU_ASSERT(next_mc->remove_notify == &next_mc);
  789. next_mc->remove_notify = NULL;
  790. }
  791. }
  792. }
  793. _starpu_spin_unlock(&mc_lock[node]);
  794. return success;
  795. }
  796. /*
  797. * Free the memory chunks that are explicitely tagged to be freed.
  798. */
  799. static size_t flush_memchunk_cache(unsigned node, size_t reclaim)
  800. {
  801. struct _starpu_mem_chunk *mc;
  802. struct mc_cache_entry *entry=NULL, *tmp=NULL;
  803. size_t freed = 0;
  804. restart:
  805. _starpu_spin_lock(&mc_lock[node]);
  806. HASH_ITER(hh, mc_cache[node], entry, tmp)
  807. {
  808. if (!_starpu_mem_chunk_list_empty(&entry->list))
  809. {
  810. mc = _starpu_mem_chunk_list_pop_front(&entry->list);
  811. STARPU_ASSERT(!mc->data);
  812. STARPU_ASSERT(!mc->replicate);
  813. mc_cache_nb[node]--;
  814. STARPU_ASSERT(mc_cache_nb[node] >= 0);
  815. mc_cache_size[node] -= mc->size;
  816. STARPU_ASSERT(mc_cache_size[node] >= 0);
  817. _starpu_spin_unlock(&mc_lock[node]);
  818. freed += free_memory_on_node(mc, node);
  819. free(mc->chunk_interface);
  820. _starpu_mem_chunk_delete(mc);
  821. if (reclaim && freed >= reclaim)
  822. goto out;
  823. goto restart;
  824. }
  825. if (reclaim && freed >= reclaim)
  826. break;
  827. }
  828. _starpu_spin_unlock(&mc_lock[node]);
  829. out:
  830. return freed;
  831. }
  832. /*
  833. * Try to free the buffers currently in use on the memory node. If the force
  834. * flag is set, the memory is freed regardless of coherency concerns (this
  835. * should only be used at the termination of StarPU for instance).
  836. */
  837. static size_t free_potentially_in_use_mc(unsigned node, unsigned force, size_t reclaim, enum starpu_is_prefetch is_prefetch STARPU_ATTRIBUTE_UNUSED)
  838. {
  839. size_t freed = 0;
  840. struct _starpu_mem_chunk *mc, *next_mc;
  841. /*
  842. * We have to unlock mc_lock before locking header_lock, so we have
  843. * to be careful with the list. We try to do just one pass, by
  844. * remembering the next mc to be tried. If it gets dropped, we restart
  845. * from zero. So we continue until we go through the whole list without
  846. * finding anything to free.
  847. */
  848. restart:
  849. _starpu_spin_lock(&mc_lock[node]);
  850. restart2:
  851. for (mc = _starpu_mem_chunk_list_begin(&mc_list[node]);
  852. mc != _starpu_mem_chunk_list_end(&mc_list[node]) && (!reclaim || freed < reclaim);
  853. mc = next_mc)
  854. {
  855. /* mc hopefully gets out of the list, we thus need to prefetch
  856. * the next element */
  857. next_mc = _starpu_mem_chunk_list_next(mc);
  858. if (!force)
  859. {
  860. struct _starpu_mem_chunk *orig_next_mc = next_mc;
  861. if (mc->remove_notify)
  862. /* Somebody already working here, skip */
  863. continue;
  864. if (next_mc)
  865. {
  866. if (next_mc->remove_notify)
  867. /* Somebody already working here, skip */
  868. continue;
  869. next_mc->remove_notify = &next_mc;
  870. }
  871. /* Note: this may unlock mc_list! */
  872. freed += try_to_throw_mem_chunk(mc, node, NULL, 0, is_prefetch);
  873. if (orig_next_mc)
  874. {
  875. if (!next_mc)
  876. /* Oops, somebody dropped the next item while we were
  877. * not keeping the mc_lock. Restart from the beginning
  878. * of the list */
  879. goto restart2;
  880. else
  881. {
  882. STARPU_ASSERT(next_mc->remove_notify == &next_mc);
  883. next_mc->remove_notify = NULL;
  884. }
  885. }
  886. }
  887. else
  888. {
  889. /* Shutting down, really free */
  890. starpu_data_handle_t handle = mc->data;
  891. if (_starpu_spin_trylock(&handle->header_lock))
  892. {
  893. /* Ergl. We are shutting down, but somebody is
  894. * still locking the handle. That's not
  895. * supposed to happen, but better be safe by
  896. * letting it go through. */
  897. _starpu_spin_unlock(&mc_lock[node]);
  898. goto restart;
  899. }
  900. /* We must free the memory now, because we are
  901. * terminating the drivers: note that data coherency is
  902. * not maintained in that case ! */
  903. freed += do_free_mem_chunk(mc, node);
  904. _starpu_spin_unlock(&handle->header_lock);
  905. }
  906. }
  907. _starpu_spin_unlock(&mc_lock[node]);
  908. return freed;
  909. }
  910. size_t _starpu_memory_reclaim_generic(unsigned node, unsigned force, size_t reclaim, enum starpu_is_prefetch is_prefetch)
  911. {
  912. size_t freed = 0;
  913. STARPU_ASSERT(node < STARPU_MAXNODES);
  914. if (reclaim && !force)
  915. {
  916. static unsigned warned;
  917. if (!warned)
  918. {
  919. if (STARPU_ATOMIC_ADD(&warned, 1) == 1)
  920. {
  921. char name[32];
  922. starpu_memory_node_get_name(node, name, sizeof(name));
  923. _STARPU_DISP("Not enough memory left on node %s. Your application data set seems too huge to fit on the device, StarPU will cope by trying to purge %lu MiB out. This message will not be printed again for further purges\n", name, (unsigned long) ((reclaim+1048575) / 1048576));
  924. }
  925. }
  926. }
  927. /* remove all buffers for which there was a removal request */
  928. freed += flush_memchunk_cache(node, reclaim);
  929. /* try to free all allocated data potentially in use */
  930. if (force || (reclaim && freed<reclaim))
  931. freed += free_potentially_in_use_mc(node, force, reclaim, is_prefetch);
  932. return freed;
  933. }
  934. /*
  935. * This function frees all the memory that was implicitely allocated by StarPU
  936. * (for the data replicates). This is not ensuring data coherency, and should
  937. * only be called while StarPU is getting shut down.
  938. */
  939. size_t _starpu_free_all_automatically_allocated_buffers(unsigned node)
  940. {
  941. return _starpu_memory_reclaim_generic(node, 1, 0, STARPU_FETCH);
  942. }
  943. /* Periodic tidy of available memory */
  944. void starpu_memchunk_tidy(unsigned node)
  945. {
  946. starpu_ssize_t total;
  947. starpu_ssize_t available;
  948. size_t target, amount;
  949. STARPU_ASSERT(node < STARPU_MAXNODES);
  950. if (!can_evict(node))
  951. return;
  952. if (mc_clean_nb[node] < (mc_nb[node] * minimum_clean_p) / 100)
  953. {
  954. struct _starpu_mem_chunk *mc, *orig_next_mc, *next_mc;
  955. int skipped = 0; /* Whether we skipped a dirty MC, and we should thus stop updating mc_dirty_head. */
  956. /* _STARPU_DEBUG("%d not clean: %d %d\n", node, mc_clean_nb[node], mc_nb[node]); */
  957. _STARPU_TRACE_START_WRITEBACK_ASYNC(node);
  958. _starpu_spin_lock(&mc_lock[node]);
  959. for (mc = mc_dirty_head[node];
  960. mc && mc_clean_nb[node] < (mc_nb[node] * target_clean_p) / 100;
  961. mc = next_mc, mc && skipped ? 0 : (mc_dirty_head[node] = mc))
  962. {
  963. starpu_data_handle_t handle;
  964. /* mc may get out of the list, we thus need to prefetch
  965. * the next element */
  966. next_mc = _starpu_mem_chunk_list_next(mc);
  967. if (mc->home)
  968. /* Home node, it's always clean */
  969. continue;
  970. if (mc->clean)
  971. /* already clean */
  972. continue;
  973. if (next_mc && next_mc->remove_notify)
  974. {
  975. /* Somebody already working here, skip */
  976. skipped = 1;
  977. continue;
  978. }
  979. handle = mc->data;
  980. STARPU_ASSERT(handle);
  981. /* This data cannnot be pushed outside CPU memory */
  982. if (!handle->ooc && starpu_node_get_kind(node) == STARPU_CPU_RAM)
  983. continue;
  984. if (_starpu_spin_trylock(&handle->header_lock))
  985. {
  986. /* the handle is busy, abort */
  987. skipped = 1;
  988. continue;
  989. }
  990. if (handle->current_mode == STARPU_W)
  991. {
  992. if (handle->write_invalidation_req)
  993. {
  994. /* Some request is invalidating it anyway */
  995. _starpu_spin_unlock(&handle->header_lock);
  996. continue;
  997. }
  998. unsigned n;
  999. for (n = 0; n < STARPU_MAXNODES; n++)
  1000. if (_starpu_get_data_refcnt(handle, n))
  1001. break;
  1002. if (n < STARPU_MAXNODES)
  1003. {
  1004. /* Some task is writing to the handle somewhere */
  1005. _starpu_spin_unlock(&handle->header_lock);
  1006. skipped = 1;
  1007. continue;
  1008. }
  1009. }
  1010. if (
  1011. /* This data should be written through to this node, avoid
  1012. * dropping it! */
  1013. (node < sizeof(handle->wt_mask) * 8 && handle->wt_mask & (1<<node))
  1014. /* This is partitioned, don't care about the
  1015. * whole data, we'll work on the subdatas. */
  1016. || handle->nchildren
  1017. /* REDUX, can't do anything with it, skip it */
  1018. || mc->relaxed_coherency == 2
  1019. )
  1020. {
  1021. _starpu_spin_unlock(&handle->header_lock);
  1022. continue;
  1023. }
  1024. if (handle->home_node != -1 &&
  1025. (handle->per_node[handle->home_node].state != STARPU_INVALID
  1026. || mc->relaxed_coherency == 1))
  1027. {
  1028. /* It's available in the home node, this should have been marked as clean already */
  1029. mc->clean = 1;
  1030. mc_clean_nb[node]++;
  1031. _starpu_spin_unlock(&handle->header_lock);
  1032. continue;
  1033. }
  1034. int target_node;
  1035. if (handle->home_node == -1)
  1036. target_node = choose_target(handle, node);
  1037. else
  1038. target_node = handle->home_node;
  1039. if (target_node == -1)
  1040. {
  1041. /* Nowhere to put it, can't do much */
  1042. _starpu_spin_unlock(&handle->header_lock);
  1043. continue;
  1044. }
  1045. STARPU_ASSERT(target_node != (int) node);
  1046. /* MC is dirty and nobody working on it, submit writeback */
  1047. /* MC will be clean, consider it as such */
  1048. mc->clean = 1;
  1049. mc_clean_nb[node]++;
  1050. orig_next_mc = next_mc;
  1051. if (next_mc)
  1052. {
  1053. STARPU_ASSERT(!next_mc->remove_notify);
  1054. next_mc->remove_notify = &next_mc;
  1055. }
  1056. _starpu_spin_unlock(&mc_lock[node]);
  1057. if (!_starpu_create_request_to_fetch_data(handle, &handle->per_node[target_node], STARPU_R, STARPU_IDLEFETCH, 1, NULL, NULL, 0, "starpu_memchunk_tidy"))
  1058. {
  1059. /* No request was actually needed??
  1060. * Odd, but cope with it. */
  1061. handle = NULL;
  1062. }
  1063. _starpu_spin_lock(&mc_lock[node]);
  1064. if (orig_next_mc)
  1065. {
  1066. if (!next_mc)
  1067. /* Oops, somebody dropped the next item while we were
  1068. * not keeping the mc_lock. Give up for now, and we'll
  1069. * see the rest later */
  1070. ;
  1071. else
  1072. {
  1073. STARPU_ASSERT(next_mc->remove_notify == &next_mc);
  1074. next_mc->remove_notify = NULL;
  1075. }
  1076. }
  1077. if (handle)
  1078. _starpu_spin_unlock(&handle->header_lock);
  1079. }
  1080. _starpu_spin_unlock(&mc_lock[node]);
  1081. _STARPU_TRACE_END_WRITEBACK_ASYNC(node);
  1082. }
  1083. total = starpu_memory_get_total(node);
  1084. if (total <= 0)
  1085. return;
  1086. available = starpu_memory_get_available(node);
  1087. /* Count cached allocation as being available */
  1088. available += mc_cache_size[node];
  1089. if (available >= (starpu_ssize_t) (total * minimum_p) / 100)
  1090. /* Enough available space, do not trigger reclaiming */
  1091. return;
  1092. /* Not enough available space, reclaim until we reach the target. */
  1093. target = (total * target_p) / 100;
  1094. amount = target - available;
  1095. if (!STARPU_RUNNING_ON_VALGRIND && tidying[node])
  1096. /* Some thread is already tidying this node, let it do it */
  1097. return;
  1098. if (STARPU_ATOMIC_ADD(&tidying[node], 1) > 1)
  1099. /* Some thread got it before us, let it do it */
  1100. goto out;
  1101. static unsigned warned;
  1102. if (!warned)
  1103. {
  1104. if (STARPU_ATOMIC_ADD(&warned, 1) == 1)
  1105. {
  1106. char name[32];
  1107. starpu_memory_node_get_name(node, name, sizeof(name));
  1108. _STARPU_DISP("Low memory left on node %s (%ldMiB over %luMiB). Your application data set seems too huge to fit on the device, StarPU will cope by trying to purge %lu MiB out. This message will not be printed again for further purges. The thresholds can be tuned using the STARPU_MINIMUM_AVAILABLE_MEM and STARPU_TARGET_AVAILABLE_MEM environment variables.\n", name, (long) (available / 1048576), (unsigned long) (total / 1048576), (unsigned long) ((amount+1048575) / 1048576));
  1109. }
  1110. }
  1111. _STARPU_TRACE_START_MEMRECLAIM(node,2);
  1112. free_potentially_in_use_mc(node, 0, amount, STARPU_PREFETCH);
  1113. _STARPU_TRACE_END_MEMRECLAIM(node,2);
  1114. out:
  1115. (void) STARPU_ATOMIC_ADD(&tidying[node], -1);
  1116. }
  1117. static struct _starpu_mem_chunk *_starpu_memchunk_init(struct _starpu_data_replicate *replicate, size_t interface_size, unsigned home, unsigned automatically_allocated)
  1118. {
  1119. struct _starpu_mem_chunk *mc = _starpu_mem_chunk_new();
  1120. starpu_data_handle_t handle = replicate->handle;
  1121. STARPU_ASSERT(handle);
  1122. STARPU_ASSERT(handle->ops);
  1123. mc->data = handle;
  1124. mc->footprint = _starpu_compute_data_footprint(handle);
  1125. mc->ops = handle->ops;
  1126. mc->automatically_allocated = automatically_allocated;
  1127. mc->relaxed_coherency = replicate->relaxed_coherency;
  1128. mc->home = home;
  1129. mc->clean = 0;
  1130. if (replicate->relaxed_coherency == 1)
  1131. /* SCRATCH is always easy to drop, thus clean */
  1132. mc->clean = 1;
  1133. else if (replicate->relaxed_coherency == 0 && handle->home_node != -1 && handle->per_node[(int) replicate->memory_node].state != STARPU_INVALID)
  1134. /* This is a normal data and the home node has the value */
  1135. mc->clean = 1;
  1136. mc->replicate = replicate;
  1137. mc->replicate->mc = mc;
  1138. mc->chunk_interface = NULL;
  1139. mc->size_interface = interface_size;
  1140. mc->remove_notify = NULL;
  1141. mc->wontuse = 0;
  1142. return mc;
  1143. }
  1144. static void register_mem_chunk(starpu_data_handle_t handle, struct _starpu_data_replicate *replicate, unsigned automatically_allocated)
  1145. {
  1146. unsigned dst_node = replicate->memory_node;
  1147. struct _starpu_mem_chunk *mc;
  1148. /* the interface was already filled by ops->allocate_data_on_node */
  1149. size_t interface_size = replicate->handle->ops->interface_size;
  1150. /* Put this memchunk in the list of memchunk in use */
  1151. mc = _starpu_memchunk_init(replicate, interface_size, (int) dst_node == handle->home_node, automatically_allocated);
  1152. _starpu_spin_lock(&mc_lock[dst_node]);
  1153. MC_LIST_PUSH_BACK(dst_node, mc);
  1154. _starpu_spin_unlock(&mc_lock[dst_node]);
  1155. }
  1156. /* This function is called when the handle is destroyed (eg. when calling
  1157. * unregister or unpartition). It puts all the memchunks that refer to the
  1158. * specified handle into the cache.
  1159. */
  1160. void _starpu_request_mem_chunk_removal(starpu_data_handle_t handle, struct _starpu_data_replicate *replicate, unsigned node, size_t size)
  1161. {
  1162. struct _starpu_mem_chunk *mc = replicate->mc;
  1163. STARPU_ASSERT(mc->data == handle);
  1164. _starpu_spin_checklocked(&handle->header_lock);
  1165. STARPU_ASSERT(node < STARPU_MAXNODES);
  1166. /* Record the allocated size, so that later in memory
  1167. * reclaiming we can estimate how much memory we free
  1168. * by freeing this. */
  1169. mc->size = size;
  1170. /* Also keep the interface parameters and pointers, for later reuse
  1171. * while detached, or freed */
  1172. _STARPU_MALLOC(mc->chunk_interface, mc->size_interface);
  1173. memcpy(mc->chunk_interface, replicate->data_interface, mc->size_interface);
  1174. /* This memchunk doesn't have to do with the data any more. */
  1175. replicate->mc = NULL;
  1176. mc->replicate = NULL;
  1177. replicate->allocated = 0;
  1178. replicate->automatically_allocated = 0;
  1179. replicate->initialized = 0;
  1180. _starpu_spin_lock(&mc_lock[node]);
  1181. mc->data = NULL;
  1182. /* remove it from the main list */
  1183. MC_LIST_ERASE(node, mc);
  1184. _starpu_spin_unlock(&mc_lock[node]);
  1185. /*
  1186. * Unless the user has provided a main RAM limitation, we would fill
  1187. * memory with cached data and then eventually swap.
  1188. */
  1189. /*
  1190. * This is particularly important when
  1191. * STARPU_USE_ALLOCATION_CACHE is not enabled, as we
  1192. * wouldn't even re-use these allocations!
  1193. */
  1194. if (handle->ops->dontcache || (starpu_node_get_kind(node) == STARPU_CPU_RAM
  1195. #ifdef STARPU_USE_ALLOCATION_CACHE
  1196. && limit_cpu_mem < 0
  1197. #endif
  1198. ))
  1199. {
  1200. /* Free data immediately */
  1201. free_memory_on_node(mc, node);
  1202. free(mc->chunk_interface);
  1203. _starpu_mem_chunk_delete(mc);
  1204. }
  1205. else
  1206. {
  1207. /* put it in the list of buffers to be removed */
  1208. uint32_t footprint = mc->footprint;
  1209. struct mc_cache_entry *entry;
  1210. _starpu_spin_lock(&mc_lock[node]);
  1211. HASH_FIND(hh, mc_cache[node], &footprint, sizeof(footprint), entry);
  1212. if (!entry)
  1213. {
  1214. _STARPU_MALLOC(entry, sizeof(*entry));
  1215. _starpu_mem_chunk_list_init(&entry->list);
  1216. entry->footprint = footprint;
  1217. HASH_ADD(hh, mc_cache[node], footprint, sizeof(entry->footprint), entry);
  1218. }
  1219. mc_cache_nb[node]++;
  1220. mc_cache_size[node] += mc->size;
  1221. _starpu_mem_chunk_list_push_front(&entry->list, mc);
  1222. _starpu_spin_unlock(&mc_lock[node]);
  1223. }
  1224. }
  1225. /*
  1226. * In order to allocate a piece of data, we try to reuse existing buffers if
  1227. * its possible.
  1228. * 1 - we try to reuse a memchunk that is explicitely unused.
  1229. * 2 - we go through the list of memory chunks and find one that is not
  1230. * referenced and that has the same footprint to reuse it.
  1231. * 3 - we call the usual driver's alloc method
  1232. * 4 - we go through the list of memory chunks and release those that are
  1233. * not referenced (or part of those).
  1234. *
  1235. */
  1236. static starpu_ssize_t _starpu_allocate_interface(starpu_data_handle_t handle, struct _starpu_data_replicate *replicate, unsigned dst_node, enum starpu_is_prefetch is_prefetch)
  1237. {
  1238. unsigned attempts = 0;
  1239. starpu_ssize_t allocated_memory;
  1240. int ret;
  1241. starpu_ssize_t data_size = _starpu_data_get_alloc_size(handle);
  1242. int told_reclaiming = 0;
  1243. int reused = 0;
  1244. _starpu_spin_checklocked(&handle->header_lock);
  1245. _starpu_data_allocation_inc_stats(dst_node);
  1246. /* perhaps we can directly reuse a buffer in the free-list */
  1247. uint32_t footprint = _starpu_compute_data_footprint(handle);
  1248. int prefetch_oom = is_prefetch && prefetch_out_of_memory[dst_node];
  1249. #ifdef STARPU_USE_ALLOCATION_CACHE
  1250. if (!prefetch_oom)
  1251. _STARPU_TRACE_START_ALLOC_REUSE(dst_node, data_size, handle, is_prefetch);
  1252. if (try_to_find_reusable_mc(dst_node, handle, replicate, footprint))
  1253. {
  1254. _starpu_allocation_cache_hit(dst_node);
  1255. if (!prefetch_oom)
  1256. _STARPU_TRACE_END_ALLOC_REUSE(dst_node, handle, 1);
  1257. return data_size;
  1258. }
  1259. if (!prefetch_oom)
  1260. _STARPU_TRACE_END_ALLOC_REUSE(dst_node, handle, 0);
  1261. #endif
  1262. STARPU_ASSERT(handle->ops);
  1263. STARPU_ASSERT(handle->ops->allocate_data_on_node);
  1264. STARPU_ASSERT(replicate->data_interface);
  1265. size_t size = handle->ops->interface_size;
  1266. if (!size)
  1267. /* nul-size VLA is undefined... */
  1268. size = 1;
  1269. char data_interface[size];
  1270. memcpy(data_interface, replicate->data_interface, handle->ops->interface_size);
  1271. /* Take temporary reference on the replicate */
  1272. replicate->refcnt++;
  1273. handle->busy_count++;
  1274. _starpu_spin_unlock(&handle->header_lock);
  1275. do
  1276. {
  1277. if (!prefetch_oom)
  1278. _STARPU_TRACE_START_ALLOC(dst_node, data_size, handle, is_prefetch);
  1279. #if defined(STARPU_USE_CUDA) && defined(STARPU_HAVE_CUDA_MEMCPY_PEER) && !defined(STARPU_SIMGRID)
  1280. if (starpu_node_get_kind(dst_node) == STARPU_CUDA_RAM)
  1281. {
  1282. /* To facilitate the design of interface, we set the
  1283. * proper CUDA device in case it is needed. This avoids
  1284. * having to set it again in the malloc method of each
  1285. * interface. */
  1286. starpu_cuda_set_device(starpu_memory_node_get_devid(dst_node));
  1287. }
  1288. #endif
  1289. allocated_memory = handle->ops->allocate_data_on_node(data_interface, dst_node);
  1290. if (!prefetch_oom)
  1291. _STARPU_TRACE_END_ALLOC(dst_node, handle, allocated_memory);
  1292. if (allocated_memory == -ENOMEM)
  1293. {
  1294. size_t handle_size = _starpu_data_get_alloc_size(handle);
  1295. size_t reclaim = starpu_memstrategy_data_size_coefficient*handle_size;
  1296. /* First try to flush data explicitly marked for freeing */
  1297. size_t freed = flush_memchunk_cache(dst_node, reclaim);
  1298. if (freed >= reclaim)
  1299. {
  1300. /* That freed enough data, retry allocating */
  1301. prefetch_out_of_memory[dst_node] = 0;
  1302. continue;
  1303. }
  1304. reclaim -= freed;
  1305. if (is_prefetch >= STARPU_IDLEFETCH)
  1306. {
  1307. /* It's just idle fetch, don't bother existing allocations */
  1308. /* And don't bother tracing allocation attempts */
  1309. prefetch_out_of_memory[dst_node] = 1;
  1310. /* TODO: ideally we should not even try to allocate when we know we have not freed anything */
  1311. continue;
  1312. }
  1313. /* Try to reuse an allocated data with the same interface (to avoid spurious free/alloc) */
  1314. if (_starpu_has_not_important_data && try_to_reuse_not_important_mc(dst_node, handle, replicate, footprint, is_prefetch))
  1315. break;
  1316. if (try_to_reuse_potentially_in_use_mc(dst_node, handle, replicate, footprint, is_prefetch))
  1317. {
  1318. reused = 1;
  1319. allocated_memory = data_size;
  1320. break;
  1321. }
  1322. if (!told_reclaiming)
  1323. {
  1324. /* Prevent prefetches and such from happening */
  1325. (void) STARPU_ATOMIC_ADD(&reclaiming[dst_node], 1);
  1326. told_reclaiming = 1;
  1327. }
  1328. /* That was not enough, we have to really reclaim */
  1329. _STARPU_TRACE_START_MEMRECLAIM(dst_node,is_prefetch);
  1330. freed = _starpu_memory_reclaim_generic(dst_node, 0, reclaim, is_prefetch);
  1331. _STARPU_TRACE_END_MEMRECLAIM(dst_node,is_prefetch);
  1332. if (!freed && is_prefetch >= STARPU_FETCH)
  1333. {
  1334. /* It's just prefetch, don't bother tracing allocation attempts */
  1335. prefetch_out_of_memory[dst_node] = 1;
  1336. /* TODO: ideally we should not even try to allocate when we know we have not freed anything */
  1337. continue;
  1338. }
  1339. prefetch_out_of_memory[dst_node] = 0;
  1340. }
  1341. else
  1342. prefetch_out_of_memory[dst_node] = 0;
  1343. }
  1344. while((allocated_memory == -ENOMEM) && attempts++ < 2);
  1345. int cpt = 0;
  1346. while (cpt < STARPU_SPIN_MAXTRY && _starpu_spin_trylock(&handle->header_lock))
  1347. {
  1348. cpt++;
  1349. _starpu_datawizard_progress(0);
  1350. }
  1351. if (cpt == STARPU_SPIN_MAXTRY)
  1352. _starpu_spin_lock(&handle->header_lock);
  1353. replicate->refcnt--;
  1354. STARPU_ASSERT(replicate->refcnt >= 0);
  1355. STARPU_ASSERT(handle->busy_count > 0);
  1356. handle->busy_count--;
  1357. ret = _starpu_data_check_not_busy(handle);
  1358. STARPU_ASSERT(ret == 0);
  1359. if (told_reclaiming)
  1360. /* We've finished with reclaiming memory, let prefetches start again */
  1361. (void) STARPU_ATOMIC_ADD(&reclaiming[dst_node], -1);
  1362. if (allocated_memory == -ENOMEM)
  1363. {
  1364. if (replicate->allocated)
  1365. /* Didn't manage to allocate, but somebody else did */
  1366. allocated_memory = 0;
  1367. goto out;
  1368. }
  1369. if (reused)
  1370. {
  1371. /* We just reused an allocation, nothing more to do */
  1372. }
  1373. else if (replicate->allocated)
  1374. {
  1375. /* Argl, somebody allocated it in between already, drop this one */
  1376. _STARPU_TRACE_START_FREE(dst_node, data_size, handle);
  1377. handle->ops->free_data_on_node(data_interface, dst_node);
  1378. _STARPU_TRACE_END_FREE(dst_node, handle);
  1379. allocated_memory = 0;
  1380. }
  1381. else
  1382. /* Install newly-allocated interface */
  1383. memcpy(replicate->data_interface, data_interface, handle->ops->interface_size);
  1384. out:
  1385. return allocated_memory;
  1386. }
  1387. int _starpu_allocate_memory_on_node(starpu_data_handle_t handle, struct _starpu_data_replicate *replicate, enum starpu_is_prefetch is_prefetch)
  1388. {
  1389. starpu_ssize_t allocated_memory;
  1390. unsigned dst_node = replicate->memory_node;
  1391. STARPU_ASSERT(dst_node < STARPU_MAXNODES);
  1392. STARPU_ASSERT(handle);
  1393. _starpu_spin_checklocked(&handle->header_lock);
  1394. /* A buffer is already allocated on the node */
  1395. if (replicate->allocated)
  1396. return 0;
  1397. STARPU_ASSERT(replicate->data_interface);
  1398. allocated_memory = _starpu_allocate_interface(handle, replicate, dst_node, is_prefetch);
  1399. /* perhaps we could really not handle that capacity misses */
  1400. if (allocated_memory == -ENOMEM)
  1401. return -ENOMEM;
  1402. if (replicate->allocated)
  1403. /* Somebody allocated it in between already */
  1404. return 0;
  1405. register_mem_chunk(handle, replicate, 1);
  1406. replicate->allocated = 1;
  1407. replicate->automatically_allocated = 1;
  1408. if (replicate->relaxed_coherency == 0 && (starpu_node_get_kind(dst_node) == STARPU_CPU_RAM))
  1409. {
  1410. /* We are allocating the buffer in main memory, also
  1411. * register it for starpu_data_handle_to_pointer() */
  1412. void *ptr = starpu_data_handle_to_pointer(handle, dst_node);
  1413. if (ptr != NULL)
  1414. {
  1415. _starpu_data_register_ram_pointer(handle, ptr);
  1416. }
  1417. }
  1418. return 0;
  1419. }
  1420. unsigned starpu_data_test_if_allocated_on_node(starpu_data_handle_t handle, unsigned memory_node)
  1421. {
  1422. STARPU_ASSERT(memory_node < STARPU_MAXNODES);
  1423. return handle->per_node[memory_node].allocated;
  1424. }
  1425. /* This memchunk has been recently used, put it last on the mc_list, so we will
  1426. * try to evict it as late as possible */
  1427. void _starpu_memchunk_recently_used(struct _starpu_mem_chunk *mc, unsigned node)
  1428. {
  1429. if (!mc)
  1430. /* user-allocated memory */
  1431. return;
  1432. STARPU_ASSERT(node < STARPU_MAXNODES);
  1433. if (!can_evict(node))
  1434. /* Don't bother */
  1435. return;
  1436. _starpu_spin_lock(&mc_lock[node]);
  1437. MC_LIST_ERASE(node, mc);
  1438. mc->wontuse = 0;
  1439. MC_LIST_PUSH_BACK(node, mc);
  1440. _starpu_spin_unlock(&mc_lock[node]);
  1441. }
  1442. /* This memchunk will not be used in the close future, put it on the clean
  1443. * list, so we will to evict it first */
  1444. void _starpu_memchunk_wont_use(struct _starpu_mem_chunk *mc, unsigned node)
  1445. {
  1446. if (!mc)
  1447. /* user-allocated memory */
  1448. return;
  1449. STARPU_ASSERT(node < STARPU_MAXNODES);
  1450. if (!can_evict(node))
  1451. /* Don't bother */
  1452. return;
  1453. _starpu_spin_lock(&mc_lock[node]);
  1454. mc->wontuse = 1;
  1455. if (mc->data && mc->data->home_node != -1)
  1456. {
  1457. MC_LIST_ERASE(node, mc);
  1458. /* Caller will schedule a clean transfer */
  1459. mc->clean = 1;
  1460. MC_LIST_PUSH_CLEAN(node, mc);
  1461. }
  1462. /* TODO: else push to head of data to be evicted */
  1463. _starpu_spin_unlock(&mc_lock[node]);
  1464. }
  1465. /* This memchunk is being written to, and thus becomes dirty */
  1466. void _starpu_memchunk_dirty(struct _starpu_mem_chunk *mc, unsigned node)
  1467. {
  1468. if (!mc)
  1469. /* user-allocated memory */
  1470. return;
  1471. if (mc->home)
  1472. /* Home is always clean */
  1473. return;
  1474. STARPU_ASSERT(node < STARPU_MAXNODES);
  1475. if (!can_evict(node))
  1476. /* Don't bother */
  1477. return;
  1478. _starpu_spin_lock(&mc_lock[node]);
  1479. if (mc->relaxed_coherency == 1)
  1480. {
  1481. /* SCRATCH, make it clean if not already*/
  1482. if (!mc->clean)
  1483. {
  1484. mc_clean_nb[node]++;
  1485. mc->clean = 1;
  1486. }
  1487. }
  1488. else
  1489. {
  1490. if (mc->clean)
  1491. {
  1492. mc_clean_nb[node]--;
  1493. mc->clean = 0;
  1494. }
  1495. }
  1496. _starpu_spin_unlock(&mc_lock[node]);
  1497. }
  1498. #ifdef STARPU_MEMORY_STATS
  1499. void _starpu_memory_display_stats_by_node(FILE *stream, int node)
  1500. {
  1501. STARPU_ASSERT(node < STARPU_MAXNODES);
  1502. _starpu_spin_lock(&mc_lock[node]);
  1503. if (!_starpu_mem_chunk_list_empty(&mc_list[node]))
  1504. {
  1505. struct _starpu_mem_chunk *mc;
  1506. fprintf(stream, "#-------\n");
  1507. fprintf(stream, "Data on Node #%d\n",node);
  1508. for (mc = _starpu_mem_chunk_list_begin(&mc_list[node]);
  1509. mc != _starpu_mem_chunk_list_end(&mc_list[node]);
  1510. mc = _starpu_mem_chunk_list_next(mc))
  1511. {
  1512. _starpu_memory_display_handle_stats(stream, mc->data);
  1513. }
  1514. }
  1515. _starpu_spin_unlock(&mc_lock[node]);
  1516. }
  1517. void _starpu_data_display_memory_stats(FILE *stream)
  1518. {
  1519. unsigned node;
  1520. fprintf(stream, "\n#---------------------\n");
  1521. fprintf(stream, "Memory stats :\n");
  1522. for (node = 0; node < STARPU_MAXNODES; node++)
  1523. {
  1524. _starpu_memory_display_stats_by_node(stream, node);
  1525. }
  1526. fprintf(stream, "\n#---------------------\n");
  1527. }
  1528. #endif
  1529. void starpu_data_display_memory_stats(void)
  1530. {
  1531. #ifdef STARPU_MEMORY_STATS
  1532. _starpu_data_display_memory_stats(stderr);
  1533. #endif
  1534. }
  1535. static int
  1536. get_better_disk_can_accept_size(starpu_data_handle_t handle, unsigned node)
  1537. {
  1538. int target = -1;
  1539. unsigned nnodes = starpu_memory_nodes_get_count();
  1540. unsigned int i;
  1541. double time_disk = 0.0;
  1542. for (i = 0; i < nnodes; i++)
  1543. {
  1544. if (starpu_node_get_kind(i) == STARPU_DISK_RAM && i != node &&
  1545. (handle->per_node[i].allocated ||
  1546. _starpu_memory_manager_test_allocate_size(i, _starpu_data_get_alloc_size(handle)) == 1))
  1547. {
  1548. /* if we can write on the disk */
  1549. if ((_starpu_get_disk_flag(i) & STARPU_DISK_NO_RECLAIM) == 0)
  1550. {
  1551. unsigned numa;
  1552. unsigned nnumas = starpu_memory_nodes_get_numa_count();
  1553. for (numa = 0; numa < nnumas; numa++)
  1554. {
  1555. /* TODO : check if starpu_transfer_predict(node, i,...) is the same */
  1556. double time_tmp = starpu_transfer_predict(node, numa, _starpu_data_get_alloc_size(handle)) + starpu_transfer_predict(i, numa, _starpu_data_get_alloc_size(handle));
  1557. if (target == -1 || time_disk > time_tmp)
  1558. {
  1559. target = i;
  1560. time_disk = time_tmp;
  1561. }
  1562. }
  1563. }
  1564. }
  1565. }
  1566. return target;
  1567. }
  1568. #ifdef STARPU_DEVEL
  1569. # warning TODO: better choose NUMA node
  1570. #endif
  1571. /* Choose a target memory node to put the value of the handle, because the current location (node) is getting tight */
  1572. static int
  1573. choose_target(starpu_data_handle_t handle, unsigned node)
  1574. {
  1575. int target = -1;
  1576. size_t size_handle = _starpu_data_get_alloc_size(handle);
  1577. if (handle->home_node != -1)
  1578. /* try to push on RAM if we can before to push on disk */
  1579. if(starpu_node_get_kind(handle->home_node) == STARPU_DISK_RAM && (starpu_node_get_kind(node) != STARPU_CPU_RAM))
  1580. {
  1581. unsigned i;
  1582. unsigned nb_numa_nodes = starpu_memory_nodes_get_numa_count();
  1583. for (i=0; i<nb_numa_nodes; i++)
  1584. {
  1585. if (handle->per_node[i].allocated ||
  1586. _starpu_memory_manager_test_allocate_size(i, size_handle) == 1)
  1587. {
  1588. target = i;
  1589. break;
  1590. }
  1591. }
  1592. if (target == -1)
  1593. {
  1594. target = get_better_disk_can_accept_size(handle, node);
  1595. }
  1596. }
  1597. /* others memory nodes */
  1598. else
  1599. {
  1600. target = handle->home_node;
  1601. }
  1602. else
  1603. {
  1604. /* handle->home_node == -1 */
  1605. /* no place for datas in RAM, we push on disk */
  1606. if (starpu_node_get_kind(node) == STARPU_CPU_RAM)
  1607. {
  1608. target = get_better_disk_can_accept_size(handle, node);
  1609. } else {
  1610. /* node != 0 */
  1611. /* try to push data to RAM if we can before to push on disk*/
  1612. unsigned i;
  1613. unsigned nb_numa_nodes = starpu_memory_nodes_get_numa_count();
  1614. for (i=0; i<nb_numa_nodes; i++)
  1615. {
  1616. if (handle->per_node[i].allocated ||
  1617. _starpu_memory_manager_test_allocate_size(i, size_handle) == 1)
  1618. {
  1619. target = i;
  1620. break;
  1621. }
  1622. }
  1623. /* no place in RAM */
  1624. if (target == -1)
  1625. {
  1626. target = get_better_disk_can_accept_size(handle, node);
  1627. }
  1628. }
  1629. }
  1630. /* we haven't the right to write on the disk */
  1631. if (target != -1 && starpu_node_get_kind(target) == STARPU_DISK_RAM && (_starpu_get_disk_flag(target) & STARPU_DISK_NO_RECLAIM))
  1632. target = -1;
  1633. return target;
  1634. }
  1635. void starpu_data_set_user_data(starpu_data_handle_t handle, void* user_data)
  1636. {
  1637. handle->user_data = user_data;
  1638. }
  1639. void *starpu_data_get_user_data(starpu_data_handle_t handle)
  1640. {
  1641. return handle->user_data;
  1642. }