7
$\begingroup$

I am having an issue with image processing involving CUDALink.

Needs["CUDALink`"]
CUDAQ[]

images = FileNames["*.jpg", "C:\\Users\\rwheatley\\Desktop\\All Folders\\Pics\\"];
count = 1;
Do[imagevar[count++] = Import[image], {image, images}];
Length[images]

start = SessionTime[];
For[i = 1, i < Length[images], i++, CUDAImageConvolve[imagevar[i], {{-1, -2, 3}}]]
CUDATime = SessionTime[] - start;

start1 = SessionTime[];
For[i = 1, i < Length[images], i++, ImageConvolve[imagevar[i], {{-1, -2, 3}}]]
Regular = SessionTime[] - start1;

CUDATime
Regular

With 34 iPhone6+ HDR photos, this always has CUDA taking longer by a factor of 4. Does anyone see what is wrong with my code?

In[43]:= CUDAInformation[]

Out[43]= {1 -> {"Name" -> "NVS 5200M", "Clock Rate" -> 1344000, 
  "Compute Capabilities" -> 2.1, "GPU Overlap" -> 1, 
  "Maximum Block Dimensions" -> {1024, 1024, 64}, 
  "Maximum Grid Dimensions" -> {65535, 65535, 65535}, 
  "Maximum Threads Per Block" -> 1024, "Maximum Shared Memory Per Block" -> 49152, 
  "Total Constant Memory" -> 65536, "Warp Size" -> 32, "Maximum Pitch" -> 2147483647, 
  "Maximum Registers Per Block" -> 32768, "Texture Alignment" -> 512, 
  "Multiprocessor Count" -> 2, "Core Count" -> 64, "Execution Timeout" -> 1, 
  "Integrated" -> False, "Can Map Host Memory" -> True, "Compute Mode" -> "Default", 
  "Texture1D Width" -> 65536, "Texture2D Width" -> 65536, "Texture2D Height" -> 65535, 
  "Texture3D Width" -> 2048, "Texture3D Height" -> 2048, "Texture3D Depth" -> 2048, 
  "Texture2D Array Width" -> 16384, "Texture2D Array Height" -> 16384,  
  "Texture2D Array Slices" -> 2048, "Surface Alignment" -> 512, 
  "Concurrent Kernels" -> True, "ECC Enabled" -> False, "TCC Enabled" -> False, 
  "Total Memory" -> 1073741824}}

Thanks to @MarcoB I have modified my code to be more Mathematica-Like as follows: ClearAll["Global*"] Needs["CUDALink"] CUDAQ[]

images = FileNames["*.jpg", "C:\\Users\\rwheatley\\Desktop\\All Folders\\Pics\\"];
imagevar = Import /@ images;

Clear[images]
MemoryInUse[]

CUDAImageConvolve[#, {{-1, -2, 3}}] & /@ imagevar; // AbsoluteTiming
ImageConvolve[#, {{-1, -2, 3}}] & /@ imagevar;  // AbsoluteTiming

The issue still remains for CUDA being slower, though. I have even tried the following to no avail:

mem = CUDAMemoryLoad[#] & /@ imagevar;

CUDAImageConvolve[#, {{-1, -2, 3}}] & /@ mem; // AbsoluteTiming
$\endgroup$
  • $\begingroup$ Hello, I was just about to modify my post but I saw your comment. I can get CUDAMemoryLoad and CUDAMemoryAllocate to work on one image but now I am having problems with how to do that with lists. CUDAMemoryLoad@ImageData[image]; CUDAMemoryAllocate[Real, Dimensions@ImageData[image]]; CUDAImageConvolve[image, {{-1, -2, 3}}] Question is how to do that with lists as MarcoB suggested. I have tried modifying the code $\endgroup$ – Richard Feb 14 '17 at 18:29
  • $\begingroup$ @Richard Reading the CUDALink manual seems to indicate that the memory management happens automatically behind the scenes, i.e. the data is moved from host to device memory automatically, and back. See this section of the Memory CUDALink tutorial. In fact, the tutorial states that "Using the memory manager is an optimization technique. As a rule of thumb, the user should get the CUDA program to work before dabbling with memory." Unfortunately, though, I can't test CUDA stuff directly for lack of hardware. $\endgroup$ – MarcoB Feb 14 '17 at 18:48
  • $\begingroup$ @andre Since I am using imagevar as a list of images now, I changed the code to: images = FileNames["*.jpg", "C:\\Users\\rwheatley\\Desktop\\All Folders\\Pics\\"]; imagevar = Import /@ images; Union[ImageType /@ imagevar] Union[ImageColorSpace /@ imagevar] Union[ImageDimensions /@ imagevar] {"Byte"} {"RGB"} {{369, 100}, {381, 325}, {390, 231}, {400, 400}, {620, 620}, {678, 592}, {850, 540}, {1200, 1199}, {1836, 3264}, {2448, 3264}, {3264, 2448}} $\endgroup$ – Richard Feb 15 '17 at 13:31
  • $\begingroup$ @andre, thank you for assisting me, btw. $\endgroup$ – Richard Feb 15 '17 at 13:34
7
$\begingroup$

CUDAImageConvolve is slow due to memory tranfert between the different memories (CPU and GPU). One can decompose the action of CUDAImageConvolve in 6 phases and get de timing of each phase.

Here are the results, compared with the CPU ImageConvolve and the straight CUDAImageConvolve :

enter image description here

One can see that the convolution alone takes only 16 mS.

This information can be usefull if you have a sequence of operations to do without leaving the GPU memory.

Here is the code :

Needs["CUDALink`"]
CUDAQ[]

imagevar = ExampleData /@ {{"TestImage","House2"},
{"TestImage","Lena"},{"TestImage","Mandrill"}}


AbsoluteTiming[ImageConvolve[#, {{-1, -2, 3}}]& /@ imagevar]
AbsoluteTiming[CUDAImageConvolve[#, {{-1, -2, 3}}]& /@ imagevar]

enter image description here

Initialization :

AbsoluteTiming[memList=CUDAMemoryLoad[ImageData[#]]& /@ imagevar;]
AbsoluteTiming[CUDAMemoryCopyToDevice /@ memList;]
(*CUDAMemoryInformation[memList[[1]]]*)
AbsoluteTiming[outMemList=CUDAMemoryAllocate["Double",Append[ImageDimensions[#],3]]& /@ imagevar;]

{0.0467458, Null}
{0.0143745, Null}
{0.000671206, Null}

convolution :

AbsoluteTiming[(CUDAImageConvolve[#[[1]], {{-1, -2, 3}},"OutputMemory"-> #[[2]]])& /@ Transpose[{memList,outMemList}];]  

{0.0151647, Null}

restitution :

AbsoluteTiming[Image[CUDAMemoryGet[#]]& /@ outMemList]
AbsoluteTiming[CUDAMemoryUnload /@ Flatten[{memList,outMemList}];]

enter image description here

NOTE :

CUDAMemoryLoad[ImageData[#]]& /@ imagevaris used instead of CUDAMemoryLoad /@ imagevar because CUDAMemoryLoad[image] create a 2D array of {Byte,Byte,Byte} even if the image is a 2D array of {Float,Float,Float}. The consequence is that the convolution creates a overflow on the Bytes, and the resulting image is then :

enter image description here

$\endgroup$
  • $\begingroup$ Very nice analysis! $\endgroup$ – MarcoB Feb 16 '17 at 4:47
  • $\begingroup$ Thank you both for your help! I also only have a 1GB NVIDIA card. I am going to start working on smaller subsets and possibly divide up the tasks so that I am not loading all images to the GPU at the same time. $\endgroup$ – Richard Feb 16 '17 at 22:02
7
$\begingroup$

This is an extended comment on code style, rather than on your problem. Unfortunately I don't have a CUDA-capable card, so I can't test your actual issue.

Generally speaking, however, you will want to try and get away from procedural loops when writing Mathematica code:

For instance in your code, instead of:

Do[imagevar[count++] = Import[image], {image, images}];

you could use:

imagevar = Import/@images;

Note that this makes imagevar a list, rather than an indexed variable, but that will actually be helpful in this case, since you can carry out "vector" operations on lists quickly and cleanly. See e.g. Map, which is used above in its "shortcut" (infix) form /@.

Similarly, now that imagevar is a list, your code

For[i = 1, i < Length[images], i++, CUDAImageConvolve[imagevar[i], {{-1, -2, 3}}]]

can be converted to the following:

CUDAImageConvolve[#, {{-1, -2, 3}}]& /@ imagevar;

If you want more information on using pure functions (i.e. the # and & expressions), take a look at How to | Work with Pure Functions.

Timing might also be best done using AbsoluteTiming; see this section of the Date and Time Functions Tutorial. For instance, the following will directly return the time it took to carry out one instance of that operation:

AbsoluteTiming[CUDAImageConvolve[#, {{-1, -2, 3}}]& /@ imagevar;]

In this context, you might also be interested in RepeatedTiming for more accurate timing results.

Similarly with the last bit, to give you timing of the non-CUDA operation:

AbsoluteTiming[ImageConvolve[#, {{-1, -2, 3}}]& /@ imagevar;]
$\endgroup$
  • $\begingroup$ MarcoB, thank you for the Mathematica help. I wasn't thinking in terms of how to optimize the Mathematica code but the AbsoluteTiming command helped get a more accurate timing which does help me achieve a better result. I do get slightly better timing when I replace my code with yours but I still get 3-4 times the period when I use CUDA instead of the regular command. $\endgroup$ – Richard Feb 14 '17 at 17:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.