Things to Consider: When to use ICs
General issues
Moving an application to ICs requires additional discipline by the developers responsible for packaging the ICs and by the quality assurance teams testing changes prior to their delivery into the field. First, developers that create ICs need to maintain a resource known as a snapshot file. A snapshot file contains information about what objects were packaged into an IC. When packaging an IC, the snapshot file is output into the same directory as the IC file. The name of the snapshot file will be the same as the IC file, but with a suffix of .snp. As a result, teams need to be disciplined about management of .snp files and sharing of these files among teams.
The snapshot file is required when packaging dependent ICs and during subsequent packaging operations to re-create the initial IC. From a development perspective, the .ic file, the .snp file, and the packager instruction class used to produce the IC should be managed as a unit. Snapshot files need not be deployed to users, because they are used only for packaging. Snapshot files contain information about the state of the image at the time of packaging. There are no restrictions on the state of the development image used to package an IC. The Applications within the image might consist of open editions with unreleased classes, or even scratch editions. Although not desirable for managing and maintaining production Smalltalk code, VAST packager gives you the freedom to manage the state of your image prior to packaging. However, to allow the resulting IC to be used as a prerequisite and to determine the compatibility of changes for subsequent packaging of the original IC, the snapshot file records the state of the image in terms of the Applications, SubApplications and Classes within it. It records what was loaded in the image at package time for future reference. A snapshot file contains only the information pertaining to the IC that was created, not the whole image.
Snapshots cannot be loaded into an image like Applications or configuration maps. For this reason, even though snapshots are somewhat like automatic versions they should not be used in this manner. ICs and snapshot files that are produced by a development team should be based on stable code that has been properly versioned in VAST Platform, so that the configuration used to package the IC can be easily retrieved.
If a VAST application is the product of the effort of two or more teams, where different teams are responsible for different ICs within the final deployment, a procedure should be established for delivery of the .ic files, .snp files, and packager instruction classes from the team creating them to the teams that will use them. The process becomes more complex if several teams are dependent on each other's development efforts. In these situations, it is best to set up an internal IC clearing house within the organization. The most natural candidate for such a group is the team that is responsible for maintaining highly reused components and frameworks. In other words, the discipline required for multiple team development of ICs is the same as the process used to manage reusable components within the organization. Some other points to consider are:
• Packaging ICs is typically much quicker that packaging complete reduced runtime images for complex client/server applications
• If the application consists of multiple ICs, where different development teams are each responsible for ICs then the packaging responsibility for those ICs will be distributed as well. This structure fits in nicely with the notions of class ownership and application management. It also alleviates the development team that is responsible for creating delivered reduced runtime images from having to debug everyone else's packaging problems.
• Single use ICs (ICs that are not shared between two or more VAST applications being run simultaneously) will typically have a larger memory footprint that their reduced runtime image counterparts. This is because there will be code in the ICs that is not used by the application, and that would not be present in a reduced runtime image.
• There is extra processing overhead associated with binding ICs either explicitly or implicitly, compared to loading a single reduced runtime image. Binding an IC may take several seconds or more.
Making maintenance easier
If deployment of changes to into the field using reduced runtime images is a problem due to large number of sites, or low bandwidth distribution channels and available memory for VAST applications on client workstations is an issue, then building an IC version of your application may help alleviate the bandwidth problem. There are a number of factors that you should be aware of prior to making a final decision:
• The kinds of changes to ICs which are not the "leaf IC," are restricted to method additions, deletions, and modifications. Changes to class signatures and hierarchies are considered incompatible changes and would result in having to repackage and redeploy the dependent ICs as well. In other words, using ICs as an incremental delivery mechanism is best suited for bug fixes rather than deployment of complete new versions of the application, if you need to modify ICs that have dependents. If your application is contained within a single leaf IC, then your developers need not concern themselves with the kinds of changes they are making.
• The boundaries on which the ICs are split, the size of the IC, and volatility of the code contained within a particular IC will determine the frequency of distribution as well as the size of the distributed IC.
Reducing memory footprint
Many development teams will be interested in the potential benefits of moving their application to ICs, in order to share code between simultaneously running VAST applications. By answering the questions posed at the beginning of this section, you will have enough information to be able to determine approximately how much memory you will save as a result of converting your application to use ICs.
One thing is certain, if your users only ever run a single copy of a single VAST-built application, then moving to ICs will most likely degrade performance and add complexity to the development process. Under these circumstances the decision to convert to ICs should solely be based on whether ICs will aid in deploying production fixes for the application and must be carefully considered.
Another general point is that the amount to which ICs can reduce your memory footprint requirements is largely a function of how much code is reused between two applications. If every VAST-built application within an IT organization includes its own custom frameworks for persistence, exception handling, data validation, business rules and so on, then the maximum sharing is limited to common code in all VAST applications. To benefit from shared ICs, the code must first be shared!
The memory that can be shared by applications built with ICs is limited to ROM objects. This means that if your application creates and holds onto many objects or creates large objects, ICs will not help much. Before migrating to ICs, first examine the dynamics of the memory use in the application for typical use cases to determine if peak memory requirements seem large. ICs will not correct poor application design.
Be sure to determine the workstation memory available to the VAST applications. This means understanding the memory requirements of the operating system and other applications that might be running at the same time. As a rough rule of thumb, you can estimate your memory footprint to be the size of your reduced runtime image file, doubled. Of course, the actual memory requirements depends on the individual application and the development team should have rough numbers for this based on measurement during actual usage rather than estimation. Understand what tools your users use on a daily basis to understand the memory constraints you are working within.
There's no substitute for understanding how users actually use the applications that you build for them. For determining the appropriate IC architecture, this is crucial. To optimize on memory footprint, you need to understand probability of applications being used simultaneously.
The most significant factor in determining the best delivery strategy is to understand the probability of simultaneous use of VAST-built applications. For the case where two or more VAST-built applications have high probability of being used together and no other VAST-built applications can be invoked, the optimal answer is to package those applications together as a single reduced runtime image. This results in the smallest possible memory footprint for the combined application. This configuration penalizes users (in terms of memory usage) that are interested only in running one of the applications packaged together.
For VAST-built applications that have a low and equivalent probability of being used simultaneously, the best strategy usually is to package the applications as ICs and then bind to them explicitly from a controlling application (that might act as a top level folder or launcher). The initial memory footprint will be lower and the memory footprint will reflect only the ICs that have been loaded into memory as a result of the user choosing to run certain applications.
For the case where there are several VAST applications, with one having high probability of simultaneous use and the others having lower probability, you can incorporate the folder or launcher in with the high probability application, and invoke the lower probability applications through explicit binding. Also, in general it is worthwhile to deploy the launcher application using ICs combined with an .icx image file, to improve startup time.
Minimizing memory footprint during IC loading
Packaged ICs contain information for the development environment such as method categories, class time stamps and library file pointers. To avoid loading this information into memory when loading an IC, specify the -r runtime option on abt.exe. This can make a difference for ICs that contain many CompiledMethods, as each CompiledMethod would otherwise use 4 bytes of memory for holding the library file pointers.