
June 1996 Newton Technology Journal
20
actual difference of 4K versus 1K.
USING A SIMPLE PROTO AND A
viewDrawScript
WITH CACHING
Though faster, the simple proto with a
viewDrawScript
is still repeating
much work. Each of the shape creation calls, like
MakeText
, are repeated
for the same item as the user scrolls up and down. To compensate for this,
we could save the shapes for each item in a cache and reuse them. To
construct the cache itself, P will allocate an array with N entries, one for each
item. We could cache each individual shape created, indexed by an item’s
index, but the Newton OS provides a better function called
MakePICT
.
This function accepts the same array of shapes and styles that
drawShape
does, but it constructs a PICT picture that can be stored and passed to
drawShape
as needed. This allows us to cache all of the drawing
commands for an item as a single picture.
To use the cache, the proto’s
viewDrawScript
will first check the
array for a cached picture. If there isn’t one, it calls
MakePICT
with shapes
and styles, caching the result. Then the
viewDrawScript
sends
drawShape
message with the cached picture.
We don’t expect the use of a cache to slow down the application as it
opens, but it does slightly: 1.8 versus 1.3 seconds. There is some time
penalty for constructing the pictures and then imaging them. Storing items
in the cache takes very little time. Because the cache is an array indexed by
the item’s index, it also takes very little time to retrieve stored shapes. As a
result, scrolling is a bit faster, down from 21.6 seconds to 14.7 seconds.
Apparently only some of the overhead is due to shape creation.
The biggest difference is in the increase in heap used, up from 1K to 12K.
The cache takes space; about 0.4K per item’s picture. If we explore what
happens if the user scrolls to the bottom of the list of 100 items and back up
again, the cache grows to the point where the application uses more than 44K
of heap! A simple and efficient solution to this is to uncache one picture each
time the user scrolls (by setting the appropriate element of the array back to
NIL.) Recall that each scroll brings one new item into view and removes one
from view. The
viewDrawScript
can ensure that the new item’s picture is
cached, and the
viewScrollUpScript
and
viewScrollDownScript
can uncache the hidden item’s picture. Making these changes slows scrolling
by 0.5 seconds but limits the application’s heap usage to 8K.
To save even more space, the cache can be made to hold exactly S
pictures, just enough for the S child views. Using modulo arithmetic, each
child view’s index can be mapped into a unique location in the cache; for
example, the sixteenth item has an index of 15. Taking this modulo S (=
14) yields an index of 1 in the cache. This position is also shared by the
second item (with an index of 1), but it’s easy to show that the second and
sixteenth items are not displayed at the same time. This saves N – S empty
array elements and could be significant for large numbers of items N.
The above approaches illustrate the general tradeoff between the time
and space a computation requires. By caching pictures we can save 6.4
seconds of scrolling time at the expense of 7K of heap. The
setOrigin
implementation saves a further 5.6 seconds of scrolling time for an additional
24K of heap. A specific application’s requirements usually help decide which
point in this tradeoff is the most preferable though often it amounts to
choosing between several unattractive alternatives.
USING A SIMPLE PROTO AND BIT MAP CACHING
Newton 2.0 OS provides a useful capability that gives us another alternative
in the time-space tradeoff for our scrolling application. Using the simple
proto and a
viewDrawScript
we will cache the whole bit map for an
item’s visual representation. In the previous implementation we cached a
picture, so that is a series of drawing instructions had to be executed by the
Newton view system to image the view. Now we’ll cache a bit map that
specifies each pixel’s value and can be imaged more quickly.
When the item is first drawn, it uses
drawShape
and shape creation
functions as before, but the resulting image is then converted into a bit map
by sending the
viewIntoBitmap
message. The resulting bit map is then
cached in an array indexed by the item’s index. When the item is to be re-
displayed, the bit map is drawn instead of the various shapes.
Making this work efficiently requires a
viewDrawScript
with three
separate conditions. The first condition checks to see if the cache contains a
bit map for this particular item. If the array implementing the cache is non-
NIL for this item’s index, there is a bit map, and the
viewDrawScript
calls
drawShape
with it. The second condition is triggered if there is no
cached bit map. It will create an empty bit map, send itself
viewIntoBitmap
, and
drawShape
and cache the result. The
viewIntoBitmap
message calls the
viewDrawScript
of the view, so
the second condition sets and checks a flag to prevent an infinite loop. The
third condition executes when the flag has been set and images the view in
response to the
viewIntoBitmap
message. This is where the shapes are
drawn as they would be in a non-caching
viewDrawScript
. For
reference, a
viewDrawScript
that caches bit maps for the simple proto
in our example is listed in Table 1. (Recall that in this code, the rounded
rectangle shape is created as part of the checkbox bit maps rather than as a
separate call to
MakeRoundRect
.)
Table 1. viewDrawScript for Simple Proto that Caches Bit Map Images.
func() begin
if index then begin
local B := madeBitmaps[index];
// Draw the cached bitmap if there is one.
if B then begin
:drawShape(B,nil);
end;
// Otherwise, trigger viewIntoBitmap and cache result.
else if NOT cachingInProcess then begin
cachingInProcess := true;
B := MakeBitmap(itemWidth,viewBounds.bottom,nil);
:viewIntoBitmap(nil,nil,B);
madeBitmaps[index] := B;
:drawShape(B,nil);
cachingInProcess := nil;
end;
// Called by viewIntoBitmap to image bitmap.
else begin
local Item := kItemArray[index];
:copyBits(if Item.check then kCheckIcon else
kUncheckIcon,0,1,modeCopy);
:copyBits(Item.icon,19,5,modeCopy);
:drawShape(
MakeText(Item.text1,36,1,text1R,12),
kTextStyle);
:drawShape(
MakeText(Item.text2,text2L,1,text2R,12),
kTextStyle);
:drawShape(
MakeRect(sliderL,6,itemWidth,12),
kBarBackgroundStyle);
:drawShape(
MakeRect(sliderL,6,
itemWidth-RIntToL(36-Item.value*36/100),
12),
kBarForegroundStyle);
end;
end;
end
Kommentare zu diesen Handbüchern