Re: [Lcms-user] [EXTERNAL]Re: Multithreading - Transformation and lcms context
An ICC-based CMM for color management
Brought to you by:
mm2
From: Noel C. <NCa...@Pr...> - 2017-08-25 12:46:43
|
Hi Marco, If it helps build confidence, we have for years in our DLL been using a transform caching scheme and multi-threading the actual transforms. We don't manage Little CMS contexts at all (i.e., we use the library functions that don't have the ContextID parameter). Our key function is called GetColorTransform, which grabs a mutex to ensure only one thread can traverse the function at any one time, then A. searches for a cached transform that already exists for the given input arguments and returns its handle immediately if one exists, or B. generates and caches the transform handle, then returns it. We multithread the transforms like crazy. As an example, a 12 core circa 2012 Xeon system transforms all the pixels in an 80 megapixel 16 bit RGBA image in an eighth of a second... 08:36:41.208 Thread 0x1A34: Line 2317: Category 'TraceColorManagement': Level 2: ConvertColorOf() - Dividing the image, height 9610, into 24 parts of 400.4 rows each for multi-threaded color conversion. 08:36:41.333 Thread 0x1A34: Line 2345: Performance Message: Total Time = 121.544 milliseconds, ConvertColorOf() - Completed color conversion using 24 simultaneous threads. Image: 8398 x 9610 pixels x 16 bits per color. That's something like 660 megapixels per second. :-) Given our application, relatively few transforms actually get created and so the caching is incredibly effective at keeping performance up. Oh, and we don't replace the default memory manager. -Noel From: Marco Freudenberger [mailto:Mar...@en...] Sent: Fri, August 25, 2017 6:27 AM To: Martí Maria; lcm...@li... Subject: Re: [Lcms-user] [EXTERNAL]Re: Multithreading - Transformation and lcms context Hi Marti! Thanks a lot for your answer. I was under the impression by reading the documentation and previous post here, that besides the plug-in and memory-management configuration (Im not using any of them right now), the context had something to do with concurrent access to global internal data structures of littlecms when working with multiple threads. If not, thats actually great and I just can use my transformation across multiple threads without recreating it. Sounds like its no problem if I just cache the transformation itself. Thanks a lot for your time answering this question, appreciate it! Marco Von: Martí Maria [mailto:mar...@li...] Gesendet: Freitag, 25. August 2017 05:13 An: Marco Freudenberger; lcm...@li... Betreff: [EXTERNAL]Re: [Lcms-user] Multithreading - Transformation and lcms context Hi Marco, Keep in mind cmsContext are only a way to store the plug-in configuration you are using. So, if you provide your own memory management routines, you may want to create a context at the very begin of your program to let lcms use your memory manager instead of the default malloc functions. contexts also stores some global settings. But that's all, contexts are not critical to threads. Deleting the context would just unregister your memory manager, or other plug-ins, if any. On the other hand, cmsDoTransform and specially cmsDoTransformLineStride are re-entrant and can be used on different threads. You can create the transform in the base thread and apply the transform in many other different threads. The only requirement is memory should be accessible. If you provide your memory manager, the context is the way you channel the malloc and free calls to your memory manager. You could just create the transform and keep it open while needed. That's what I do in the color translator and works fine. There is no need to convert to a devicelink. Now for your questions: > 1. Does that mean I could actually just cache the transformation (cmsHTRANSFORM) and re-use it, although the lcms context originally used to load the profiles the transformation was deleted? Can I delete the transformation from a different thread it was created from? If you provide you own memory management functions, then creates the transform, then unregister you memory manager, further cmsDeleteTranform() would call default free(), is likely something will fail. I would just create the context at the begin of program and keep it open all the time to make sure your memory manager is being consistently called. Again, this is just to keep track of the plug-ins you are using. And yes, you can create the transform and reuse it. Even with different buffer formats in some cases. > 2. Alternatively, if that doesnt work, could I just cache the transformation together with the lcmsContext it was created in and use it from different threads concurrently on different images? And if so, could multiple DIFFERENT transformations, which might be applied concurrently, share the same context (assuming they are not CREATED concurrently) or would I need a separate context per transformation? Of course yes, many different transformations can share the same context. That is how it is supposed to work: you create one and only one context, put there all your plug--ins and operates in this context all the time. The plug-in API documentation would give you more details on contexts and plugins. Hope that helps Marti On 8/25/2017 10:34 AM, Marco Freudenberger wrote: All, Ive got a multithreading / lcms context related question on lcms2. Im currently using lcms 2.7, but certainly can upgrade to latest release, if that makes any difference. I tried to find the answer to this somewhere in the documentation, but couldnt get a definite answer. For a project, I need to do color space conversion (from RGB -> CMYK, not that it matters) in a dll. The CMYK profiles are mostly large table based device profiles, so creating the transformation takes a while (depending on the image size, most often longer than applying it). The code might be called thousands of times in an hour. As all the transformations are likely from the same one or two RGB profile(s) to the same (or a little number of different) CMYK profile(s), I would like to cache transformations. The problem is, that the dll calls might be called from different threads and might be called concurrently. For each of the calls, in the original implementation I was: - creating a lcms context - load the profiles - create the transformation - close the profiles - apply the transformation to the image (potentially in multiple threads) - delete the transformation - delete the lcms context. As I said, I would like to cache a small number of transformations. As I wasnt sure about the multithreading (transformation potentially used in different threads) and concurrency (code might be called multiple times) aspects, my first implementation is trying to play save going through a device link: - create a lcms context - check if transformation from IN->OUT is in chache. if not: load profiles, create transformation, close profiles, CREATE A DEVICELINK FROM TRANSFORMATION, SAVE DEVICELINK IN MEMORY (CACHE) - if it is in cache: load transformation from devicelink in cache - apply the transformation to the image (potentially in multiple threads) - delete the transformation (not the devicelink). - delete the lcms context. - if cache item needs to be cleared, delete the devicelink buffer. That process as two downsides: (a) loading a transformation from devicelink profile is much faster than creating it from the in and out profile, but still takes quite some time. (b) the results are slightly different (not as accurate?) when loading the transformation from devicelink. Im getting some CMYK value differences up to ~2 or 3 values per channel on 8 bit images. What Im wondering is: Once Ive got a handle to a transformation, I can apply it to one image in multiple threads (although right now Im doing that while the lcmsContext still exists). Also, to apply it, I dont need to pass in a lcms context to cmsDoTransform (at least not explicitly maybe a handle is stored in the transformation). So my questions: 1. Does that mean I could actually just cache the transformation (cmsHTRANSFORM) and re-use it, although the lcms context originally used to load the profiles the transformation was deleted? Can I delete the transformation from a different thread it was created from? 2. Alternatively, if that doesnt work, could I just cache the transformation together with the lcmsContext it was created in and use it from different threads concurrently on different images? And if so, could multiple DIFFERENT transformations, which might be applied concurrently, share the same context (assuming they are not CREATED concurrently) or would I need a separate context per transformation? Thanks for any useful input Marco ------------------------------------------------------------------------ ------ Check out the vibrant tech community on one of the world's most engaging tech sites, Slashdot.org! http://sdm.link/slashdot _______________________________________________ Lcms-user mailing list Lcm...@li... https://lists.sourceforge.net/lists/listinfo/lcms-user |