Permlink Replies: 31 - Pages: 3 [ 1 2 3 | Next ] - Last Post: 13 Jan 25, 20:32 Last Post By: MarkusD Threads: [ Previous | Next ]
davidekholm

Posts: 3,603
Registered: 18-Oct-2002
Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 20:19
  Click to reply to this thread Reply
I've discovered a way to speed up JPEG reads 3x and JPEG writes up to 7x. This is possible by integrating the "jpeg-turbo" (https://libjpeg-turbo.org/) native code library into jAlbum. Java's standard JPEG image reader and writer is already using native code, but jpeg-turbo uses special "SIMD" assembly instructions. SIMD stands for "Single Instruction Multiple Data" and is a set of instructions that operates on multiple data sets (https://en.wikipedia.org/wiki/Single_instruction,_multiple_data). It obviously makes a huge difference when it comes to multimedia processing. The jpeg-turbo library has accelerated native code for all modern CPUs, including Intel/AMD 64 and arm CPUs (including Apple's M-series).

To try out this library, install https://jalbum.net/download/imageio-turbojpeg.jar into jAlbum's lib folder and restart jAlbum. You can also get a further speed boost by doing a core update so you get v36.1.2.

The Java integration I've made here currently has one limitation: It doesn't pass along EXIF metadata to generated images.

Apart from being super fast, the jpeg-turbo library is supposed to be good at handling "corner case" JPEGs.

On my Mac (M4 CPU) I get 84% faster builds of "Sample Project" compared with the official v36.1
On Windows, I get 55% faster builds of "Sample Project", but this is when emulating Intel. I'd like to hear what you get on a genuine Intel/AMD Windows machine.

I've also tested this library on Linux (aarch64), but not on Linux (amd64)

What do you think?
RobM

Posts: 3,963
Registered: 4-Aug-2006
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 20:40   in response to: davidekholm in response to: davidekholm
  Click to reply to this thread Reply
M2 Mac, 8MB
jAlbum 36.1 "Sample Project" made in 6.339s
jAlbum 36.1.2 "Sample Project" made in 7.293s
jAlbum 36.1.2 Turbo "Sample Project" made in 5.142s (19% quicker)
JeffTucker

Posts: 8,431
Registered: 31-Jan-2006
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 20:40   in response to: davidekholm in response to: davidekholm
  Click to reply to this thread Reply
davidekholm wrote:
The Java integration I've made here currently has one limitation: It doesn't pass along EXIF metadata to generated images.

For that kind of speed improvement, I'd say that's an acceptable trade-off. Writing EXIF to the generated images has always been a little dodgy.

And I'm a bit of a purist - I say that EXIF should consist only of information the camera writes to an image file when you shoot the photo. It's not the place for other, user-generated content. And since the generated images aren't coming from a camera, EXIF data have no business being in them, anyway.

Will do some time trials, and post back later....
davidekholm

Posts: 3,603
Registered: 18-Oct-2002
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 20:58   in response to: RobM in response to: RobM
  Click to reply to this thread Reply
RobM wrote:
M2 Mac, 8MB
jAlbum 36.1 "Sample Project" made in 6.339s
jAlbum 36.1.2 "Sample Project" made in 7.293s
jAlbum 36.1.2 Turbo "Sample Project" made in 5.142s (19% quicker)

Interesting that you don't get that "radical" differences. I did a 3x "warmup build" on both "before" and "after", but even without any warmup I get a 77% improvement. Can you share your data from Profiler.instance? Here's my data (Sample Project):

Original:
             FileFilters.saveImage:   210 calls      0,644s
             FileFilters.loadImage:    57 calls      0,508s
        HardwareSmoothScaler.scale:   159 calls      0,502s
       FileFilters.unifyColorModel:    57 calls       0,09s
          AlbumBean.makeIndexPages:     4 calls      0,032s
       AlbumBean.registerVariables:    58 calls      0,024s
       Process compiled expression:     7 calls      0,023s
                              init:     1 calls      0,022s
      Executing compiled scriptlet:    63 calls       0,02s
                   Create MediaRSS:     4 calls      0,014s
     AlbumObjectImpl.getProperties:    57 calls      0,011s
     AlbumObjectImpl.getXmpManager:    63 calls       0,01s
   Executing interpreted scriptlet:   368 calls      0,009s
       AlbumObjectImpl.getMetadata:    57 calls      0,009s
        AlbumObjectProperties.load:    13 calls      0,009s
       RecoveryTool.createLifeboat:     1 calls      0,008s
     FileFilters.getBasicImageInfo:    57 calls      0,005s
                AlbumBean.pushVars:   158 calls      0,004s
           Reading file attributes:    55 calls      0,004s
                 AlbumBean.popVars:   157 calls      0,002s
         AlbumBean.countTotalFiles:     1 calls      0,002s
                 predir processing:     4 calls      0,002s
   JAlbumUtilities.countCategories:     8 calls      0,001s
                Process expression:     1 calls      0,001s
                    Copy res files:     1 calls      0,001s
              Compiling scriptlets:    46 calls          0s
     AlbumBean.getFolderProperties:     5 calls          0s
          AlbumBean.processFilters:   264 calls          0s
                     Writing pages:    12 calls          0s
                             Total:  1948 calls      1,957s
 

Turbo:
        HardwareSmoothScaler.scale:   159 calls      0,304s
             FileFilters.saveImage:   210 calls      0,283s
             FileFilters.loadImage:    57 calls      0,142s
       AlbumBean.registerVariables:    58 calls      0,035s
          AlbumBean.makeIndexPages:     4 calls      0,031s
       Process compiled expression:     7 calls      0,029s
                              init:     1 calls      0,027s
      Executing compiled scriptlet:    63 calls      0,018s
                   Create MediaRSS:     4 calls      0,013s
        AlbumObjectProperties.load:    13 calls       0,01s
     AlbumObjectImpl.getProperties:    57 calls       0,01s
     AlbumObjectImpl.getXmpManager:    53 calls      0,008s
   Executing interpreted scriptlet:   368 calls      0,008s
       AlbumObjectImpl.getMetadata:    57 calls      0,008s
       RecoveryTool.createLifeboat:     1 calls      0,007s
                AlbumBean.pushVars:   158 calls      0,006s
                 AlbumBean.popVars:   157 calls      0,005s
     FileFilters.getBasicImageInfo:    57 calls      0,005s
                 predir processing:     4 calls      0,004s
           Reading file attributes:    55 calls      0,003s
                Process expression:     1 calls      0,002s
         AlbumBean.countTotalFiles:     1 calls      0,002s
   JAlbumUtilities.countCategories:     8 calls      0,001s
              Compiling scriptlets:    46 calls      0,001s
     AlbumBean.getFolderProperties:     5 calls          0s
                    Copy res files:     1 calls          0s
          AlbumBean.processFilters:   264 calls          0s
                     Writing pages:    12 calls          0s
                             Total:  1881 calls      0,962s
 
JeffTucker

Posts: 8,431
Registered: 31-Jan-2006
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 21:10   in response to: JeffTucker in response to: JeffTucker
  Click to reply to this thread Reply
Oh, this is definitely a "keeper." In fact, I'd call it jAlbum 37. :)

Just don't forget to get rid of the option to write EXIF to the output images.

I fed a project 100 recent JPG's from my iPhone 14. My PC is aging, but it's got an Intel i7 8700 3.20GHz chip, 16GB RAM, and an SSD drive.

Three "start from no output" trials. Before: 11.8s. After: 5.2s. That cuts 56% off the build time.

ETA: That also indicates that your Intel emulator is pretty reliable!
davidekholm

Posts: 3,603
Registered: 18-Oct-2002
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 21:30   in response to: JeffTucker in response to: JeffTucker
  Click to reply to this thread Reply
Great!

Short album build tests may be biased negatively as there is a short load time for the library itself. You can preload the native code library by issuing:
org.libjpegturbo.turbojpeg.TJLoader.load();
from the system console.
JeffTucker

Posts: 8,431
Registered: 31-Jan-2006
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 21:39   in response to: JeffTucker in response to: JeffTucker
  Click to reply to this thread Reply
On my M2 Mac, with the same 100 JPG's, the new version shaves 39% off the build time.
JeffTucker

Posts: 8,431
Registered: 31-Jan-2006
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 21:42   in response to: davidekholm in response to: davidekholm
  Click to reply to this thread Reply
davidekholm wrote:
You can preload the native code library by issuing:
org.libjpegturbo.turbojpeg.TJLoader.load();
from the system console.

On the PC, that doesn't yield any improvement. On the Mac, it results in a 45% faster build, rather than 39%.
RobM

Posts: 3,963
Registered: 4-Aug-2006
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 22:04   in response to: davidekholm in response to: davidekholm
  Click to reply to this thread Reply
All single clean builds of the album, used two skins Missive and Plain.
Missive skin times 8.229s v 5.225s (36.5% gain)
Plain skin times 7.482s v 4.218s (43% gain)

36.1.1 No Turbo
(Missive skin)
"Sample Project" made in 8.229s
0.028s ==>
                            FileFilters.loadImage:    96 calls      2.497s
                             FileFilters.saveImage:    81 calls      2.131s
                        HardwareSmoothScaler.scale:    77 calls      1.218s
                          AlbumBean.makeIndexPages:     4 calls       0.85s
                                              init:     1 calls       0.74s
                                  Compiling script:     4 calls      0.531s
                              Compiling scriptlets:    51 calls      0.519s
                      Executing compiled scriptlet:   627 calls      0.341s
                       Process compiled expression:    10 calls      0.302s
                       FileFilters.unifyColorModel:    96 calls      0.183s
                       AlbumBean.registerVariables:   116 calls      0.103s
                                   Create MediaRSS:     4 calls      0.063s
                   Executing interpreted scriptlet:   382 calls       0.05s
                     AlbumObjectImpl.getXmpManager:    79 calls      0.048s
                       AlbumObjectImpl.getMetadata:    71 calls      0.044s
                        AlbumObjectProperties.load:    37 calls      0.036s
                                    Copy res files:     1 calls      0.035s
                     AlbumObjectImpl.getProperties:    82 calls      0.035s
                                 predir processing:     4 calls      0.031s
                          BetterMediumScaler.scale:    10 calls      0.021s
                     FileFilters.getBasicImageInfo:    70 calls       0.02s
                       RecoveryTool.createLifeboat:     1 calls      0.019s
                           Reading file attributes:    88 calls      0.015s
                                     Writing pages:    12 calls       0.01s
                                Process expression:     1 calls      0.006s
                   JAlbumUtilities.countCategories:    15 calls      0.004s
                                 AlbumBean.popVars:   322 calls      0.003s
                         AlbumBean.countTotalFiles:     1 calls      0.003s
                                AlbumBean.pushVars:   323 calls      0.002s
                     AlbumBean.getFolderProperties:    13 calls      0.001s
                               Emptying res folder:     1 calls          0s
                          AlbumBean.processFilters:   178 calls          0s
                                             Total:  2858 calls      9.861s


(Plain skin)
"Sample Project" made in 7.482s
0.002s ==>
                             FileFilters.saveImage:   218 calls      3.767s
                             FileFilters.loadImage:    75 calls      1.995s
                        HardwareSmoothScaler.scale:   165 calls      1.239s
                                              init:     1 calls      0.758s
                       Process compiled expression:     7 calls      0.399s
                                  Compiling script:     4 calls      0.368s
                      Executing compiled scriptlet:    63 calls      0.198s
                          AlbumBean.makeIndexPages:     4 calls       0.18s
                       FileFilters.unifyColorModel:    75 calls      0.165s
                              Compiling scriptlets:    46 calls      0.061s
                       AlbumBean.registerVariables:    60 calls       0.05s
                                   Create MediaRSS:     4 calls      0.032s
                   Executing interpreted scriptlet:   382 calls      0.022s
                        AlbumObjectProperties.load:    27 calls       0.02s
                     AlbumObjectImpl.getProperties:    70 calls      0.018s
                                AlbumBean.pushVars:   162 calls      0.015s
                                 predir processing:     4 calls      0.015s
                     AlbumObjectImpl.getXmpManager:    68 calls      0.014s
                       AlbumObjectImpl.getMetadata:    65 calls      0.013s
                       RecoveryTool.createLifeboat:     1 calls      0.012s
                          BetterMediumScaler.scale:     5 calls      0.011s
                                 AlbumBean.popVars:   161 calls      0.009s
                     FileFilters.getBasicImageInfo:    64 calls      0.009s
                           Reading file attributes:    69 calls      0.007s
                                    Copy res files:     1 calls      0.006s
                                Process expression:     1 calls      0.003s
                         AlbumBean.countTotalFiles:     1 calls      0.003s
                                     Writing pages:    12 calls      0.003s
                          AlbumBean.processFilters:   274 calls      0.002s
                   JAlbumUtilities.countCategories:     8 calls      0.001s
                     AlbumBean.getFolderProperties:     9 calls          0s
                               Emptying res folder:     1 calls          0s
                                             Total:  2107 calls      9.395s


36.1.2 Turbo
(Missive Skin)
"Sample Project" made in 5.225s
0.002s ==>
HardwareSmoothScaler.scale:    77 calls      1.308s
                                              init:     1 calls      0.919s
                             FileFilters.loadImage:    96 calls      0.818s
                          AlbumBean.makeIndexPages:     4 calls      0.733s
                                  Compiling script:     4 calls      0.663s
                              Compiling scriptlets:    58 calls      0.545s
                       Process compiled expression:    10 calls      0.366s
                      Executing compiled scriptlet:   991 calls       0.34s
                             FileFilters.saveImage:    81 calls      0.306s
                       AlbumBean.registerVariables:   116 calls      0.152s
                               AlbumBean.makeSlide:    52 calls      0.135s
                                   Create MediaRSS:     4 calls      0.073s
                   Executing interpreted scriptlet:   382 calls      0.059s
                     FileFilters.getBasicImageInfo:    70 calls      0.056s
                     AlbumObjectImpl.getXmpManager:    79 calls      0.048s
                                 predir processing:     4 calls      0.047s
                       AlbumObjectImpl.getMetadata:    71 calls      0.044s
                                    Copy res files:     1 calls      0.038s
                        AlbumObjectProperties.load:    37 calls      0.033s
                     AlbumObjectImpl.getProperties:    82 calls      0.032s
                          BetterMediumScaler.scale:    10 calls      0.025s
                                     Writing pages:    64 calls      0.019s
                       RecoveryTool.createLifeboat:     1 calls      0.016s
                           Reading file attributes:    87 calls      0.013s
                                Process expression:     1 calls      0.006s
                                 AlbumBean.popVars:   530 calls      0.004s
                   JAlbumUtilities.countCategories:    15 calls      0.003s
                         AlbumBean.countTotalFiles:     1 calls      0.003s
                                AlbumBean.pushVars:   531 calls      0.003s
                     AlbumBean.getFolderProperties:    13 calls      0.001s
                          AlbumBean.processFilters:   178 calls          0s
                                             Total:  3651 calls      6.808s


(Plain skin)
"Sample Project" made in 4.218s
0.002s ==>
                        HardwareSmoothScaler.scale:   165 calls        1.2s
                             FileFilters.saveImage:   218 calls       0.73s
                                              init:     1 calls      0.679s
                             FileFilters.loadImage:    75 calls      0.565s
                                  Compiling script:     4 calls      0.411s
                       Process compiled expression:     7 calls      0.278s
                              Compiling scriptlets:    46 calls      0.185s
                          AlbumBean.makeIndexPages:     4 calls       0.17s
                      Executing compiled scriptlet:    63 calls      0.146s
                       AlbumBean.registerVariables:    60 calls      0.059s
                                   Create MediaRSS:     4 calls      0.032s
                   Executing interpreted scriptlet:   382 calls      0.023s
                        AlbumObjectProperties.load:    27 calls      0.019s
                     AlbumObjectImpl.getProperties:    70 calls      0.018s
                                 predir processing:     4 calls      0.016s
                                AlbumBean.pushVars:   162 calls      0.014s
                     FileFilters.getBasicImageInfo:    64 calls      0.013s
                     AlbumObjectImpl.getXmpManager:    68 calls      0.012s
                       RecoveryTool.createLifeboat:     1 calls      0.012s
                       AlbumObjectImpl.getMetadata:    65 calls      0.011s
                          BetterMediumScaler.scale:     5 calls      0.009s
                                 AlbumBean.popVars:   161 calls      0.007s
                                    Copy res files:     1 calls      0.007s
                           Reading file attributes:    69 calls      0.007s
                                Process expression:     1 calls      0.004s
                         AlbumBean.countTotalFiles:     1 calls      0.002s
                                     Writing pages:    12 calls      0.002s
                   JAlbumUtilities.countCategories:     8 calls      0.001s
                          AlbumBean.processFilters:   274 calls      0.001s
                     AlbumBean.getFolderProperties:     9 calls          0s
                               Emptying res folder:     1 calls          0s
                                             Total:  2032 calls      4.633s
RobM

Posts: 3,963
Registered: 4-Aug-2006
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 22:14   in response to: davidekholm in response to: davidekholm
  Click to reply to this thread Reply
davidekholm wrote:
Great!

Short album build tests may be biased negatively as there is a short load time for the library itself. You can preload the native code library by issuing:

org.libjpegturbo.turbojpeg.TJLoader.load();
from the system console.
Preloading for me, using Plain skin goes from 4.218s to 3.39s (19.6% gain)
MarkusD

Posts: 682
Registered: 13-Apr-2006
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 22:55   in response to: davidekholm in response to: davidekholm
  Click to reply to this thread Reply
What do you think?
David, bei allem Respekt, aber das wird im Alltag kaum einen Unterschied machen, ob das Erzeugen einer JPG-Datei nun 20%, 30% oder 50% schneller wird. Warum? Weil ich nur extrem selten neue Dateien in meinem Projekt habe wenn ich es compiliere. In 9 von 10 Fällen compiliere ich ein Projekt neu, weil ich Kommentare, Titel oder Schlüsselworte verändert habe. Und dieser Prozess dauert bei einem Real-World-Projekt weit über 10 Minuten.



Und ich frage mich bis heute, was macht JA in dieser Zeit? Das ist immer wieder sehr frustrierend.

Was ich sehr häufig während des Compilierens sehe ist die Meldung, dass Metadaten gelesen werden. Aber wieso werden Metadaten von fast 12.000 Objekten (Bildern und Video) gelesen, wenn keine einzige Datei davon neu ist (alle diese Infos stehen doch in den .info-Dateien)?

Auch bei PNG-Dateien werden Metadaten gelesen, so viel ich weiß haben PNG-Dateien überhaupt keine Metadaten. Alleine das Compilieren einiger tausend PNG-Dateien dauert eine halbe Ewigkeit.

Wenn du wirklich die Performance von JA steigern willst, dann musst du meiner Meinung nach an völlig anderen Prozessen optimieren.

Bitte verstehe das alles als sehr wohlmeinende positive Kritik.

Bitte entschuldige den Text auf Deutsch.

Gruß, Markus
JeffTucker

Posts: 8,431
Registered: 31-Jan-2006
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 23:05   in response to: MarkusD in response to: MarkusD
  Click to reply to this thread Reply
David will correct me if I'm wrong, but I believe that when the message refers to reading metadata, that doesn't mean just embedded metadata, like EXIF and XMP. It also means all the other ancillary data you have entered for images and folders - titles, comments, keywords, etc.

I suspect that if you had projects that had only brief, concise titles, you would not see the message about reading metadata. My family album has over 6000 objects (mostly JPG's, but a lot of videos, as well), in about 50 folders, and I have never seen a message about "reading metadata." If it happens, it flickers by so quickly, there isn't even time to read it.
davidekholm

Posts: 3,603
Registered: 18-Oct-2002
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 23:30   in response to: MarkusD in response to: MarkusD
  Click to reply to this thread Reply
Markus, I understand your point of view. Usage varies between users and in your case, you'd benefit from having metadata reading optimized. Can you please open the system console after that long album build and issue Profiler.instance, then copy and paste the result here.
davidekholm

Posts: 3,603
Registered: 18-Oct-2002
Re: Faster image reading and writing with jpeg-turbo
Posted: 7 Jan 25, 23:32   in response to: JeffTucker in response to: JeffTucker
  Click to reply to this thread Reply
JeffTucker wrote:
David will correct me if I'm wrong, but I believe that when the message refers to reading metadata, that doesn't mean just embedded metadata, like EXIF and XMP. It also means all the other ancillary data you have entered for images and folders - titles, comments, keywords, etc.

I suspect that if you had projects that had only brief, concise titles, you would not see the message about reading metadata. My family album has over 6000 objects (mostly JPG's, but a lot of videos, as well), in about 50 folders, and I have never seen a message about "reading metadata." If it happens, it flickers by so quickly, there isn't even time to read it.


I believe it refers to all metadata, but the VAST majority of time is done unpacking EXIF and xmp metadata from the large images. I guess a good improvement would be to detect if an image hasn't been touched and instead grab the metadata from the already generated .json files... That would speed up these kinds of album updates significantly
JeffTucker

Posts: 8,431
Registered: 31-Jan-2006
Re: Faster image reading and writing with jpeg-turbo
Posted: 8 Jan 25, 01:07   in response to: davidekholm in response to: davidekholm
  Click to reply to this thread Reply
That still leaves me head-scratching a bit about why I've never noticed this message. Could it be because I'm not using any JSON-based skins? Does generating those files account for the overhead?

ETA: Just as a point of comparison, if I add a few images to my big family album and hit Make Album (letting it process all subdirectories), the process finishes in well under one minute.
Legend
Forum admins
Helpful Answer
Correct Answer

Point your RSS reader here for a feed of the latest messages in all forums