[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[ft-devel] reduce memory allocation & maximum footprint of FT2 POSIX res
From: |
mpsuzuki |
Subject: |
[ft-devel] reduce memory allocation & maximum footprint of FT2 POSIX resource fork accessor, by "partial stream" |
Date: |
Mon, 19 Jan 2009 10:33:01 +0900 |
Hi all,
In this 2 weeks, I was doing an experiment to reduce the
memory allocation & footprint of POSIX resource fork
accessor, by the introduction of "partial stream".
Now I've written a proof of idea and I want to hear the
comments.
Regards,
mpsuzuki
BACKGROUND
==========
Now FreeType2 can load several MacOS specific resource-fork
based font files, like Suitcase font and LaserWriter
PostScript fonts, even on non-Macintosh platforms, as far
as they have an access to resource-fork.
Unfortunately, ANSI or POSIX API cannot provide to access
both of data-fork & resource-fork by single file descriptor.
Most systems provide an access to resource-fork by different
pathname (aslike Mac OS X POSIX API can access the resource
fork of "foo-bar.txt" by "foo-bar.txt/..namedfork/rsrc").
In addition, most drivers in FreeType2 access the data by
the absolute position in the stream (please grep FT_STREAM_SEEK
and check their arguments), therefore it is difficult to
make existing drivers parse a part of the resource-fork
stream with unnecessary header and trailer. To solve the
problem simply, Yamato-san made the resource-fork accessor
copying the whole of resource-fork to memory buffer, then
extract essential part, and open a face from the extracted
part.
In summary, by the memory copying whole of resource-fork,
the footprint of FreeType2 for resource-fork fonts is expected
to be much larger than that for naked TrueType fonts. I had
similar problem when I was writing preliminary support of sfnt-
wrapped PS Type1 & CID-keyed font.
IDEA OF PARTIAL STREAM
======================
To apply existing drivers for the data access by "relative"
position in the stream, I implemented "partial stream" in
resource-fork accessor. Its descriptor stores a reference
to existing file stream (it is called "source"), the offsets
to the head and end of a part in the source.
Using a partial stream which is opened with appropriate
offset, we can make the unnecesarry header invisible for
the eyes of each font drivers' implementation, without
inflating the footprint. The outline of the implementation
is following (for detail, please check the patched ftrfork.c).
-----------------------------------------------------------
typedef struct FT_PartialStreamRec_
{
FT_Stream source; /* source stream */
FT_ULong offset; /* offset from source stream head to this stream head */
FT_ULong limit; /* length from source stream head to this stream end */
FT_ULong flags; /* flags specified by FT_Stream_OpenPartial */
} FT_PartialStreamRec, *FT_PartialStream;
/* static */ FT_ULong
ft_partial_stream_io( FT_Stream stream,
FT_ULong pos,
FT_Byte* buffer,
FT_ULong count )
{
...
if ( FT_Stream_Seek( part->source, part->offset + pos ) )
FT_Stream_Seek( part->source, orig_pos );
else if ( FT_Stream_Read( part->source, buffer, count ) )
red_bytes = FT_Stream_Pos( part->source ) - orig_pos;
else
red_bytes = count;
...
return red_bytes;
}
/* static */ void
ft_partial_stream_close( FT_Stream stream )
{
...
part->source = NULL;
part->offset = 0;
part->limit = 0;
FT_FREE( part );
stream->descriptor.pointer = NULL;
...
}
FT_BASE_DEF( FT_Error )
FT_Stream_OpenPartial( FT_Library library,
FT_Stream stream,
FT_Stream source,
FT_ULong offset,
FT_ULong limit,
FT_ULong flags )
{
...
part->source = source;
part->offset = offset;
part->limit = limit;
part->flags = flags;
stream->descriptor.pointer = part;
...
stream->read = ft_partial_stream_io;
stream->close = ft_partial_stream_close;
...
return error;
}
-----------------------------------------------------------
One of the remarkable problem of "partial stream" would be
that the partial stream can cause memory leak or double-free
trouble. When we close the partial stream, should we close
(and free) the source stream too? It is difficult to determine.
As a first step, I made the closer of partial stream to close
its source stream by default. For the case that the source
comes from external, a flag to prevent the closing of referred
source stream is introduced. If the decision if the partial
stream should be kept or closed is determined soon after the
creation of partial stream, such mechanism might be sufficient.
However I have to reconsider more carefully about the case:
single resource-fork provides multiple faces, both faces are
opened as persistent resource, and the client application forks
and close different faces.
-----------------------------------------------------------
/* static */ void
ft_partial_stream_close( FT_Stream stream )
{
...
if ( part->source && !( part->flags & FT_PARTSTREAM_KEEP_SOURCE ) )
{
FT_Stream_Close( part->source );
FT_FREE( part->source );
}
...
-----------------------------------------------------------
Attached patch to introduce "partial stream" mechanism into
resource-fork accessor and ftmac.c. Its quality is very
experimental and the size is huge. Step-by-step changesets
are available from GNU arch archive:
archive URL:
http://gyvern.ipc.hiroshima-u.ac.jp/~mpsuzuki/arch-2009.01.03/
version name:
freetype2--mps-carbon-free--0.2.1.5
EXPERIMENTAL RESULT
===================
I measured the memory usages of ftdump, for 3 font files:
Courier New (suitcase including 2 sfnts, but FOND declares 4 faces)
Courier.dfont (dfont including 2 sfnts, FOND declares 2 faces)
Impact (suitcase including 1 sfnt, FOND declares 1 face)
Memory usage is measured by FreeType2's internal memory debugger.
The binary is built by
-Wall -Wextra \
-ggdb -g3 -O0 -fkeep-inline-functions \
-DFT_DEBUG_MEMORY=7 -DFT_DEBUG_LEVEL_ERROR=7 -DFT_DEBUG_LEVEL_TRACE=7
on Mac OS X 10.4 running on PowerPC. In following tables,
* old-carbon is a binary built with ftmac.c
* old-posix is a binary built without ftmac.c
* new-posix is a binary using partial stream and without ftmac.c
----------------------------------------------------------------------------
total memory allocations
old-carbon old-posix new-posix
Courier New 501094 515678 104941
Courier.dfont 887500 555297 95248
Impact 153073 153927 63853
----------------------------------------------------------------------------
maximum memory footprint
old-carbon old-posix new-posix
Courier New 120903 121061 32782
Courier.dfont 193374 193414 34592
Impact 85967 86131 37304
----------------------------------------------------------------------------
SUMMARY
=======
It is clearly shown that the partial stream can reduce
the footprint in FT_New_Face(). Even if the resource
fork have single sfnt and no-sfnt-bitmap, the footprint
is reduced about half. This is reasonable that the resource
fork size is almost same with sfnt, and the maximum foot-
print would be occur when current resource-fork accessor
is buffering from whole copy of resource-fork image to the
sfnt image only. However, I expected that using file stream
instead of memory image can reduce the footprint further
(e.g. in the case of partial stream, whole of glyf table
is not loaded onto the memory), it was not achieved.
FUTURE ISSUES
=============
There is similar issue in LaserWriter PostScript font.
In the POST resource of LW font, a PFB font is splitted
into small segments. Current implementation makes a
memory image of completed PFB by copying each segments.
Another new stream collecting the offsets and length
to each segment can reduce the memory allocation to
load LW font.
20091019a.patch.bz2
Description: Binary data
- [ft-devel] reduce memory allocation & maximum footprint of FT2 POSIX resource fork accessor, by "partial stream",
mpsuzuki <=