memalloc.c 55 KB

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