Note that there are some explanatory texts on larger screens.

plurals
  1. PODividing up CUDA cudaMemcpy into chunks
    primarykey
    data
    text
    <p>A co-worker and I were brainstorming on how to mitigate the memory transfer time between host and device and it came up that perhaps arranging things to one mega-transfer (i.e. one single call) might help. This led me to create a test case where I took timings of transferring few large data chunks vs. many small data data chunks. I got some very interesting/strange results, and was wondering if anyone here had an explanation?</p> <p>I won't put my whole code up here since it's quite long, but I tested the chunking in two different ways:</p> <ol> <li><p>Explicitly writing out all cudaMemcpy's, e.g.:</p> <p>cudaEventRecord(start, 0);<br> cudaMemcpy(aD, a, nBytes/10, cudaMemcpyHostToDevice);<br> cudaMemcpy(aD + 1*nBytes/10, a + 1*nBytes/10, nBytes/10, cudaMemcpyHostToDevice);<br> cudaMemcpy(aD + 2*nBytes/10, a + 2*nBytes/10, nBytes/10, cudaMemcpyHostToDevice);<br> cudaMemcpy(aD + 3*nBytes/10, a + 3*nBytes/10, nBytes/10, cudaMemcpyHostToDevice);<br> cudaMemcpy(aD + 4*nBytes/10, a + 4*nBytes/10, nBytes/10, cudaMemcpyHostToDevice);<br> cudaMemcpy(aD + 5*nBytes/10, a + 5*nBytes/10, nBytes/10, cudaMemcpyHostToDevice);<br> cudaMemcpy(aD + 6*nBytes/10, a + 6*nBytes/10, nBytes/10, cudaMemcpyHostToDevice);<br> cudaMemcpy(aD + 7*nBytes/10, a + 7*nBytes/10, nBytes/10, cudaMemcpyHostToDevice);<br> cudaMemcpy(aD + 8*nBytes/10, a + 8*nBytes/10, nBytes/10, cudaMemcpyHostToDevice);<br> cudaMemcpy(aD + 9*nBytes/10, a + 9*nBytes/10, nBytes/10, cudaMemcpyHostToDevice);<br> cudaEventRecord(stop, 0);<br> cudaEventSynchronize(stop);<br> cudaEventElapsedTime(&amp;time, start, stop); </p></li> <li><p>Putting the cudaMemcpy's into a for loop:</p> <p>cudaEventRecord(start, 0);<br> for(int i = 0; i &lt; nChunks; i++)<br> {<br> cudaMemcpy(aD + i*nBytes/nChunks, a + i*nBytes/nChunks, nBytes/nChunks, cudaMemcpyHostToDevice);<br> }<br> cudaEventRecord(stop, 0);<br> cudaEventSynchronize(stop);<br> cudaEventElapsedTime(&amp;time, start, stop); </p></li> </ol> <p>To note, I also did a "warm-up" transfer at the start of each test just in case, though I don't think it was needed (the context was created by a cudaMalloc call).</p> <p>I tested this on total transfer sizes ranging from 1 MB to 1 GB, where each test case transferred the same amount of information regardless of how it was chunked up. A sample of my output is this:</p> <blockquote> <p>single large transfer = 0.451616 ms<br> 10 explicit transfers = 0.198016 ms<br> 100 explicit transfers = 0.691712 ms<br> 10 looped transfers = 0.174848 ms<br> 100 looped transfers = 0.683744 ms<br> 1000 looped transfers = 6.145792 ms<br> 10000 looped transfers = 104.981247 ms<br> 100000 looped transfers = 13097.441406 ms </p> </blockquote> <p>What's interesting here and what I don't get is that, across the board, the 10 transfers were ALWAYS faster by a significant amount than any of the others, even the single large transfer! And that result stayed consistent no matter how large or small the data set was (i.e. 10x100MB vs 1x1GB or 10x1MB vs 1x10MB still results in the 10x being faster). If anyone has any insight on why this is or what I may be doing wrong to get these weird numbers, I would be very interested to hear what you have to say.</p> <p>Thanks!</p> <p>P.S. I know that cudaMemcpy carries with it an implicit synchronization and so I could have used a CPU timer and that cudaEventSynchronize is redundant, but I figured it was better to be on the safe side</p> <p>UPDATE: I wrote a function to try and take advantage of this apparent rip in the performance space-time continuum. When I use that function, though, which is written EXACLTY as in my test cases, the effect goes away and I see what I expect (a single cudaMemcpy is fastest). Perhaps this is all more akin to quantum physics than relativity wherein the act of observing changes the behavior...</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload