freetype-devel
[Top][All Lists]
Advanced

[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.

Attachment: 20091019a.patch.bz2
Description: Binary data


reply via email to

[Prev in Thread] Current Thread [Next in Thread]