public inbox for fortran@gcc.gnu.org
 help / color / mirror / Atom feed
From: Julian Brown <julian@codesourcery.com>
To: Thomas Schwinge <thomas@codesourcery.com>
Cc: <gcc-patches@gcc.gnu.org>, <fortran@gcc.gnu.org>,
	<tobias@codesourcery.com>, <jakub@redhat.com>,
	Tom de Vries <tdevries@suse.de>
Subject: Re: [PATCH 1/5] OpenMP, NVPTX: memcpy[23]D bias correction
Date: Mon, 2 Oct 2023 15:53:59 +0100	[thread overview]
Message-ID: <20231002155359.3a44a582@squid.athome> (raw)
In-Reply-To: <87sf704k5l.fsf@euler.schwinge.homeip.net>

[-- Attachment #1: Type: text/plain, Size: 1903 bytes --]

On Wed, 27 Sep 2023 00:57:58 +0200
Thomas Schwinge <thomas@codesourcery.com> wrote:

> On 2023-09-06T02:34:30-0700, Julian Brown <julian@codesourcery.com>
> wrote:
> > This patch works around behaviour of the 2D and 3D memcpy
> > operations in the CUDA driver runtime.  Particularly in Fortran,
> > the "base pointer" of an array (used for either source or
> > destination of a host/device copy) may lie outside of data that is
> > actually stored on the device.  The fix is to make sure that we use
> > the first element of data to be transferred instead, and adjust
> > parameters accordingly.  
> 
> Do you (a) have a stand-alone test case for this (that is, not
> depending on your other pending patches, so that this could go in
> directly -- together with the before-FAIL test case).

Thanks for the reply! Here's a version with a stand-alone test case.

> Do you (b)
> know if is this a bug in our use of the CUDA Driver API or rather in
> CUDA itself?  If the latter, have you reported this to Nvidia?

I don't think the CUDA behaviour is *wrong*, as such -- at least to the
C/C++ way of thinking (or indeed a graphics-oriented way of thinking),
one would normally think of an array as having a zero-based origin, and
these 2D/3D memory copies would be intended as a way of updating just a
part of an array (or texture) that has full duplicate copies on both
the host and device.  Our use-case just happens to be a bit different,
both because Fortran (internally) represents an array by a zero-based
origin but may use 1-based (or whatever-based) indices, and because we
support partial mappings of host arrays on the device in all three
supported languages -- which amounts to much the same thing, actually.

That said, it *could* be fixed in CUDA, though probably not in all the
versions currently deployed out there in the world.  So I guess we'd
still need a patch like this anyway.

Julian

[-- Attachment #2: cuda-memcpyxd-bias-2.diff --]
[-- Type: text/x-patch, Size: 5759 bytes --]

commit f6fd3ad060bbe5c57661cd861d009dbc2b415778
Author: Julian Brown <julian@codesourcery.com>
Date:   Wed Aug 23 23:46:29 2023 +0000

    OpenMP, NVPTX: memcpy[23]D bias correction
    
    This patch works around behaviour of the 2D and 3D memcpy operations in
    the CUDA driver runtime.  Particularly in Fortran, the "base pointer"
    of an array (used for either source or destination of a host/device copy)
    may lie outside of data that is actually stored on the device.  The fix
    is to make sure that we use the first element of data to be transferred
    instead, and adjust parameters accordingly.
    
    2023-10-02  Julian Brown  <julian@codesourcery.com>
    
    libgomp/
            * plugin/plugin-nvptx.c (GOMP_OFFLOAD_memcpy2d): Adjust parameters to
            avoid out-of-bounds array checks in CUDA runtime.
            (GOMP_OFFLOAD_memcpy3d): Likewise.
            * testsuite/libgomp.c-c++-common/memcpyxd-bias-1.c: New test.

diff --git a/libgomp/plugin/plugin-nvptx.c b/libgomp/plugin/plugin-nvptx.c
index 00d4241ae02..cefe288a8aa 100644
--- a/libgomp/plugin/plugin-nvptx.c
+++ b/libgomp/plugin/plugin-nvptx.c
@@ -1827,6 +1827,35 @@ GOMP_OFFLOAD_memcpy2d (int dst_ord, int src_ord, size_t dim1_size,
   data.srcXInBytes = src_offset1_size;
   data.srcY = src_offset0_len;
 
+  if (data.srcXInBytes != 0 || data.srcY != 0)
+    {
+      /* Adjust origin to the actual array data, else the CUDA 2D memory
+	 copy API calls below may fail to validate source/dest pointers
+	 correctly (especially for Fortran where the "virtual origin" of an
+	 array is often outside the stored data).  */
+      if (src_ord == -1)
+	data.srcHost = (const void *) ((const char *) data.srcHost
+				      + data.srcY * data.srcPitch
+				      + data.srcXInBytes);
+      else
+	data.srcDevice += data.srcY * data.srcPitch + data.srcXInBytes;
+      data.srcXInBytes = 0;
+      data.srcY = 0;
+    }
+
+  if (data.dstXInBytes != 0 || data.dstY != 0)
+    {
+      /* As above.  */
+      if (dst_ord == -1)
+	data.dstHost = (void *) ((char *) data.dstHost
+				 + data.dstY * data.dstPitch
+				 + data.dstXInBytes);
+      else
+	data.dstDevice += data.dstY * data.dstPitch + data.dstXInBytes;
+      data.dstXInBytes = 0;
+      data.dstY = 0;
+    }
+
   CUresult res = CUDA_CALL_NOCHECK (cuMemcpy2D, &data);
   if (res == CUDA_ERROR_INVALID_VALUE)
     /* If pitch > CU_DEVICE_ATTRIBUTE_MAX_PITCH or for device-to-device
@@ -1895,6 +1924,44 @@ GOMP_OFFLOAD_memcpy3d (int dst_ord, int src_ord, size_t dim2_size,
   data.srcY = src_offset1_len;
   data.srcZ = src_offset0_len;
 
+  if (data.srcXInBytes != 0 || data.srcY != 0 || data.srcZ != 0)
+    {
+      /* Adjust origin to the actual array data, else the CUDA 3D memory
+	 copy API call below may fail to validate source/dest pointers
+	 correctly (especially for Fortran where the "virtual origin" of an
+	 array is often outside the stored data).  */
+      if (src_ord == -1)
+	data.srcHost
+	  = (const void *) ((const char *) data.srcHost
+			    + (data.srcZ * data.srcHeight + data.srcY)
+			      * data.srcPitch
+			    + data.srcXInBytes);
+      else
+	data.srcDevice
+	  += (data.srcZ * data.srcHeight + data.srcY) * data.srcPitch
+	     + data.srcXInBytes;
+      data.srcXInBytes = 0;
+      data.srcY = 0;
+      data.srcZ = 0;
+    }
+
+  if (data.dstXInBytes != 0 || data.dstY != 0 || data.dstZ != 0)
+    {
+      /* As above.  */
+      if (dst_ord == -1)
+	data.dstHost = (void *) ((char *) data.dstHost
+				 + (data.dstZ * data.dstHeight + data.dstY)
+				   * data.dstPitch
+				 + data.dstXInBytes);
+      else
+	data.dstDevice
+	  += (data.dstZ * data.dstHeight + data.dstY) * data.dstPitch
+	     + data.dstXInBytes;
+      data.dstXInBytes = 0;
+      data.dstY = 0;
+      data.dstZ = 0;
+    }
+
   CUDA_CALL (cuMemcpy3D, &data);
   return true;
 }
diff --git a/libgomp/testsuite/libgomp.c-c++-common/memcpyxd-bias-1.c b/libgomp/testsuite/libgomp.c-c++-common/memcpyxd-bias-1.c
new file mode 100644
index 00000000000..6aa7b3d614f
--- /dev/null
+++ b/libgomp/testsuite/libgomp.c-c++-common/memcpyxd-bias-1.c
@@ -0,0 +1,61 @@
+/* { dg-do run } */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+#include <omp.h>
+
+/* Say this is N rows and M columns.  */
+#define N 1024
+#define M 2048
+
+#define row_offset 256
+#define row_length 512
+#define col_offset 128
+#define col_length 384
+
+int
+main ()
+{
+  int *arr2d = (int *) calloc (N * M, sizeof (int));
+  uintptr_t dstptr;
+  int hostdev = omp_get_initial_device ();
+  int targdev;
+
+#pragma omp target enter data map(to: arr2d[col_offset*M:col_length*M])
+
+#pragma omp target map(from: targdev, dstptr) \
+		   map(present, tofrom: arr2d[col_offset*M:col_length*M])
+  {
+    for (int j = col_offset; j < col_offset + col_length; j++)
+      for (int i = row_offset; i < row_offset + row_length; i++)
+	arr2d[j * M + i]++;
+    targdev = omp_get_device_num ();
+    dstptr = (uintptr_t) arr2d;
+  }
+
+  /* Copy rectangular block back to the host.  */
+  {
+    size_t volume[2] = { col_length, row_length };
+    size_t offsets[2] = { col_offset, row_offset };
+    size_t dimensions[2] = { N, M };
+    omp_target_memcpy_rect ((void *) arr2d, (const void *) dstptr,
+			    sizeof (int), 2, &volume[0], &offsets[0],
+			    &offsets[0], &dimensions[0], &dimensions[0],
+			    hostdev, targdev);
+  }
+
+#pragma omp target exit data map(release: arr2d[col_offset*M:col_length*M])
+
+  for (int j = 0; j < N; j++)
+    for (int i = 0; i < M; i++)
+      if (i >= row_offset && i < row_offset + row_length
+	  && j >= col_offset && j < col_offset + col_length)
+	assert (arr2d[j * M + i] == 1);
+      else
+	assert (arr2d[j * M + i] == 0);
+
+  free (arr2d);
+
+  return 0;
+}

  reply	other threads:[~2023-10-02 14:54 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-09-06  9:34 [PATCH 0/5] OpenMP: Array-shaping operator and strided/rectangular 'target update' support Julian Brown
2023-09-06  9:34 ` [PATCH 1/5] OpenMP, NVPTX: memcpy[23]D bias correction Julian Brown
2023-09-26 22:57   ` Thomas Schwinge
2023-10-02 14:53     ` Julian Brown [this message]
2023-12-19 20:45       ` Tobias Burnus
2023-09-06  9:34 ` [PATCH 2/5] OpenMP: Allow complete replacement of clause during map/to/from expansion Julian Brown
2023-09-06  9:34 ` [PATCH 3/5] OpenMP: Support strided and shaped-array updates for C++ Julian Brown
2023-09-06  9:34 ` [PATCH 4/5] OpenMP: Array shaping operator and strided "target update" for C Julian Brown
2023-09-06  9:34 ` [PATCH 5/5] OpenMP: Noncontiguous "target update" for Fortran Julian Brown

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20231002155359.3a44a582@squid.athome \
    --to=julian@codesourcery.com \
    --cc=fortran@gcc.gnu.org \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jakub@redhat.com \
    --cc=tdevries@suse.de \
    --cc=thomas@codesourcery.com \
    --cc=tobias@codesourcery.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).