On Wednesday, May 22, 2019 at 5:58:02 AM UTC,
rug...@gmail.com wrote:
> Hi,
>
> On Tuesday, May 21, 2019 at 5:18:36 PM UTC-5, S. Sokolow wrote:
> > On Tuesday, May 21, 2019 at 4:07:14 PM UTC,
rug...@gmail.com wrote:
> >
> > *nod* Next time I do a Free Pascal project, I'll leverage the
> > "exhaustively test combinations of build flags to minimize output
> > size" helper that I plan to write for this project.
>
> Too much effort for too little gain. Seriously, in my (limited)
> experience, it makes extremely little difference. Just stick to -O3.
You overestimate how much effort it is.
To prove that, here's an implementation of it that took me 5-10 minutes.
...admittedly, untested to the point where I haven't even checked for syntax errors. Just a quick copy-paste of a helper function I had in another project and the rest from memory.
The only reason I hadn't written it already is that, with `wmake test` still on my TODO list, it wasn't useful yet and this is the first project I've done where I'm optimizing for size.
#!/usr/bin/env python3
import os, subprocess
from itertools import chain, combinations
# Command used to build the binary to test for size
BUILD_CMD = ['wmake', 'all']
# Compiler command and flags which should always be used
# (eg. warning level, target CPU, memory model, etc.)
REQUIRED_FLAGS = ['-q', '-0', '-ms', '-zpw', '-wx', '@$(src)hello.lnk']
# Command to be evaluated for size
BINARY_NAME = 'install.exe'
# Command to be run to verify that the flag combo doesn't cause breakages
TEST_CMD = ['wmake', 'test']
# Optimizer settings which should be tested
OPTIONAL_FLAGS = ['-ei', '-ob', '-oe', '-oh', '-oi', '-ok', '-ol', '-on',
'-or', '-os', '-wx', '-zm']
# Already-written function from another project of mine
def powerset(iterable): # type: (Iterable[Any]) -> Iterator[Sequence[Any]]
"""C{powerset([1,2,3])} --> C{() (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)}
@rtype: Iterable
"""
i = list(iterable)
return chain.from_iterable(combinations(i, j) for j in range(len(i) + 1))
smallest_size, smallest_flags = None, None
for flag_set in powerset(OPTIONAL_FLAGS):
flag_str = 'WCLFLAGS=' + ' '.join(flag_set)
try:
try:
subprocess.check_call(BUILD_CMD + [flag_str])
except subprocess.CalledProcessError:
continue
# Don't bother to test files unless they're smaller than the best known
size = os.stat(BINARY_NAME).st_size
if smallest_size and size >= smallest_size:
continue
# Run the test suite to verify that these flags don't break the build
# in some way
try:
subprocess.check_call(TEST_CMD + [flag_str])
except subprocess.CalledProcessError:
continue
smallest_size = size
smallest_flags = flag_set
finally:
subprocess.call(['wmake', 'clean'])
print("Smallest binary size achieved: %s" % smallest_size)
print("...using compiler flags: %r" % smallest_flags)
> -Ctrio is valid, I think. Maybe obscure ones like -Sgo too, can't
> quite remember.
> fpc.cfg has some defaults worth checking, too.
Thanks. I'll make a note to check those out.
> (The TUI ide, fp.exe, can use .chm for online help. Not perfect but
> better than nothing. Plain text versions are also good for reference.)
Oh, yeah. I remembered seeing a CHM unit but it never really hit me that it could be used to write a DPMI-based DOS CHM viewer.
> Sometimes it's better to specify in source rather than accidentally
> relying on someone knowing correct switches to manually use.
> Flexibility can be both a blessing and a curse.
*nod* My policy is to put the stuff which alters the behaviour of the code in the source and the stuff which merely alters optimization settings in the Makefile.
> > (eg. The "Size Matters" wiki page itself points out
>
> Written by a guy (nice and smart ... but very critical) who abhors
> the whole idea of size savings. Granted, he likes smartlinking,
> but beyond that, he couldn't care less.
Interesting to know, but I only gave it as an example. The flags I chose were the result of reading every bit of info I could find on how to size-optimize Free Pascal output, including looking through the list of compiler flags and directives for any that looked promising.
> Disclaimer: I know nothing of Delphi dialect. But sysutils
> is mostly for that alone. So you can (and should!) do without.
> It's also mandatory for exceptions, IIRC, which do add a
> small size and speed penalty (but most Delphi users always
> use it anyways). Dynamic arrays also use exceptions behind
> the scenes.
>
> Do keep in mind that FPC supports several dialects (e.g. "tp"),
> and you can use different dialects for different modules / units.
> (IIRC, default "fpc" dialect allows function overloading and
> structured function returns, unlike "tp".)
I think the unzip code I'd been planning to use required exceptions but I never got that far before I decided to switch to Open Watcom C/C++, so I can't be certain.
> Not really, no. But it can be cool, in theory, to have 186 or 286
> routines (or even 386 or 686). If it makes a noticeable difference,
> of course.
No argument there.
> But the 8088 is slower than the 8086, and even those are way
> slower than an actual 286. Something about effective addressing
> and ALU, prefetch queue, clock speed, and more complications
> (jumps are somewhat costly). Honestly, I wouldn't worry about
> it *at all* until you've done everything else (if even then).
> But I'm far from an expert in this.
Hey, I just grabbed the
MIPS.COM benchmark suggested on the DOSBox Wiki for figuring out how to match DOSBox to a specific performance point and turned the cycles down until the fastest benchmark matched the slowest reference example it offered.
I may reconsider my target later if need be, but it's currently an enjoyable challenge to hold myself to appealing performance under those conditions, when I spend most of my coding time writing stuff in Python that remains solidly I/O-bound and "good enough" without me even trying as long as I pick a sane algorithm.
> > The Minimum Viable Product won't use compression at all and,
> > when I add support for Zip, I'll start with a Store-only
> > implementation since the first goal of that is to make
> > single-file installers.
>
> Would this help? (Probably not, but ....)
>
> *
ftp://ftp.freepascal.org/pub/fpc/contrib/zfs210.zip
The attributions say it ported the same LGPLed Info-ZIP decompression code that I was planning to use.
Aside from that, it's technically illegal to use it for *anything* because the author added additional requirements beyond the terms of the LGPL license that came with the Info-ZIP routines, which means you have to satisfy both those additional requirements and the clause in the LGPL which says you are forbidden from doing so.
(Modern versions of the GPL and LGPL resolve this by including a clause which specifically says that, if the author is ignorant enough to impose conditions beyond those specified in the license text, they don't have legal force and you can ignore them.)
> Actually, ZLIB might have some sample code (miniunz?).
The unzip.c from miniunz which handles the archive structure is based on Info-ZIP code, so this clause still applies:
Redistributions in binary form (compiled executables and libraries) must reproduce the above copyright notice, definition, disclaimer, and this list of conditions in documentation and/or other materials provided with the distribution. Additional documentation is not needed for executables where a command line license option provides these and a note regarding this option is in the executable's startup banner. The sole exception to this condition is redistribution of a standard UnZipSFX binary (including SFXWiz) as part of a self-extracting archive; that is permitted without inclusion of this license, as long as the normal SFX banner has not been removed from the binary or disabled.
You can see why I'm planning to try to contact them about that. It's not really very suited to something like an installer creation kit, where I don't want to force a startup banner on users. It'd feel like a shareware nag screen.
I suspect, however, that I'll have to prototype with a binary that uses Info-ZIP code and then un-cripple my creation by writing my own from-scratch equivalent to miniunz if I feel that it's making the program too big.
That'd be perfect except that tar is a streaming archival format and I need one where it's easy to unpack arbitrary files without unpacking the whole thing.
My clever plan to avoid having to implement my own structured data store for the automation scripting is "I already need filesystem and/or archive manipulation routines, and both of those are hierarchical key-value stores when you think about it."
(ie. Similar to Mojosetup for Linux, I want to have a "scripts" folder in the self-extractor. However, here, to save on startup time, memory, and/or temp space on disk, the self-extractor stub will decompress the the individual pseudo-batch files on demand.)
To get the kind of random access I want, I'd need to individually gzip each file I wanted to be able to extract individually, put them inside an UNcompressed tar file, and then build a seeking index comparable to a Zip file's central directory on startup.
That's not a standard or familiar way to do it and the whole point of using Zip is to make it easy to build an installer using minimal custom tooling.
(With my current concept, all you need is to write your scripting using any text editor with batch file syntax highlighting, zip everything up using the default "maximum compatibility" options, and either specify that the installer stub be used as a custom SFX stub or, if that's not an option, concatenate them together and then use `zip -A` from Info-ZIP to fix up the Zip file's offsets.)
Also a nice option, but ZOO is an even more esoteric format these days so, again, I'd still like to try for Zip first.