[Looking for Charlie's main web site?]

Could CF image processing be killing your ColdFusion server? Explanation and solutions.

Are you having slow ColdFusion pages and wondering what may be the cause? There can of course be many root causes, but a common one that I'm finding lately as I help people is due to using certain of CF's image processing features, especially resizing such as to create thumbnails after a file is uploaded (or when many files are uploaded).

Such folks may be using the CFIMAGE action="resize" tag, or the imageResize() or ImageScaleToFit() functions to do resizing. (Or they may be also processing images using ImageRotate, ImageShear, or ImageTranslate, though the defaults for those are not problematic like the resize/scale tag/function processing).

The "problem" (if this is the cause of a slow page) is due to a default "interpolation" setting for CFIMAGE resizing, imageResize, and ImageScaletoFit. The default may not perform well at all. The good news is that the value is configurable, and you can test to compare quality/performance of difference values, as will explained below. There are still some other things to consider also. (If you're currently using CFIMAGE to do resizing, jump to the last section of this entry to see an example of code switching from the "slow" approach to the faster one. But really, you ought to read the rest of this entry to understand what's being proposed.)

While I offer all the info here for your consideration, if you need help implementing the solution, or better understanding how to find and resolve these or other problems affecting your CF server performance, see more on my CF server troubleshooting consulting services.

Problem: Default "Interpolation" value is "highestquality"

The culprit in this particular problem is a default value in the resize/scaling tag/functions for the "interpolation" level, which presupposes that you want "highestquality" (that's literally an available value for this setting). That quality seems to come at a significant performance penalty for many (which may vary depending on the type or size of file being processed, and again is multiplied the more files you're processing in a single request.)

Solution: There are alternative values for "interpolation"

So the first "good news" is that there is a range of options one can set for this interpolation value, and the opposite one is "highestperformance". For many people, changing to that value has no negative impact on quality (again, that may depend on the image, its size, what you're using the result for, etc.) but it offers a GREAT improvement in performance. You need to test for yourself.

Implementation 1: Controlling Interpolation in the CFIMAGE tag

So if you use CFIMAGE action="resize", what can you do? Well, the "bad news" is that for CF8 and CF9, the CFIMAGE tag has no means for you to affect the interpolation value. It has no INTERPOLATION attribute.

Some great news is that CF10 adds this attribute (for use specifically with action="resize"), but note that the default is still "highestquality", so you will want to consider changing it. See the docs for CF10 on CFIMAGE for the range of INTERPOLATION attribute values.

If you're on CF 8 or 9, though, you will need to consider changing your code to use the imageResize() function instead, which is discussed in the next section.

(Note that, for now, only the action="rewrite" option for CFIMAGE makes any use of interpolation at all.)

Implementation 2: Controlling Interpolation in the imageResize(), imageScaleToFit(), and other image processing functions

In all three releases (CF 8, 9, and 10), the imageResize() function--and the perhaps less-well-known imageScaleToFit()--all have an available (optional) argument for controlling the interpolation value. It's the 4th arg in both imageResize() and imageScaleToFit(). But in all three releases, the value for each again defaults to "highestquality".

Specifying it as "highestperformance" (or some value between the two) again may have a noticeable effect on improving performance without much loss of image quality. See the docs on these functions (here's a link to the CF9 CFML reference.) Test things for yourself, and report here if it helps.

(As an interesting aside, the docs suggest that while imageRotate(), imageShear(), and imageTranslate() functions also have an optional interploation argument, theirs each defaults to the fastest of the values they offer. One wonders why Adobe chose for resizing and scaling to opt for the opposite values.)

Changing from CFIMAGE will require reading and writing the file a new way

Finally, if you do have to change from using CFIMAGE to the imageResize() function (not required for CF10), you may also need to deal with how to load the image from disk into memory, and write it out, if you're also doing that in the one tag.

CFIMAGE has both a SOURCE and DESTINATION attribute which can point to a file on disk to be read/written. The imageResize() function's does not support that. Instead, its first ("name") argument instead expects to be passed a variable with an image in it, such as is created by other CFML image-processing tags or functions. imageNew() can read an image from disk into memory.

Similarly, the result of calling imageResize() is a manipulation of that image object in memory. The function has no means to write out the result. That can be done with either imageWrite() or another CFIMAGE action="write".

So to change from using the tag-based resize to function-based, you may need to add a line of code to load the image from disk, such as using imageNew(), and one to write the resulting image to disk, such as using imageWrite().

Consider the following tag-based resize code, which reads, resizes, and writes out an image (available in the CFDOCS directory, if you installed that with ColdFusion):

<cfimage source="../cfdocs/images/artgallery/jeff01.jpg" action="resize" width="100" height="100" destination="jeff01_thumbnail.jpg" overwrite="yes">

That could be re-written as either:

<cfset myImage=ImageNew("../cfdocs/images/artgallery/jeff01.jpg")>
<cfset ImageResize(myImage,100,100,"highestperformance")>
<cfset imageWrite(myimage,"jeff02_thumbnail.jpg")>

or (for those who prefer script to tags):

<cfscript>
myImage=ImageNew("../cfdocs/images/artgallery/jeff01.jpg");
ImageResize(myImage,100,100,"highestperformance");
imageWrite(myimage,"jeff02_thumbnail.jpg");
</cfscript>

Again, test things for yourself to determine whether changing to highestperformance results in an image quality you (and your users) can find acceptable. See the docs for a range of alternative options to consider. And if you move up to CF10, note that you can solve the problem with CFIMAGE action="rewrite" by just adding the new INTERPOLATION attribute right onto the tag.

For more on the many (many) new features of CF10, check out my blog entry on the topic.

And again if you need help solving these or such other problems affecting your CF server performance, let me know how I can help, via my CF server troubleshooting consulting services.

Of course I welcome and encourage followup and feedback here on this particular problem. And for solutions to other such knotty CF troubleshooting challenges, see my cf911.com site of CF server troubleshooting resources.

Comments
Folks may be interested in my blog post here - http://www.raymondca... - which provides a table of the interpolation options and data related to the time it took for CF (in this case CF8) to resize an image.
# Posted By Raymond Camden | 5/29/12 7:14 AM
@Ray, great stuff. Thanks for sharing it. I had wondered about doing such a comparison, but just wanted to get the info out, since I'e been pointing out the problem and solutions to so many people, and had one on the Adobe forums just yesterday that finally prompted me to write the entry.

BTW, if folks don't notice it, the fastest in his table, at .031 sec, is more than 50 times faster than the slowest (1.7 secs)! Sadly, I suspect most won't do the math, so it's a real good thing you mentioned the one taking 4-minutes. That should get their attention. :-)

I notice you also (in a comment on that entry) pointed to still another related entry of yours, which may interest folks reading this one:

http://www.raymondca...

That said, I notice that your entry refers to "resizing" but shows only imagescaletofit. There's no mention of CFIMAGE action="resize" or imageresize. If you don't mind, I'll add a comment mentioning cfimage action="resize" and imageresize, so that others may more readily find your blog entry if they go searching for those terms.

I'll also point to my entry here for still more info on changing from the one to the other, and also highlight that 50x improvement which your entry shows.

We can't have enough coverage of this problem (and the good news also that CF10 offers an easier workaround for CFIMAGE). Keep up the great work!
# Posted By charlie arehart | 5/29/12 11:10 AM
My experience using CFIMage on CF9 was not good. We were having issues with the server becoming unresponsive if a single user was uploading multiple images. We tried using a background thread, but anything we tried doing with native image processing was negatively impacting the user experience and the uptime of the production server.

For example, on a 2600x1950 3.5mb JPG image, it took 297ms to read the image, 4,735ms to scale the image to 320x320 ("highestPerformance" = 1,922ms) and the resultant filesize was 56.9k ("highestPerformance" = 72.9k).

I switched to CFX_OpenImage (C++ tag) and it took 15ms to read the image, 469ms to scale the image to 320x320 and the resultant filesize was 20.2k and the quality looked consistent with the CFImage image.

After switching to CFX_OpenImage, we haven't encountered any more timeout issues... even when multiple images were concurrently being resized.

I'd be interested in comparing CF9 to CF10, but I'm guessing that it wouldn't come close to the performance we now have from using the free C++ tag.
http://www.kolumbus....
# Posted By James Moberg | 5/29/12 11:58 AM
James, did you happen to check out the link to Ray's second blog entry that I pointed out in the comment above yours? It addresses what I suspect was your problem. The good news is that it's just a change to the JVM (and so, a JVM bug not a CF one).

That said, I would certainly not recommend someone update to CF10 "just for this one improvement", but if you did try it and it was faster, then the good news is that yes that speed boost would also come with CF10, as it comes on a much more updated JVM than what's in CF9.

If you may be tempted to check it out, please do report back what you find. But I'll understand that you may not be interested in changing code that you have now using the custom tag.

Hey, if either your info on that or mine on the jvm update helps someone, then it will have been worthwhile to have this added discussion here. Thanks for chiming in.
# Posted By Charlie Arehart | 5/29/12 1:23 PM
I was using JVM 1.6.0_17 (newer than Ray's) and it didn't change anything. Concurrent processing of images spiked the CPU regardless.

Do you know if CF10 still chokes on CMYK images? (If fixed, that would be a new feature.) This was another reason we switched to OpenImage as we got tired of informing graphic designers that their images were in the "wrong format." (In addition to CMYK, we are now able to read EPS images & generate JPG thumbnails.)

My client would be interested in upgrading to CF10 if PDF generation was better, but many of the new features weren't compelling enough because we are already doing the same thing using third-party libraries and modules.

I have CF10 + CFX_OpenImage installed on my developer workstation. I'll run the same tests & post the results.
# Posted By James Moberg | 5/29/12 1:36 PM
I wonder if it is possible to change that default behaviour somehow so that cfimage uses low quality interpolation by default, would certainly avoid a lot of issues on shared servers.
# Posted By snake | 5/29/12 2:19 PM
I wasn't able to run the same tests on the same CF9 server because it doesn't have CF10 installed, but I ran the comparison scripts on my CF10-Dev 64bit Windows workstation.

Original image: 2600x1950 3.5mb JPG
Output Image: 320x320 max w/.80 quality

CFImage ImageScaletoFit "highestQuality": 2,255ms
CFImage ImageScaletoFit "highestPerformance": 383ms
OpenImage ImageScaletoFit: 280ms (w/verbose debugging enabled)

CFImage ImageRead (to get dimensions): 202ms
OpenImage: 7ms (w/verbose debugging enabled)

CFImage filesize: 56k
CFImage "highestPerformance" filesize: 73k (poor quality image)
OpenImage filesize: 20k

I wrote a CFLoop to perform the test 10 times & appended the execution times to an array to be averaged... here are those results:
CFImage_Read = 213
OpenImage_Read = 7

CFImage_imageScaleToFit = 2,272
OpenImage_imageScaleToFit = 243

CFImage_imageScaleToFit_Fast = 408 (poor quality image)
OpenImage_imageScaleToFit_usingCommands = 221

I set the output quality to 100% and none of the generation times were affected. Here are the outputted file sizes:
CFImage "highestQuality": 169k
CGimage "highestPerformance": 189k (noticeable degradation)
OpenImage: 122k (47k smaller)

Even though CF10's CFImage may be faster than CF9, I'll still use CFX_OpenImage for the performance gain, smaller file sizes & ability to work with CMYK/EPS images.
# Posted By James Moberg | 5/29/12 2:31 PM
James and Russ, I have sent a note to some Adobe folks in the hopes of getting answers to your questions, since I can offer none for you myself.

BTW, James, I did not assert that CF10 offered any performance gain for CFIMAGE (your last sentence). I said only that CF10 offered the option to control the interpolation differently than had been available in CF 8/9 (albeit, yes, changing that interpolation level in any release has improved performance for man). Still, thanks for offering your details of how there are still some challenges. Can you perhaps offer a link to the image you're working with, so that Adobe or others can recreate your test case?

And Russ, I can see that it is hard for Adobe to "just change the default". That would break backward compatibility for code that intentionally relies on the default being highestquality. But I can see an argument for letting that default be set at the server, application, or jvm config level. There are pros and cons to such configurability, though, so I appreciate the challenge Adobe would have addressing this problem.

Short of that, it's why I am writing this entry, to implore people to consider changing the interpolation value. Despite James' more unfortunate experience with certain kinds of files, I find that by far most people are definitely helped by changing the interpolation, so it seems very much worth trumpeting as an option to consider.
# Posted By charlie arehart | 5/29/12 2:54 PM
Changing the interpolation will make it faster, but it also degrades the quality. We couldn't find the sweet spot between having it execute quickly & generate an image of acceptable quality for the client. We also wanted the end user to be able to see the result so it wasn't something that we wanted to offload & queue in the background.

We didn't have any control over the dimensions of the JPG images that were uploaded. Some images were far larger than what we needed. (The images being uploaded were also resized to be used to generate PDF files.) When multiple users were uploading, it could take 80+ seconds to process each image & the CPU would be pegged at 100%.

If needed, I can provide a large JPG image to test, but we were experiencing really slow times on any images over 3,500 pixels wide taken by many different digital cameras.
# Posted By James Moberg | 5/29/12 4:26 PM
Thanks for the additional details, James, if it may help others.

As for your first paragraph, I acknowledged in the entry that there could be a tradeoff between performance and quality, but I pointed out as well that many folks find there to be none at all, and that the highestperformance (or any of the options between that and highestquality).

Let's leave it that yours is one of those experiences that was worse than most. I hope readers will not be discouraged to still consider everything else that was offered, in case it may well help them, as it has many who I've assisted in my consulting practice when faced with this being the root cause of poor performance.
# Posted By charlie arehart | 5/29/12 4:59 PM
To James and others, I asked Adobe and they said that in fact CF10 did address some improvements related to CMYK image processing:

"we fixed the issue where CMYK images were not working with 64 bit OS and MAC OS"

They also said they would hope to create a blog entry soon (on blogs.coldfusion.com) about it.

But if you or anyone who experienced the issue before might want to test it, perhaps you will find things have improved. If not, then besides reporting that here if you'd like (for the sake of the conversation), please do also then file a bug report at https://bugbase.adob..., so that they can address it.
# Posted By Charlie Arehart | 6/1/12 10:43 AM
Hey, did anyone who had the CMYK color issues (for image resizing) ever identify whether their problems still happen in CF10? Adobe says they addressed it.

I'd mentioned in an earlier comment above that Adobe would be coming out with a blog entry to discuss it. Shilpi did a few days later, at http://www.shilpikha... .

She clarified that the image resize improvements (with respect to CMYK issues) were made with respect to "Mac and 64bit OS".

If anyone has any remaining resize issues due to color, I'm sure they'd want to hear about it.

But again, per the main point of my blog entry here, please do be sure first to try changing the interpolation level, whether in imageresize or (in CF10, forward) with CFIMAGE.
# Posted By Charlie Arehart | 8/8/12 8:48 AM
Thanks so much, this solution works wonderfully. My CF8 install was taking over 2 minutes to resize a simple 70k photo. The imageResize feature took only a couple seconds to do the same thing. Amazing.
# Posted By Bill | 11/20/13 10:37 PM
Wonderful to hear, Bill. Thanks for sharing your experience and kind regards. That sort of feedback helps keep us bloggers motivated to share our experiences. :-)
# Posted By Charlie Arehart | 11/21/13 12:10 AM
Charlie saves the day. Yet again. Thanks so much for this.
# Posted By Kory Gorsky | 3/10/14 8:58 AM
Thanks, Kory. Always happy to try to help, and especially to hear that it HAS helped someone. :-)
# Posted By Charlie Arehart | 3/11/14 12:19 PM
While the interpolation changes have helped me in the past, we just completed upgraded our infrastructure and ran into what I think is a JDK bug. I just wanted to cross reference with you guys:

http://www.bennadel....

I linked this blog post in the comments of the above one.
# Posted By Ben Nadel | 4/30/15 8:13 AM
Very interesting, Ben. Thanks for sharing. It's a bummer to see from the comments on your blog that folks are finding this in both Java 1.7 and 1.8. Will be interesting to hear if it's ever sorted out, whether as a bug in Java, CF, or some library that CF uses.

I have a follow up question to confirm what update you had applied (you mention there you were on 10, and a commenter there, Paul, mentions using 11 but doesn't clarify if he had this specific issue there). You can answer there and readers of this who are interested can take up the conversation there. :-)
# Posted By Charlie Arehart | 4/30/15 11:20 PM
Copyright ©2018 Charlie Arehart
Carehart Logo
BlogCFC was created by Raymond Camden. This blog is running version 5.005.
(Want to validate the html in this page?)

Managed Hosting Services provided by
Managed Dedicated Hosting