On 03/21/2013 07:25 PM, Laurent Bourgès wrote:
Peter,

My last idea for today:
Consider the object field of the LevelEnum as a mutable field (non final), JavaLogger initialization can then set its value during its initialization (j.u.l available).

Doing so, it will avoid any Map.get() in isLoggable(int) to improve performance as done in my patch.

Good idea. And it's thread-safe too I think, since the static initializer HB any instance methods can run and the field is only accessed from JavaLogger's instance methods....

Here's the new rev:

http://dl.dropbox.com/u/101777488/jdk8-tl/PlatformLogger/webrev.05/index.html

I removed the object() and forObject() methods and access the field directly. I also renamed it to something more significant. forObject() is not necessary, since it only had one call site and I just inlined it. I also "took the opportunity". Now we're back to original + 9 LOC ...

Regards, Peter


I will test this solution soon...

My previous message I forgot to send to the mailing list:

my last two cents:
- I prefer having theobject()and forObject() methods in the JavaLogger class because it reduces the risk to use it elsewhere and it avoids double indirection: JavaLogger calls these methods that call back JavaLogger to access its fields ... too obfuscated for me
+
+        // the following two methods are (and should only be) called from 
JavaLogger...
+
+        Object object() {

+            return JavaLogger.levelObjects.get(this);
+        }
+
+        static LevelEnum forObject(Object levelObject) {

+            LevelEnum levelEnum = JavaLogger.levelEnums.get(levelObject);
+            return levelEnum == null ? UNKNOWN : levelEnum;
+        }

+    }
- take the opportunity to replace repeated lines in LoggerProxy.doLog() methods 
by isLoggable(level):
             if (level < levelValue || levelValue == OFF) {


                 return;
             }
Great job anyway,
Cheers,
Laurent



2013/3/21 Peter Levart <peter.lev...@gmail.com <mailto:peter.lev...@gmail.com>>

    Hi Laurent, Mandy,

    I think I've got it now. It's a middle-ground. It would be a shame
    to loose the code-reuse (one switch instead of two). So I only
    moved the levelObjects to JavaLogger, not the entire LevelEnum:

    
http://dl.dropbox.com/u/101777488/jdk8-tl/PlatformLogger/webrev.04/index.html

    So this is basically similar to original code. It just uses
    LevelEnum instead of java.lang.Integer and EnumMap instead of
    HashMap. Hopefully these perform better. If I moved the LevelEnum
    into the JavaLogger, I would have to re-instate the second switch
    for LoggerProxy and the solution would not be much simpler than
    your patch, Laurent. This is still just 20 LOC more than original
    code.

    Regards, Peter


    On 03/21/2013 05:09 PM, Peter Levart wrote:
    You're right, loggingEnabled can change afterwards. I'll correct
    the code as suggested...

    Peter

    On 03/21/2013 04:57 PM, Laurent Bourgès wrote:
    Peter,

    I am convinced it is better if the LevelEnum belongs to
    JavaLogger class (encapsulated so defered initialization).

    Here is a typical scenario:
    1 - PlatformLogger initialized (not JUL) => LoggerProxy must
    work so LevelEnum can not be used and the formergetLevelName()
    (switch case) is required.
    2 - JUL initialized: calls redirectPlatformLogger() =>
    JavaLoggers created => LevelEnum initialized (Level object can
    be retrieved from LoggingSupport)

    For example, JUL can be started programmatically so the
    loggingEnabled flag is not enough to initialize the
    LevelEnum.object at step 1 and it will be null. Then at step 2,
    LevelEnum.object are null so the JavaLogger is broken.

    Here is a test of this scenario:
    public static void main(String[] args) {

        final PlatformLogger log =
    PlatformLogger.getLogger("sun.awt.X11"); // 1: LoggerProxy

        if (log.isLoggable(PlatformLogger.INFO)) {
    log.info <http://log.info>("PlatformLogger.INFO: LoggerProxy");
        }

        final Logger logger = Logger.getLogger("test");
    //   calls PlatformLogger.redirectPlatformLoggers(); // 2 :
    JavaLogger

        if (log.isLoggable(PlatformLogger.INFO)) {
    log.info <http://log.info>("PlatformLogger.INFO: JavaLogger");
        }

    logger.info <http://logger.info>("done");
    }

    Laurent

    2013/3/21 Peter Levart <peter.lev...@gmail.com
    <mailto:peter.lev...@gmail.com>>

        Hi Laurent, Mandy,

        Let me try one more time (it would be less optimal to have 2
        switch statements instead of one). Here's the 3rd webrev:

        
http://dl.dropbox.com/u/101777488/jdk8-tl/PlatformLogger/webrev.03/index.html

        The changes:
        - added the comments
        - made LevelEnum private (to not be tempted to be used from
        outside the PlatformLogger)
        - used the loggingEnabled flag as a pre-check when trying to
        parse the level objects


        What do you think?

        Regards, Peter

        On 03/21/2013 04:06 PM, Laurent Bourgès wrote:
        Thanks Mandy for the clarifications !

        Peter, I propose to:
        - move the LevelEnum into the JavaLogger class in order to
        initialize it as later as possible i.e. after j.u.l calls
        redirectPlatformLoggers()
        - only use it in JavaLogger class (private enum) so revert
        changes to PlatformLogger and LoggerProxy classes
        - add few comments in the code to explain lazy
        initialization (see mandy's answer)

        Finally, could you keep my comment before the switch case
        (high occurences first) ?
        +        static LevelEnum forValue(int levelValue) {

        +            // higher occurences first (finest, fine, finer, info)
        +            // based on isLoggable(level) calls (03/20/2013)
        +            // in jdk project only (including generated sources)

        +            switch (levelValue) {
        +                case PlatformLogger.FINEST:  return FINEST;  // 116 + 
2257 matches in generated files
        +                case PlatformLogger.FINE:    return FINE;    // 270

        +                case PlatformLogger.FINER:   return FINER;   // 157
        +                case PlatformLogger.INFO:    return INFO;    // 39
        +                case PlatformLogger.WARNING: return WARNING; // 12

        +                case PlatformLogger.CONFIG:  return CONFIG;  // 6
        +                case PlatformLogger.SEVERE:  return SEVERE;  // 1
        +                case PlatformLogger.OFF:     return OFF;     // 0

        +                case PlatformLogger.ALL:     return ALL;     // 0
        +                default:                     return UNKNOWN;
        +            }

        +        }

        cheers,
        Laurent

        2013/3/21 Mandy Chung <mandy.ch...@oracle.com
        <mailto:mandy.ch...@oracle.com>>

            Laurent, Peter,

            I haven't looked at the patch yet.  One thing worths
            mentioning is that PlatformLogger was added for the
            platform code to use so as to avoid the initialization
            of java.util.logging since logging is not turned on by
            default and that to reduce the startup overhead. In
            addition, it also enables the elimination of the core
            classes dependency from java.util.logging for
            modularization effort. Therefore the PlatformLogger
            only lazily looks up the Level object when
            java.util.logging is present and also has been
            initialized by application code.

            Mandy


            On 3/21/2013 7:45 AM, Peter Levart wrote:
            On 03/21/2013 03:30 PM, Laurent Bourgès wrote:
            Peter,

            your solution looks better; I wanted my patch to be
            simple, efficient and only modify the JavaLogger
            class (localized changes).

            In your patch, I have doubts related to lazy and
            conditional initialization in JavaLogger (static
            initialization):
            if (LoggingSupport.isAvailable()) {
                 // initialize ...
            }

            In original code, if LoggingSupport.isAvailable()
            returned false, levelObjects map remained empty and
            consequently null was used as the level object passed
            to LoggingSupport methods. In LevelEnum I try to keep
            this logic. When LevelEnum is first used, it's
            constants are initialized and level objects with them. If
            LoggingSupport.isAvailable() returns false, level
            objects are initialized to null.

            I just noticed there's a bug in initialization of the
            LevelEnum.UNKNOWN member constant. It should not try
            to parse level object. Here's an update:

            
http://dl.dropbox.com/u/101777488/jdk8-tl/PlatformLogger/webrev.02/index.html

            But your concern might be correct. In my code
            LevelEnum is also used from the LoggerProxy.format()
            method (in addition to all the places in JavaLogger)
            to obtain the level name for formatting. If this
            method is called the first time while
            LoggingSupport.isAvailable() returns false and that
            happens before JavaLogger uses LevelEnum for the first
            time (and at that time LoggingSupport.isAvailable()
            would return true), then level objects will not be
            initialized correctly.

            Lazy initialization of level objects might help (for
            the price of added complexity)...

            Regards, Peter

            Does somebody have the knowledge about LoggingSupport.isAvailable()

            and the lazy PlatformLogger initialization (JavaLogger are only 
used when j.u.l is initialized) ?


            What's happening if LoggingSupport.isAvailable() returns false in 
your patch ?
            - LevelEnum  instances are incorrectly initialized:object field is 
null !

            - PlatformLogger is then broken ... as Level object are required by 
j.u.l calls


            To fix both problems, moving theLevelEnum  into JavaLogger should 
help and check nulls onLevelEnum.object field.


            Thanks for your feedback,
            Laurent


            2013/3/21 Peter Levart <peter.lev...@gmail.com
            <mailto:peter.lev...@gmail.com>>

                On 03/21/2013 12:12 PM, Laurent Bourgès wrote:

                    Here is an improved patch tested on JDK7u13
                    and JDK8 internal build on my
                    machine linux x64:
                    http://jmmc.fr/~bourgesl/share/webrev-8010309/ 
<http://jmmc.fr/%7Ebourgesl/share/webrev-8010309/>

                    FYI, I removed completely the Map<Integer,
                    Object> levelObjects and used
                    two arrays to perform the PlatformLogger's
                    level (int) to j.u.l.Level
                    mapping:

                    I decided to keep it simple as possible (no
                    enum ...) and used a switch
                    case based on current level occurences:


                Hi Laurent,

                In my experience enums are just the right and
                most compact tool for coding such constant
                associations. Here's a quick try (ripping off
                your optimized switch ;-):

                
http://dl.dropbox.com/u/101777488/jdk8-tl/PlatformLogger/webrev.01/index.html

                ...it adds 12 LOC to the original PlatformLogger
                and is 43 LOC less tha your patch. In addition:

                - only one switch instead of two (to maintain)
                - no parallel IDX_ constants

                What do you think?

                Regards, Peter



                      510 /**
                      511          * Return the corresponding
                    j.u.l.Level instance
                      512          * @param level PlatformLogger
                    level as integer
                      513          * @return Object (j.u.l.Level
                    instance) or null if no
                    matching level
                      514  */
                      515 private static Object getLevel(final
                    int level) {
                      516   if (levelObjects == null) {
                      517       return null;
                      518   }
                      519   // higher occurences first (finest,
                    fine, finer, info)
                      520   // based on isLoggable(level) calls
                    (03/20/2013)
                      521   // in jdk project only (including
                    generated sources)
                      522   switch (level) {
                      523       case FINEST  : return
                    levelObjects[IDX_FINEST];
                    // 116 + 2257 matches in generated files
                      524       case FINE    : return
                    levelObjects[IDX_FINE];    // 270
                      525       case FINER   : return
                    levelObjects[IDX_FINER];   // 157
                      526       case INFO    : return
                    levelObjects[IDX_INFO];    // 39
                      527       case WARNING : return
                    levelObjects[IDX_WARNING]; // 12
                      528       case CONFIG  : return
                    levelObjects[IDX_CONFIG];  // 6
                      529       case SEVERE  : return
                    levelObjects[IDX_SEVERE];  // 1
                      530       case OFF   : return
                    levelObjects[IDX_OFF];     // 0
                      531       case ALL   : return
                    levelObjects[IDX_ALL];     // 0
                      532       default    : return null;
                      533   }
                      534         }

                    I enhanced the PlatformLoggerTest class also
                    and figured out that TLAB
                    optimized Integer allocations but I think the
                    patch is still useful.

                    Can you have a look to the patch ?
                    Should I write a jtreg test  (performance /
                    GC issue) ?

                    Cheers,
                    Laurent


                    2013/3/20 Mandy Chung <mandy.ch...@oracle.com
                    <mailto:mandy.ch...@oracle.com>>

                          Hi Laurent,

                        Thank you for signing the OCA.  Your
                        contribution is very welcome.  You
                        can submit a patch for this bug (see [1])
                        to Core libraries group which
                        owns logging.  Jim Gish and I will
                        sponsor it.

                        Thanks
                        Mandy
                        [1] http://openjdk.java.net/contribute/


                        On 3/20/2013 5:45 AM, Laurent Bourgès wrote:

                        Hi mandy,

                        Do you want me to propose an improved
                        patch to remove the former Map and
                        fix the getLevel() method ? or you prefer
                        fix on your own ?

                        Is it better to discuss the fix on the
                        bug database (still not visible) ?

                        Laurent

                        2013/3/19 Mandy Chung
                        <mandy.ch...@oracle.com
                        <mailto:mandy.ch...@oracle.com>>

                              Hi Laurent,

                            Thanks for the contribution.  I agree
                            that the map can be replaced with a
                            direct mapping from a int value to
                            Level object and avoid the autoboxing
                            conversion.

                            I have filed a bug to track this and
                            target this for JDK8:
                            http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=
                            8010309

                            Thanks
                            Mandy


                            On 3/19/13 5:19 AM, Laurent Bourgès
                            wrote:

                            Dear all,

                            I run recently netbeans profiler on
                            my swing application
                            (Aspro2:http://www.jmmc.fr/aspro
                            ) under linux x64 platform and
                            figured out a
                            performance and waste issue related
                            to PlatformLogger.

                            Actually, the JavaLogger
                            implementation uses a Map<Integer,
                            Object>
                            levelObjects to store mapping between
                            PlatformLogger's levels (int) and JUL
                            Level instances.

                            However, the isLoggable(int level)
                            method is highly used by awt project and
                            other JDK projects and it leads to
                            many Integer allocations as autoboxing
                            converts the level as int to an
                            Integer instance used by the
                            Map.get() call.

                                 /**
                                  * JavaLogger forwards all the
                            calls to its corresponding
                                  * java.util.logging.Logger object.
                                  */
                                 static class JavaLogger extends
                            LoggerProxy {
                             private static final* Map<Integer,
                            Object>* levelObjects = new
                            HashMap<>();
                            ...
                             public boolean isLoggable(*int level*) {
                             return
                            LoggingSupport.isLoggable(javaLogger, *
                            levelObjects.get(level)*);
                                     }
                                 }

                            I wrote a simple test to illustrate
                            that performance / waste problem:
                            PlatformLoggerTest that simply
                            performs 1 million DISABLED log
                            statements:
                             if
                            (log.isLoggable(PlatformLogger.FINEST)) {
                             log.finest("test
                            PlatformLogger.FINEST");
                                         }

                            As you can see in screenshots:
                            - 5 million Integer instances are
                            allocated
                            - Integer.valueOf(int) is called 5
                            million times (autoboxing)
                            - HashMap.get() represents 30% of the
                            test time
                            - patched PlatformLogger is 3x times
                            faster
                            [jvm options: -Xms8m -Xmx8m -verbose:gc]

                            I suggest you to use an alternative
                            way to map PlatformLogger's levels
                            (int) and JUL Level instances to fix
                            that performance / memory issue: I
                            added the getLevel(int level) method
                            that performs a switch case to return
                            the corresponding Level object (quick
                            and dirty solution).

                            I advocate this is not a very clean
                            solution but I prefer efficiency here:
                            any better solution may be
                            appropriate to avoid at least Integer
                            allocation
                            and maybe enhance performance.

                            Best regards,
                            Laurent Bourgès

                            PS: here is the patch as text:

                            # This patch file was generated by
                            NetBeans IDE
                            # It uses platform neutral UTF-8
                            encoding and \n newlines.
                            --- PlatformLogger.java (6767)
                            +++ PlatformLogger.java (6768)
                            @@ -468,31 +468,13 @@
                                   * java.util.logging.Logger object.
                                   */
                                  static class JavaLogger extends
                            LoggerProxy {
                            -        /** Note: using Integer keys
                            leads to a lot of new Integer
                            instances !! */
                            -  private static final Map<Integer,
                            Object> levelObjects = new
                            HashMap<>();
                            -        /** fastest mapping to Level
                            instances from PlatformLogger level
                            as integer */
                            -  private static final Object[]
                            fastLevelObjects;
                            -
                            -
                            +  private static final Map<Integer,
                            Object> levelObjects =
                            +  new HashMap<>();
                            +
                            static {
                            if (LoggingSupport.isAvailable()) {
                                // initialize the map to Level
                            objects
                            getLevelObjects();
                            -
                            -    // initialize the fastLevelObjects:
                            -  fastLevelObjects = new Object[] {
                            -
                             LoggingSupport.parseLevel(getLevelName(OFF)),
                                  //
                            0
                            -
                             LoggingSupport.parseLevel(getLevelName(SEVERE)),
                               //
                            1
                            -
                             LoggingSupport.parseLevel(getLevelName(WARNING)),
                              //
                            2
                            -
                             LoggingSupport.parseLevel(getLevelName(INFO)),
                                 //
                            3
                            -
                             LoggingSupport.parseLevel(getLevelName(CONFIG)),
                               //
                            4
                            -
                             LoggingSupport.parseLevel(getLevelName(FINE)),
                                 //
                            5
                            -
                             LoggingSupport.parseLevel(getLevelName(FINER)),
                                //
                            6
                            -
                             LoggingSupport.parseLevel(getLevelName(FINEST)),
                               //
                            7
                            -
                             LoggingSupport.parseLevel(getLevelName(ALL))
                                   //
                            8
                            -    };
                            -            } else {
                            -  fastLevelObjects = null; // check null
                            }
                                      }

                            @@ -515,7 +497,7 @@
                            this.javaLogger =
                            LoggingSupport.getLogger(name);
                            if (level != 0) {
                                // level has been updated and so
                            set the Logger's level
                            -
                             LoggingSupport.setLevel(javaLogger,
                            getLevel(level));
                            +  LoggingSupport.setLevel(javaLogger,
                            levelObjects.get(level));
                            }
                                      }

                            @@ -526,11 +508,11 @@
                                      * not be updated.
                                      */
                                      void doLog(int level,
                            String msg) {
                            -  LoggingSupport.log(javaLogger,
                            getLevel(level), msg);
                            +  LoggingSupport.log(javaLogger,
                            levelObjects.get(level), msg);
                                      }

                                      void doLog(int level,
                            String msg, Throwable t) {
                            -  LoggingSupport.log(javaLogger,
                            getLevel(level), msg, t);
                            +  LoggingSupport.log(javaLogger,
                            levelObjects.get(level), msg,
                            t);
                                      }

                                      void doLog(int level,
                            String msg, Object... params) {
                            @@ -544,12 +526,12 @@
                            for (int i = 0; i < len; i++) {
                                sparams [i] =
                            String.valueOf(params[i]);
                            }
                            -  LoggingSupport.log(javaLogger,
                            getLevel(level), msg, sparams);
                            +  LoggingSupport.log(javaLogger,
                            levelObjects.get(level), msg,
                            sparams);
                                      }

                            boolean isEnabled() {
                            Object level =
                            LoggingSupport.getLevel(javaLogger);
                            -  return level == null ||
                            level.equals(getLevel(OFF)) == false;
                            +  return level == null ||
                            level.equals(levelObjects.get(OFF)) ==
                            false;
                                      }

                                      int getLevel() {
                            @@ -566,34 +548,12 @@

                                      void setLevel(int newLevel) {
                            levelValue = newLevel;
                            -
                             LoggingSupport.setLevel(javaLogger,
                            getLevel(newLevel));
                            +  LoggingSupport.setLevel(javaLogger,
                            levelObjects.get(newLevel));
                                      }

                            public boolean isLoggable(int level) {
                            -  return
                            LoggingSupport.isLoggable(javaLogger,
                            getLevel(level));
                            +  return
                            LoggingSupport.isLoggable(javaLogger,
                            levelObjects.get(level));
                                      }
                            -
                            -        /**
                            -         * Return the corresponding
                            level object (fastest implementation)
                            -         * @param level
                            PlatformLogger level as primitive integer
                            -         * @return Object (JUL Level
                            instance)
                            -         */
                            -  private static Object
                            getLevel(final int level) {
                            -  // higher occurences first
                            (finest, fine, finer, info):
                            -  switch (level) {
                            -    case FINEST  : return
                            fastLevelObjects[7];
                            -    case FINE  : return
                            fastLevelObjects[5];
                            -    case FINER   : return
                            fastLevelObjects[6];
                            -    case INFO  : return
                            fastLevelObjects[3];
                            -    case CONFIG  : return
                            fastLevelObjects[4];
                            -    case WARNING : return
                            fastLevelObjects[2];
                            -    case SEVERE  : return
                            fastLevelObjects[1];
                            -    case ALL   : return
                            fastLevelObjects[8];
                            -    case OFF   : return
                            fastLevelObjects[0];
                            -    default  : return null;
                            -            }
                            -        }
                            -
                                  }

                                  private static String
                            getLevelName(int level) {















Reply via email to