Pārlūkot izejas kodu

When enforcing data dependencies, it is important that the ressources are
always taken in the same order to prevent deadlocks. To do so, we need a total
ordering between the different data handles. This is achieved with a
lexicographic order in order to handle concurrent accesses to subsets of the
data when filters are used.

Cédric Augonnet 15 gadi atpakaļ
vecāks
revīzija
d01cc2e5f9

+ 2 - 0
src/Makefile.am

@@ -62,6 +62,7 @@ noinst_HEADERS = 						\
 	datawizard/memalloc.h					\
 	datawizard/copy-driver.h				\
 	datawizard/coherency.h					\
+	datawizard/sort_data_handles.h				\
 	datawizard/memory_nodes.h				\
 	datawizard/interfaces/data_interface.h			\
 	common/hash.h						\
@@ -120,6 +121,7 @@ libstarpu_la_SOURCES = 						\
 	datawizard/progress.c					\
 	datawizard/copy-driver.c				\
 	datawizard/hierarchy.c					\
+	datawizard/sort_data_handles.c				\
 	datawizard/memalloc.c					\
 	datawizard/footprint.c					\
 	datawizard/datastats.c					\

+ 14 - 5
src/core/dependencies/data-concurrency.c

@@ -18,6 +18,7 @@
 #include <datawizard/coherency.h>
 #include <core/policies/sched_policy.h>
 #include <common/starpu-spinlock.h>
+#include <datawizard/sort_data_handles.h>
 
 static unsigned _submit_job_enforce_data_deps(starpu_job_t j, unsigned start_buffer_index);
 
@@ -114,8 +115,10 @@ static unsigned attempt_to_submit_data_request_from_job(starpu_job_t j, unsigned
 {
 	unsigned ret;
 
-	starpu_data_handle handle = j->task->buffers[buffer_index].handle;
-	starpu_access_mode mode = j->task->buffers[buffer_index].mode;
+	/* Note that we do not access j->task->buffers, but j->ordered_buffers
+	 * which is a sorted copy of it. */
+	starpu_data_handle handle = j->ordered_buffers[buffer_index].handle;
+	starpu_access_mode mode = j->ordered_buffers[buffer_index].mode;
 
 	while (_starpu_spin_trylock(&handle->header_lock))
 		_starpu_datawizard_progress(_starpu_get_local_memory_node(), 0);
@@ -166,8 +169,6 @@ static unsigned _submit_job_enforce_data_deps(starpu_job_t j, unsigned start_buf
 {
 	unsigned buf;
 
-	/* TODO compute an ordered list of the data */
-
 	unsigned nbuffers = j->task->cl->nbuffers;
 	for (buf = start_buffer_index; buf < nbuffers; buf++)
 	{
@@ -184,9 +185,17 @@ static unsigned _submit_job_enforce_data_deps(starpu_job_t j, unsigned start_buf
    reading and another writing) */
 unsigned _starpu_submit_job_enforce_data_deps(starpu_job_t j)
 {
-	if ((j->task->cl == NULL) || (j->task->cl->nbuffers == 0))
+	struct starpu_codelet_t *cl = j->task->cl;
+
+	if ((cl == NULL) || (cl->nbuffers == 0))
 		return 0;
 
+	/* Compute an ordered list of the different pieces of data so that we
+	 * grab then according to a total order, thus avoiding a deadlock
+	 * condition */
+	memcpy(j->ordered_buffers, j->task->buffers, cl->nbuffers*sizeof(starpu_buffer_descr));
+	_starpu_sort_task_handles(j->ordered_buffers, cl->nbuffers);
+
 	return _submit_job_enforce_data_deps(j, 0);
 }
 

+ 2 - 0
src/core/jobs.h

@@ -57,6 +57,8 @@ LIST_TYPE(starpu_job,
 	pthread_mutex_t sync_mutex;
 	pthread_cond_t sync_cond;
 
+	struct starpu_buffer_descr_t ordered_buffers[STARPU_NMAXBUFS];
+
 	struct starpu_tag_s *tag;
 	struct starpu_cg_list_s job_successors;
 

+ 7 - 0
src/datawizard/coherency.h

@@ -83,6 +83,13 @@ struct starpu_data_state_t {
 	starpu_spinlock_t header_lock;
 
 	uint32_t nnodes; /* the number of memory nodes that may use it */
+
+	/* In case we user filters, the handle may describe a sub-data */
+	struct starpu_data_state_t *root_handle; /* root of the tree */
+	struct starpu_data_state_t *father_handle; /* father of the node, NULL if the current node is the root */
+	unsigned sibling_index; /* indicate which child this node is from the father's perpsective (if any) */
+	unsigned depth; /* what's the depth of the tree ? */
+
 	struct starpu_data_state_t *children;
 	unsigned nchildren;
 

+ 8 - 0
src/datawizard/hierarchy.c

@@ -67,6 +67,10 @@ void _starpu_register_new_data(starpu_data_handle handle, uint32_t home_node, ui
 
 	/* there is no hierarchy yet */
 	handle->nchildren = 0;
+	handle->root_handle = handle;
+	handle->father_handle = NULL;
+	handle->sibling_index = 0; /* could be anything for the root */
+	handle->depth = 1; /* the tree is just a node yet */
 
 	handle->is_not_important = 0;
 
@@ -196,6 +200,10 @@ void starpu_partition_data(starpu_data_handle initial_handle, starpu_filter *f)
 		STARPU_ASSERT(children);
 
 		children->nchildren = 0;
+		children->root_handle = initial_handle->root_handle;
+		children->father_handle = initial_handle;
+		children->sibling_index = i;
+		children->depth = initial_handle->depth + 1;
 
 		children->is_not_important = initial_handle->is_not_important;
 

+ 99 - 0
src/datawizard/sort_data_handles.c

@@ -0,0 +1,99 @@
+/*
+ * StarPU
+ * Copyright (C) INRIA 2008-2010 (see AUTHORS file)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU Lesser General Public License in COPYING.LGPL for more details.
+ */
+
+#include <starpu.h>
+#include <common/config.h>
+
+#include "hierarchy.h"
+
+/* To avoid deadlocks in case we have multiple tasks accessing the same piece
+ * of data  (eg. task T1 needs A and B, and T2 needs B and A), we need to lock
+ * them in order, so that we need a total order over data. We must also not
+ * lock a child before its parent. */
+
+static void find_data_path(struct starpu_data_state_t *data, unsigned path[])
+{
+	unsigned depth = data->depth;
+	struct starpu_data_state_t *current = data;
+
+	/* Compute the path from the root to the data */
+	unsigned level; /* level is the distance between the node and the current node */
+	for (level = 0; level < depth; level++)
+	{
+		STARPU_ASSERT(data);
+		path[depth - level - 1] = current->sibling_index;
+		current = data->father_handle;
+	}
+} 
+
+static int _compar_data_paths(const unsigned pathA[], unsigned depthA,
+				const unsigned pathB[], unsigned depthB)
+{
+	unsigned level;
+	unsigned depth = STARPU_MIN(depthA, depthB);
+
+	for (level = 0; level < depth; level++)
+	{
+		if (pathA[level] != pathB[level])
+			return (pathA[level] < pathB[level])?-1:1;
+	}
+
+	/* If this is the same path */
+	if (depthA == depthB)
+		return 0;
+
+	/* A is a subdata of B or B is a subdata of A, so the smallest one is
+	 * the father of the other (we take this convention). */
+	return (depthA < depthB)?-1:1;
+}
+
+/* A comparision function between two handles makes it possible to use qsort to
+ * sort a list of handles */
+static int _starpu_compar_handles(struct starpu_data_state_t *dataA,
+				struct starpu_data_state_t *dataB)
+{
+	/* Perhaps we have the same piece of data */
+	if (dataA == dataB)
+		return 0;
+
+	/* In case we have data/subdata from different trees */
+	if (dataA->root_handle != dataB->root_handle)
+		return ((dataA->root_handle < dataB->root_handle)?-1:1);
+
+	/* Things get more complicated: we need to find the location of dataA
+	 * and dataB within the tree. */
+	unsigned dataA_path[dataA->depth - 1];
+	unsigned dataB_path[dataB->depth - 1];
+
+	find_data_path(dataA, dataA_path);
+	find_data_path(dataB, dataB_path);
+
+	return _compar_data_paths(dataA_path, dataA->depth, dataB_path, dataB->depth);
+}
+
+static int _starpu_compar_buffer_descr(const void *_descrA, const void *_descrB)
+{
+	const starpu_buffer_descr *descrA = _descrA;
+	const starpu_buffer_descr *descrB = _descrB;
+
+	return _starpu_compar_handles(descrA->handle, descrB->handle);
+}
+
+/* The descr array will be overwritten, so this must be a copy ! */
+void _starpu_sort_task_handles(starpu_buffer_descr descr[], unsigned nbuffers)
+{
+	qsort(descr, nbuffers, sizeof(starpu_buffer_descr), _starpu_compar_buffer_descr);
+}

+ 30 - 0
src/datawizard/sort_data_handles.h

@@ -0,0 +1,30 @@
+/*
+ * StarPU
+ * Copyright (C) INRIA 2008-2010 (see AUTHORS file)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU Lesser General Public License in COPYING.LGPL for more details.
+ */
+
+#ifndef __SORT_DATA_HANDLES_H__
+#define __SORT_DATA_HANDLES_H__
+
+#include <starpu.h>
+#include <common/config.h>
+#include <stdlib.h>
+
+#include <stdarg.h>
+#include <datawizard/coherency.h>
+#include <datawizard/memalloc.h>
+
+void _starpu_sort_task_handles(starpu_buffer_descr descr[], unsigned nbuffers);
+
+#endif // SORT_DATA_HANDLES