Closed Bug 213753 Opened 21 years ago Closed 21 years ago

[ps] Runtime bloat in gfx/src/ps/nsPostScriptObj.cpp from fprintf()s

Categories

(Core :: Printing: Output, defect)

defect
Not set
minor

Tracking

()

RESOLVED FIXED

People

(Reporter: kherron+mozilla, Assigned: kherron+mozilla)

Details

(Keywords: memory-footprint)

Attachments

(2 files)

nsPostScriptObj.cpp contains a sequence of over 1300 calls to fprintf() each
printing a one-line string. This contributes around 32k to the runtime code size
on linux (about 45k on solaris) contrasted with using a single fprintf() call.
This patch converts the entire block to a single |fprintf| call. The compiler
concats the individual strings into one big string.

This saves about 32k of text on linux (gcc 3.2):

  > size libgfx* nsPost*
     text	   data     bss     dec     hex filename
   265206	 217300      44  482550   75cf6 libgfxps.so
   299447	 217328      44  516819   7e2d3 libgfxps.so.old
    60862	   2032       8   62902    f5b6 nsPostScriptObj.o
    92644	   2032       8   94684   171dc nsPostScriptObj.o.old

and about 45k on solaris:

  > size libgfx* nsPostScriptObj.o*   
  libgfxps.so: 145320 + 233464 + 64 = 378848
  libgfxps.so.old: 190960 + 238440 + 64 = 429464
  nsPostScriptObj.o: 91887 + 2688 + 8 = 94583
  nsPostScriptObj.o.old: 199262 + 2688 + 8 = 201958

On my fairly wimpy solaris box, it also reduces the time to compile this file
from 8m15s using 120Mb to 0m20s using 20Mb.
Comment on attachment 128443 [details] [diff] [review]
Convert to a single |fprintf| call.

I was asked in IRC how I produced the patch. It was basically three vi s//
operations. From memory:

s/^  fprintf(f, /    /
s/); *$//
s/\%\%/\%/g

i.e. fix up the beginning of each line, fix up the end, and un-double the %
characters. A couple of lines had to be cleaned up by hand because they didn't
match the first pattern properly.

The |fprintf| calls in question are all printing constant strings with no
variable values involved. Before making the change I printed a test page to a
file. After making the change I reprinted the page and compared the resulting
PS; there was no difference other than a timestamp which appears at the bottom
of each page.
Attachment #128443 - Flags: superreview?(bzbarsky)
Attachment #128443 - Flags: review?(timeless)
Comment on attachment 128443 [details] [diff] [review]
Convert to a single |fprintf| call.

sr=bzbarsky; once this has review, let me know and I can land it.
Attachment #128443 - Flags: superreview?(bzbarsky) → superreview+
Summary: Runtime bloat in gfx/src/ps/nsPostScriptObj.cpp from fprintf()s → [ps] Runtime bloat in gfx/src/ps/nsPostScriptObj.cpp from fprintf()s
At about line 610 you end up with 
  fprintf(f, "%s", huge_string);
Wouldn't
  fputs (huge_string, f);
be faster? There are actually lots of other places where this also 
occurs.
I wrote a quickie benchmark that tests four different methods of writing a big
string and got results like the following:

fprintf(f, String)      17400876
fprintf(f, %s, String)  2297751
fputs(String, f)        2482240
fwrite(String, sizeof, sizeof, f)       115029

fprintf(f, String)      15610071
fprintf(f, %s, String)  3248828
fputs(String, f)        2884262
fwrite(String, sizeof, sizeof, f)       154088

fprintf(f, String)      16031274
fprintf(f, %s, String)  2507793
fputs(String, f)        2234064
fwrite(String, sizeof, sizeof, f)       115137

fprintf(f, String)      14915339
fprintf(f, %s, String)  2595806
fputs(String, f)        2410765
fwrite(String, sizeof, sizeof, f)       153214

Those numbers are time in microseconds to do the operation 100,000 times writing
to /dev/null. As you can see there's a big advantage going from

    fprintf(f, string)
to
    fprintf(f, "%s", string)

and another big advantage going to |fwrite|. |fprintf| vs. |fputs| tends to
favor |fputs| but the results aren't consistent.

That said, this code only runs once per document printed, so optimizing for
speed isn't the first priority. I used fprintf because I felt that

    fputs(
        [1314 lines]
        , f)

was bad style. fwrite is worse in this regard.
Comment on attachment 128443 [details] [diff] [review]
Convert to a single |fprintf| call.

r=dbaron if you do a before-patch and after-patch printing tests, diff the two
resulting postscript files, and make sure they're the same.  (I didn't look too
closely.  My eyes would glaze over.)
Attachment #128443 - Flags: review?(timeless) → review+
Clearly, people aren't comfortable that the patch contains no mistakes. So I've
written this perl script to perform the change. You can run it as:

    perl ./convert-it nsPostScriptObj.cpp > new-file	# Writes to stdout
    perl -i.bak ./convert-it nsPostScriptObj.cpp	# Update in place

When run as above, the script will convert the block of code to a single
fprintf() call, identical to the patch. Alternately, you can add the switch
"-fwrite":

    perl ./convert-it -fwrite nsPostScriptObj.cpp > new-file

In this case it will convert the code to the form:

  static const char psBoilerplate[] = {
    "/rhc {\n"
    "	 {\n"
    "	     currentfile read {\n"
    [etc.]
    "  /unicodeshow2 { real_unicodeshow_native } bind def\n"
    "} bind def\n"

    "\n"
  };
  fwrite(psBoilerplate, 1, sizeof(psBoilerplate) - 1, f);

As noted in a previous comment, fwrite() is faster than fprintf() (though
again, this code only executes once per print job).

Whether using the patch or the perl script, the generated postscript is
identical. The following files were produced by printing the google home page
to a file using various versions of mozilla:

$ ls -l google*
-rw-r--r--    1 kherron  kherron    227780 Sep 20 13:01 google-orig.ps
-rw-r--r--    1 kherron  kherron    227780 Sep 20 13:05 google-patch.ps
-rw-r--r--    1 kherron  kherron    227780 Sep 20 13:12 google-perl-fprintf.ps
-rw-r--r--    1 kherron  kherron    227780 Sep 20 13:17 google-perl-fwrite.ps

$ md5sum google*
45c830db9ed9a39ec4567db4cb871118  google-orig.ps
45c830db9ed9a39ec4567db4cb871118  google-patch.ps
45c830db9ed9a39ec4567db4cb871118  google-perl-fprintf.ps
45c830db9ed9a39ec4567db4cb871118  google-perl-fwrite.ps

Mozilla print jobs normally contain the date/time in the margin; to avoid this
I went into File->Page Setup->Margins, then set the right footer to "blank".
->kjh
Assignee: printing → kjh-5727
Fix checked in to trunk, 2003-09-20 14:27 -0700.
Status: NEW → RESOLVED
Closed: 21 years ago
Resolution: --- → FIXED
  libgfxps.so
  	Total:	     -22736 (+0/-22736)
  	Code:	     -21040 (+0/-21040)
  	Data:	      -1696 (+0/-1696)
  	      -1696 (+0/-1696)	R (DATA)
  		      -1696 (+0/-1696)	UNDEF:libgfxps.so:R
  			        -20	kCharsetConverterManagerCID
  			      -1676	kFCSCID
  	     -21040 (+0/-21040)	T (CODE)
  		     -21040 (+0/-21040)	UNDEF:libgfxps.so:T
  			         -8	global constructors keyed to
                                            nsRenderingContextImpl::gBackbuffer
  			     -21032	nsPostScriptObj::begin_document(void)
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Creator:
Created:
Updated:
Size: