ADC Home > Reference Library > Technical Notes > Legacy Documents > Tools >
Important: This document is part of the Legacy section of the ADC Reference Library. This information should not be used for new development.
Current information on this Reference Library topic can be found here:
Some people consider defining a segmentation strategy for a Macintosh application a black art. Well, it is a form of programming art. Like many problems in software development, defining a segmentation strategy for a Macintosh application requires choosing between a number of conflicting tradeoffs to meet the performance criteria of a given application. Segmentation strategies that are optimized for speed generally require a larger footprint in memory, while strategies that are optimized for memory usage come with a performance hit. Unfortunately, there are no tools available for automatically segmenting applications.
There are few tools available that help define segmentation strategies. Also the information on how to do this is not fully documented.
This Technical Note will try to cover the most important issues of segmentation with MacApp programs and to help developers create their own segmentation strategies.
The Need for Segmentation
Inside Macintosh describes how the Segment Loader works, and why there's a need to create segments that are loaded into memory on demand. The syntax for creating a segment name is:
C++: Specify a
Object Pascal: Specify a
If you forget to place the segment compiler directives in your method, it will
inherit the earlier directive (in C++ as well as in Object Pascal) all the way
to the end of the file, so suddenly you'll find many methods inside one of your
segments. Methods without any defined segment will go into the
Segments are really
Methods are the actual routines stored in the
Jump Tables and Performance
There is a known relation between jump table sizes and segmentation. For normal procedures and functions, we don't need a jump table entry if all calls to the routine are from the same segment (intrasegment call). However, we need jump table entries if there are calls to other segments from the routine (intersegment calls). Examine the segmentation of your code; you might find places where a change in segmentation would eliminate jump table entries. With some effort you may shrink big jump tables and improve the performance of your application.
Some programmers worry that many C++ accessor (Get...) and mutator (Set...)
methods will increase the jump table entries considerably, but you can avoid
this by using C++
Caching of member field values inside the class (for instance keeping track of
field values using temp variables) decreases the need for Get and Set calls.
This design is, however, not purely object-oriented, because the class then
needs to know about the internal implementation of an object. For instance it
needs to know when the cached value is invalid, and it also assumes too much
about the internal fields. In addition, we could place the major parts of an
object inside one single segment for further performance improvements. This
helps mostly if the accessors are providing information to other objects that
reside in the same segment, as in the case where we use accessors internally in
the same object. You can use
The Segment Loader has to fill the jump table with the right addresses when the segments are loaded in. When the segment is unloaded, the environment has to reset the jump table with information about the missing segment. MacApp has to make sure that memory is always available for data and unloaded segments. All this takes time, so clever segmentation does improve performance. Also, PowerBook owners don't like applications that spend a lot if time starting the hard disk--for instance for fetching CODE resources frequently! For example, if we place functions that call each other in the same segment, we will eliminate other segment loading events.
One strategy for organizing segments is based on functionality--functions that work together should be placed in the same segment (see reason defined earlier). For example, we need certain routines during an application's initialization phase, but after initialization is complete they can reside on disk until the next launch of the application.
Another strategy is to organize segments so they are as small as possible. This means that the application heap will contain only those segments that we need, increasing the amount of application heap available. The problem with this segmentation strategy has to do with all the Segment Loader calls that we trigger every time a function is not available in memory. This happens only if the segment itself is not initially loaded. Once a segment is loaded, it is marked purgeable when not used by MacApp, and unless we have a full application heap, the segment is still present in memory. Still, if we need to load a lot of segments from a hard disk, it will cause a lot of disk spinning in the case of portables. And end users don't like disks that spin, because they decrease the battery lifetime.
How to Segment MacApp Code
The strategy presented below is a guideline to MacApp application segmentation, but it should not be taken as a prime directive. However, as this is the current MacApp strategy it is not really feasible to use any other alternate segmentation strategies with MacApp applications, because MacApp will be a big part of the application code. However, feel free to experiment and test with the other alternative--small CODE segments--in code that you control.
The MacApp strategy is based on functional groupings, where functions related to each other (they call each other) are placed in the same segment. The functional grouping is:
MacApp has far more segments. We recommend that you do a search of the
Unsure about the placement?
Place the method in the same segment as other methods that call it.
Stack-based C++ classes?
Place the member functions of these classes in the same segment that corresponds to the functionality (in the same segment as the routine or member function that use them).
WDEFs, CDEFs, and similar code segments--should they be part of the class segment?
Place these CODE segments into the
'res!' and 'seg!' Resources Explained
MacApp programmers sometimes will get puzzled concerning the use of
One use for making segments permanently resident is for time-critical functions grouped together in a special segment. Thus, loading the segment doesn't require overhead if the method is suddenly needed. For example, we could use this technique to reduce overhead for time-critical communication methods. Also, we might have functions that are called during interrupt time that always need to be resident.
Here's an example of a
MacApp will also place internal trap patching code in resident segments, to avoid runtime problems with patches that some entity might suddenly unload. This is also true with programmer-provided trap patches.
The MacApp programmer should be aware that segments live a dangerous life inside the MacApp framework. Unless they are resident, or needed, they are purged out. This happens both at initialization time, as well as during the lifetime of the application. We don't want to have segments floating around in memory that we need only during the initialization phase of the application. Likewise, we don't want to have segments in memory that we trigger very few times during the application lifetime.
So who is taking care of the segmentation cleanup? Your mother? No, it's a
global function called
However, we might have situations where we have written code by mistake so that
Now, if you have SourceBug running in the background, and have the warning flag
in the Debug menu entry set for unloading segments, you will get a nice little
Development Tools, ModelFar Support
For a long time, segments were restricted to 32K sizes due to the A5-relative data referencing with 16-bit offsets, but MPW 3.2 eliminates this 32K limit on segment size via new switches to the compilers and the linker.
The 68020 introduced 32-bit PC-relative branching (
Another new feature is "32-bit everything," which transparently removes the limitations on code segment and jump table sizes and the size of the global data areas. The drawback is a larger code size footprint and some slowdown due to increased load time for the larger code segments. But hey, look what you get!
We activate 32-bit everything code generation and linking by using the
Finally, one can generate larger than 32K jump tables using the
See MPW documentation for more information about
Also, the MacApp debugger has support for monitoring segment loading and unloading. Please consult the current MacApp documentation for more information.
Segments and Fragmentation
Sometimes we want to move a separate segment back into the
Note that fragmentation is not a problem for MacApp applications. All resident segments are preloaded and locked high in memory. Nonresident segments that are in use are also locked high. Segments that are not in use but still present are not locked. This means they can be moved by the Memory Manager (which does not contribute any fragmentation).
Another solution is to use the linker to mark code resources from the libraries
as locked. These segments will then be loaded into memory as resident segments,
avoiding fragmentation problems. To do this, for instance modify the user
Yet again in the case of MacApp this is not necessary. The MacApp way is to
define these segments in the
Finally, you can use the linker to merge old segments into new segments with
Linker Problems and Errors
A very typical
This means that the linker can't create 16-bit offset references within the
same code segment, or that we can't do 16-bit PC-relative jumps. The way to go
is to start using
Sometimes even if we compile and link using
Another trick is to use the -
Resident Segment Dangers
Here's an important note to remember. If you make a segment resident (for
instance including it in the '
So the first time you call a routine in the segment, the code will trigger a
This works fine during a safe period of segment loading. However, if you call
the routine during a critical time when it's not safe to call
One workaround is to make sure to call the first routine from this resident
segment during a safe time. For instance you could call the function itself
from another segment (as in the initialization phase, from
It is hoped that these suggestions will improve the segmentation strategies in your application. Future new runtime models (the Shared Library Manager, PowerPC runtime environments, Bedrock) might radically change the way developers segment code. Stay tuned for more news.
Inside Macintosh, Volume II, Chapter 2 (Segment Loader)
MPW Command Reference (forthcoming)
Building and Managing Programs in MPW (forthcoming)
MPW 3.2 Release Notes
Guide to MacApp Tools (MacApp 3.0)
MADA 92 Orlando Conference CD, Presentation on the MacApp 3.0