1 Reply Latest reply on May 12, 2005 6:51 AM by Ivelin Ivanov

    Dependency tracking: benchmark

    Sacha Labourey Master

      There was discussion going on (between Adrian and Bill) about the excessive time it would take if we wanted the MC to be fully aware of all dependencies before actually loading classes. The idea was, for the MC, to be able to read dependencies from a class file before actually loading the class through a CL, satisfy these dependencies and the load the class.

      It looks like loading these dependencies on a class by class basis (at the time you load the class), takes a lot of time. Hence, I wanted to test how much time it would take if, instead of loading that information at class-load time, we would load it at deployment-time, when a new xAR gets deployed, for ALL classes in that JAR.

      The code below just does this:
      - it list a folder
      - read all JARs in that folder
      - for each JAR read all *.class files
      - for each class file analyse its bytecode (WITHOUT LOADING THE CLASS)
      - read class, method and field annotations
      - populate a repository which, for each class name, store the list of related annotations that are required by that class.

      On my laptop, loading the JBoss 3.2.x lib folder (~9Mo of JARs) takes 2.8 seconds. Now, this can be optimized as lots of JARs are simply "dumb code" (with no annotations) such as parsers, JSR-77 API, etc. removing a few of these jars bring the analysis time below 2 seconds.

      Hence, instead of analysing the dependencies at CLASS-LOAD-TIME, one-by-one, why not do it at DEPLOYMENT-TIME, for all classes in the xAR, and store that dependency data structure in the related xAR DeploymentInformation. Hence, once the classloader would have to actually load a specific class, it would not have to analyse the bytecode at that time but instead rely on the pre-populated dependency data structure that had been built at deployment time.

      FYI, code uses Javassist low-level API ro read class bytecode:

      
      import javassist.bytecode.*;
      
      import java.util.jar.*;
      import java.util.zip.ZipEntry;
      import java.util.Iterator;
      import java.util.List;
      import java.util.ArrayList;
      import java.io.DataInputStream;
      import java.io.FileFilter;
      import java.io.File;
      import java.io.BufferedInputStream;
      
      public class ReadClass {
      
       ArrayList db = new ArrayList(6000);
      
       public static void main (String[] args) throws Exception
       {
       ReadClass self = new ReadClass ();
       self.doJob ();
       }
      
       public void doJob () throws Exception
       {
       File file = new File ("P:\\CVS_HOME\\jboss-3.2\\build\\output\\jboss-3.2.6RC2\\server\\default\\lib"); // replace with your directory here
       File[] files = file.listFiles(new FileFilter()
       {
       public boolean accept (File f)
       {
       return f.isFile() && f.getName().endsWith(".jar");
       }
       }
       );
      
       long start = System.currentTimeMillis();
      
       for (int i=0; i<files.length; i++)
       {
       processFile(files \[i\] );
       }
      
       System.out.println ("Total time: " + (System.currentTimeMillis() - start));
       System.out.println ("Total classes: " + db.size());
      
      
      
       }
      
       public void processFile (File fileToProcess)
       throws Exception
       {
       DataInputStream in = null;
      
       JarFile file = new JarFile (fileToProcess);
       java.util.Enumeration entries = file.entries();
      
       while (entries.hasMoreElements())
       {
       ZipEntry entry = (ZipEntry) entries.nextElement();
       if (entry.getName().endsWith(".class"))
       {
       ArrayList annotations = new ArrayList();
      
       in = new DataInputStream (new BufferedInputStream (file.getInputStream(entry)));
       ClassFile cf = new ClassFile (in);
      
       // Classes attributes
       //
       extractAnnotations (cf.getAttributes(), annotations);
      
       // Methods attributes
       //
       Iterator methods = cf.getMethods().iterator ();
       while (methods.hasNext())
       {
       MethodInfo method = (MethodInfo)methods.next();
       extractAnnotations( method.getAttributes(), annotations);
       }
      
       // Fields attributes
       //
       Iterator fields = cf.getFields().iterator ();
       while (fields.hasNext())
       {
       FieldInfo field = (FieldInfo)fields.next();
       extractAnnotations( field.getAttributes(), annotations);
       }
      
       // Add annotations to shared structure
       //
       db.add(new ClassAnnotation (cf.getName(), annotations));
       }
       }
      
       }
      
       public void extractAnnotations (List attrs, ArrayList store)
       {
       Iterator attributes = attrs.iterator();
       while (attributes.hasNext())
       {
       AttributeInfo attr = (AttributeInfo)attributes.next();
       if (attr instanceof AnnotationsAttribute)
       {
       store.addAll(java.util.Arrays.asList( ((AnnotationsAttribute)attr).getAnnotations()));
       }
       }
      
       }
      
       public class ClassAnnotation
       {
       String name;
       ArrayList annotations;
      
       // store which annotations are being used by a given class
       public ClassAnnotation (String name, ArrayList annotations)
       {
       this.name = name;
       this.annotations = annotations;
       }
       }
      }
      


        • 1. Re: Dependency tracking: benchmark
          Ivelin Ivanov Expert

          Makes sense. It is consistent with the way we populate in the UCL3 repository information about all packages that a registered classloader recognizes at deployment time.

          As additional minor optimization, per compressed archive, the dependency table can be written to a cash file on first startup and loaded consequently.

          Ivelin