On 03/21/2013 04:31 PM, Peter Levart wrote:
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)

Hi Mandy,

JavaLogger and LoggerProxy are only used from within PlatformLogger. Wouldn't it be better to declare them private? Their usage (one or the other) depends on the 'loggingSupport' flag that is initialized with PlatformLogger class. Usage of JavaLogger or LoggerProxy outside PlatformLogger would be wrong, I think.

- used the loggingEnabled flag as a pre-check when trying to parse the level objects

I think this pre-check is optimal and does not even try to load the LoggingSupport class unless loggingEnabled is true. I tried the following test:

public class TestLazyClassInit {

private static final boolean FALSE = Boolean.getBoolean("non.existent.property");

    static class Foo {
        static {
            System.out.println("Foo initialized");
        }

        static boolean isOk() {
            return true;
        }
    }

    public static void main(String[] args) {
        if (FALSE && Foo.isOk()) {
            System.out.println("FALSE is true");
        } else {
            System.out.println("FALSE is false");
        }
    }
}


... and it only prints: "FALSE is false"...




Regards, Peter



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