Pages

Wednesday, December 24, 2025

Saving using the ZLIB algorithm

ZLIB was introduced as an improved save compression algorithm as part of IBM i 7.4. With IBM Power10 processors the algorithm uses the on-chip Nest Accelerator (NX) GZIP, therefore, it is faster, less CPU intensive, and produces a smaller save file than other compression routines. I wanted to try it out to see how big a difference it would make.

First thing I need to do is to check if the IBM Power server and model I will be performing my tests upon is at least a Power 10. I can get the model number from the system values, but I cannot get the system type. The most convenient place I know to get that is from the API QLZARCAPI. After running the following:

01  CALL QSYS/QLZARCAPI

And I look in the job log I can see information on the first line of data:

SYSTEM INFO -> SYSTEM SERIAL NUMBER: XX-XXXXX  . SYSTEM TYPE-MODEL: 9105-22A.

I admit I do not know all the system types and model numbers, therefore, I googled it and found that I am on an IBM Power S1022.

Which library to use? I wanted one that was large enough for it to take some time to save, that would result in a significant difference between all the compression and compaction types I would be using. I decided upon the library QGPL as it is 1,165,643,776 bytes.

The save command I used was:

   SAVLIB LIB(QGPL) DEV(*SAVF) SAVF(MYLIB/TEMPSAVF) UPDHST(*NO)

Following that would be the data compression and compaction values. I used the following:

  • DTACPR(*NO) COMPACT(*NO)
  • DTACPR(*NO) COMPACT(*DEV)
  • DTACPR(*LOW) COMPACT(*NO)
  • DTACPR(*LOW) COMPACT(*DEV)
  • DTACPR(*MEDIUM) COMPACT(*NO)
  • DTACPR(*MEDIUM) COMPACT(*DEV)
  • DTACPR(*HIGH) COMPACT(*NO)
  • DTACPR(*HIGH) COMPACT(*DEV)
  • DTACPR(*ZLIB) COMPACT(*NO)
  • DTACPR(*ZLIB) COMPACT(*DEV)

I was the only human on the partition, and only the regular system jobs were running. I ran each type of save three times. The table below shows the results:

Data compression & compaction Duration (secs) Size
DTACPR(*NO) COMPACT(*NO) 1.740247 1,108,369,408
DTACPR(*NO) COMPACT(*NO) 1.921011 1,108,369,408
DTACPR(*NO) COMPACT(*NO) 1.833295 1,108,369,408
Average 1.831518
 
DTACPR(*NO) COMPACT(*DEV) 1.893641 1,108,369,408
DTACPR(*NO) COMPACT(*DEV) 1.674422 1,108,369,408
DTACPR(*NO) COMPACT(*DEV) 1.750669 1,108,369,408
Average 1.772911
 
DTACPR(*LOW) COMPACT(*NO) 9.507516 970,481,664
DTACPR(*LOW) COMPACT(*NO) 9.707669 970,481,664
DTACPR(*LOW) COMPACT(*NO) 9.898091 970,481,664
Average 9.704425
 
DTACPR(*LOW) COMPACT(*DEV) 9.905191 970,481,664
DTACPR(*LOW) COMPACT(*DEV) 10.092469 970,481,664
DTACPR(*LOW) COMPACT(*DEV) 9.607400 970,481,664
Average 9.868353
 
DTACPR(*MEDIUM) COMPACT(*NO) 89.672719 1,182,294,016
DTACPR(*MEDIUM) COMPACT(*NO) 89.050254 1,182,294,016
DTACPR(*MEDIUM) COMPACT(*NO) 91.256908 1,182,294,016
Average 90.004962
 
DTACPR(*MEDIUM) COMPACT(*DEV) 90.630935 1,182,294,016
DTACPR(*MEDIUM) COMPACT(*DEV) 89.050254 1,182,294,016
DTACPR(*MEDIUM) COMPACT(*DEV) 89.571185 1,182,294,016
Average 89.750791
 
DTACPR(*HIGH) COMPACT(*NO) 111.188026 934,305,792
DTACPR(*HIGH) COMPACT(*NO) 110.655373 934,305,792
DTACPR(*HIGH) COMPACT(*NO) 110.680131 934,305,792
Average 110.841177
 
DTACPR(*HIGH) COMPACT(*DEV) 109.857095 934,305,792
DTACPR(*HIGH) COMPACT(*DEV) 111.348628 934,305,792
DTACPR(*HIGH) COMPACT(*DEV) 109.673412 934,305,792
Average 110.293045
 
DTACPR(*ZLIB) COMPACT(*NO) 13.150133 838,361,088
DTACPR(*ZLIB) COMPACT(*NO) 12.448293 838,361,088
DTACPR(*ZLIB) COMPACT(*NO) 12.586512 838,361,088
Average 12.728313
 
DTACPR(*ZLIB) COMPACT(*DEV) 12.969524 838,361,088
DTACPR(*ZLIB) COMPACT(*DEV) 12.654507 838,361,088
DTACPR(*ZLIB) COMPACT(*DEV) 12.568934 838,361,088
Average 12.730988

While the ZLIB was not the fastest save time (12.73 seconds versus 1.77 for the no compression and compaction), it did generate the smallest save file (838,361,088 bytes), with a save rate of 65,857,116 bytes per second.

I am impressed with the compression of the ZLIB algorithm. I know it takes longer than the no and low compressions. I think the benefits of having a smaller save file to save to tape or to send to another system makes that extra time worthwhile.

What do you think?

There is an improved ZLIB algorithm, zlibNX, that is only available with the IBM Power 11. I cannot wait to try the same tests using that.

This would not be an article from me without some code within it. Let me show how I gathered the results of my saves. First I needed an output file, which I created as a SQL DDL table I called SAVE_TYPES:

01  CREATE TABLE MYLIB.SAVE_TYPES
02  (SAVE_TYPE VARCHAR(50),
03   START_TIMESTAMP TIMESTAMP,
04   END_TIMESTAMP TIMESTAMP,
05   DIFFERENCE DEC(10,6),
06   SIZE DEC(15,0) ;

Line 2: This column will be used to identify what the compression and compaction of this save was.

Lines 3 – 6: The column names explain what data will be contained in these columns.

I used a RPG program to perform the saves. It consists of two procedures, a Main and the one that performs the save. I am going to start by showing the Main procedure:

01  **free
02  ctl-opt main(Main) option(*srcstmt) dftactgrp(*no) ;

03  dcl-pr QCMDEXC extpgm ;
04    *n char(50) const ;
05    *n packed (15 : 5) const ;
06  end-pr ;

07  dcl-proc Main ;
08    QCMDEXC('CRTSAVF MYLIB/TEMPSAVF' : 24.00000) ;

09    DoTheSave('DTACPR(*NO) COMPACT(*NO)') ;
10    DoTheSave('DTACPR(*NO) COMPACT(*DEV)') ;

11    DoTheSave('DTACPR(*LOW) COMPACT(*NO)') ;
12    DoTheSave('DTACPR(*LOW) COMPACT(*DEV)') ;

13    DoTheSave('DTACPR(*MEDIUM) COMPACT(*NO)') ;
14    DoTheSave('DTACPR(*MEDIUM) COMPACT(*DEV)') ;

15    DoTheSave('DTACPR(*HIGH) COMPACT(*NO)') ;
16    DoTheSave('DTACPR(*HIGH) COMPACT(*DEV)') ;

17    DoTheSave('DTACPR(*ZLIB) COMPACT(*NO)') ;
18    DoTheSave('DTACPR(*ZLIB) COMPACT(*DEV)') ;

19    return ;
20  on-exit ;
21    QCMDEXC('DLTF MYLIB/TEMPSAVF' : 19.00000) ;
22  end-proc ;

Line 2: I am using a Main procedure, rather than the RPG cycle. I always like using *SRCSTMT as it makes the compiler use the source line numbers, rather than generate its own sequence numbers. And as I am calling another procedure my program cannot run in the default activation group.

Lines 3 – 6: I am using the QCMDEXC API to execute the save and other related commands. I need to define a procedure prototype for QCMDEXC to define the parameters passed to it.

Line 7: Start of the Main procedure, that ends on line 22.

Line 8: I am using the QCMDEXC API to create the save file I will be using.

Lines 9 – 18: I am calling the subprocedure that will perform the save, DoTheSave, passing to it the data compression and compaction parameters of the save command.

Lines 20 – 21: The ON-EXIT section is performed whether the procedure completes successfully or not. What I am doing here is deleting the save file I created on line 8.

Now for the subprocedure that does the save:

23  dcl-proc DoTheSave ;
24    dcl-pi *n ;
25      CompressionType char(50) const ;
26    end-pi ;

27    dcl-c ConstSave 'SAVLIB LIB(QGPL) DEV(*SAVF) SAVF(MYLIB/TEMPSAVF) UPDHST(*NO) ' ;
28    dcl-s Command char(200) ;
29    dcl-s Start timestamp ;
30    dcl-s End timestamp ;
31    dcl-s Difference packed(10 : 6) ;
32    dcl-s Size packed(15 : 0) ;

33    Command = ConstSave + CompressionType ;

34    Start = %timestamp() ;
35    QCMDEXC(Command : %len(Command)) ;
36    End = %timestamp() ;

37    Difference = %diff(End : Start : *seconds : 6) ;

38    exec sql SELECT OBJSIZE INTO :Size
39               FROM TABLE(QSYS2.OBJECT_STATISTICS('MYLIB','*FILE','TEMPSAVF')) ;

40    exec sql INSERT INTO SAVE_TYPES VALUES(RTRIM(:CompressionType), :Start, :End,
41                                           :Difference, :Size) ;
42  on-exit ;
43    QCMDEXC('CLRSAVF MYLIB/TEMPSAVF' : 24.00000) ;
44  end-proc ;

Lines 24 – 26: I have defined the parameter input to this subprocedure as a constant. This allows me to pass a string to this subprocedure, as I did in the Main procedure.

Line 27: I have defined the first part of the save command as a constant.

Lines 28 – 32: These are the variables that this procedure will use. I think their names explain what I will be using them for.

Line 33: Into the variable Command, I combine the constant part of the save command with compression and setting parameters that was passed to this procedure.

Line 34: Capture the starting timestamp.

Line 35: Execute the command in the variable Command. The second parameter of is the length of the command string. I retrieve that using the Length built in function, %LEN.

Line 36: Capture the ending timestamp.

Line 37: Calculate the difference between the two timestamps, in seconds to six decimal places.

Lines 38 and 39: Retrieve the size of the save file into the variable Size, using the OBJECT_STATISTICS SQL table function.

Lines 40 and 41: Insert the data into the output file. Notice I am using a right trim scalar function, RTRIM, to convert the fixed length compression type string from fixed, as it was passed to this subprocedure, to be a variable length in the output file.

Lines 42 and 43: In this ON-EXIT section I am clearing the save file.

All I did was call the program three time during a day to capture the results I have shown in the table above.

 

This article was written for IBM i 7.6 and 7.5, running on an IBM Power10 or higher.

No comments:

Post a Comment

To prevent "comment spam" all comments are moderated.
Learn about this website's comments policy here.

Some people have reported that they cannot post a comment using certain computers and browsers. If this is you feel free to use the Contact Form to send me the comment and I will post it for you, please include the title of the post so I know which one to post the comment to.