blob: 7c6ec2943ef972eb6f7e50e2d5d56a6d1a4de87a [file] [log] [blame]
.ll 6i
.nr PO 1.15i
.nr HM 0i
.nr FM 1.05i
.TL
SPIFF -- A Program for Making Controlled Approximate Comparisons of Files
.AU
Daniel Nachbar
.AI
Software Engineering Research Group
Bell Communications Research
Morristown, New Jersey
.AB
The well known program
.B
diff
.R
[1]
is inappropriate for some
common tasks such as comparing the output of floating
point calculations where roundoff errors
lead
.B
diff
.R
astray and comparing program source code
where some differences in the text (such as white space and comments)
have no effect on the operation of the compiled code. A new program,
named
.B
spiff,
.R
addresses these and other similar cases
by lexical parsing of the input files and then applying
a differencing algorithm to the token sequences.
.B
Spiff
.R
ignores differences
between floating point numbers that are below a user settable tolerance.
Other features include user settable commenting and literal string
conventions and a choice of differencing algorithm.
There is also an interactive mode wherein the input texts are displayed
with differences highlighted. The user can change numeric tolerances
"on the fly" and
.B
spiff
.R
will adjust the highlighting accordingly.
.AE
.SH
Some Troubles With Diff
.PP
Over the past several years, it has been fairly easy to tell when
a new type of computer arrived at a nearby computer center.
The best clue was the discordant chorus of
groaning, sighing, gnashing of teeth, pounding of foreheads on desks,
and other sounds of distress. Tracing these noises to their source, one
would find some poor soul in the process of installing
a numerical analysis package on the new machine.
.PP
One might expect that "moving up" to a new machine
would be a cause for celebration.
After all, new machines are typically bigger, faster,
and better than old machines.
However, the floating point arithmetic on any new machine is frequently
slightly different from any old machine.
As a consequence,
software package test routines produce output that is slightly different,
but still correct, on the new machines.
Serious troubles appear when the person installing the software package
attempts to compare the test output files from two different machines
by using a difference finding program such as
.B
diff.
.R
Programs such as
.B
diff
.R
do a character by character comparison.
.B
Diff
.R
finds a great many differences, most of which
are due to roundoff errors in the least significant digits of floating point
numbers. Others are the result of differences in the way
in which the two test runs
had printed a number (3.4e-1 vs. 0.34).
In one case, the test suite for the S statistical analysis package[2],
over 1700 floating point numbers are produced
(per machine). In the eyes of
.B
diff,
.R
roughly 1200 of these numbers are different.
However, none of the "differences" are important ones.
Nonetheless, software installers wind up inspecting the output by eye.
.PP
A similar problem arises when one attempts to
look for differences between two versions
of the same C program.
.B
Diff
.R
reports many differences that are not of interest. In
particular, white space (except inside quotation marks) and
anything inside a comment have no effect on the operation of the compiled
program and are usually not of interest.
.B
Diff
.R
does have a mode of operation where white space
within a line (spaces and tabs) can be ignored.
However, differences in the placement of newlines cannot be ignored.
This is particularly annoying since C programming
styles differ on whether to place a newline character before or after the '{'
characters that start blocks.
.SH
The Problem in General Terms
.PP
As already mentioned, programs such as
.B
diff
.R
do
a character-by-character comparison of the input files.
However, when it comes to interpreting
the contents of a file (either by a human or by a program)
it is almost never the case that characters
are treated individually. Rather, characters make up tokens such
as words and numbers, or act as separators between these tokens.
When comparing files, one is usually looking for
differences between these tokens, not the characters that make them up
or the characters that separate them.
.PP
What is needed is a program that first parses the input files
into tokens, and then applies a differencing algorithm to the token
sequences.
In addition to finding differences in terms of tokens,
it is possible to interpret the tokens and
compare different types of tokens in different ways. Numbers, for example,
can differ by a lot or a little.\**
.FS
Current differencing programs do not have such a notion because
the difference between two characters is a binary function.
Two characters are the same or they are not.
.FE
It is possible to use a tolerance when comparing two number tokens and
report only those differences that exceed the tolerance.
.SH
Design Issues
.PP
A serious design issue for such a program is how
complex to make the parse. The
.I
deeper
.R
one goes in the parsing the larger
the unit of text that can be manipulated. For instance, if one is looking
for differences in C code, a complete parse tree can be produced and
the differencing algorithm could examine insertion and deletion of entire
branches of the tree. However, deep parsing requires much more
complex parsing and slower differencing algorithms.
.PP
Another design issue is deciding how to interpret the tokens.
Closer interpretation may lead to greater flexibility in comparing tokens, but
also results in a more cumbersome and error-prone implementation.
.PP
In the program described here, we attempt to keep both the depth
of the parse and the semantics of the tokens to a minimum.
The parse is a simple
lexical parse with the input files broken up into one dimensional
sequences of numbers, literal strings and white space.
Literal strings and white space are not interpreted. Numbers
are treated as representing points on the real number line.
.SH
Default Operation
.PP
.B
Spiff\**
.R
.FS
We picked the name as a way to pay a small tribute to that famous intergalactic
adventurer Spaceman Spiff[3].
.B
Spiff
.R
is also a contraction of "spiffy diff".
.FE
works very much like
.B
diff.
.R
It reads two files, looks
for differences, and prints a listing of the
differences in the form of
an edit script.\**
.FS
An edit script is a sequence of insertions and deletions
that will transform the first file into the second.
.FE
As already suggested,
.B
spiff
.R
parses the files into
literal strings and real numbers.
The definition of these tokens can be altered somewhat by the user
(more on this later). For now, suffice it
to say that literals are strings like "cow", "sit",
"into", etc. Real numbers look like "1.3", "1.6e-4" and so on.
All of the common formats for real numbers are recognized.
The only requirements for a string to be
treated as a real number is the presence
of a period and at least one digit.
By default, a string of digits without a decimal point
(such as "1988") is not considered to be a real number,
but rather a literal string.\**
Each non-alphanumeric character (such as #$@^&*)
is parsed into a separate literal token.
.FS
Integer numbers are often used as indices, labels, and so on.
Under these circumstances, it is more appropriate to treat them as literals.
Our choice of default was driven by a design goal
of having
.B
spiff
.R
be very conservative
when choosing to ignore differences.
.FE
.PP
Once
.B
spiff
.R
determines the two sequences of tokens,
it compares members of the first sequence with
members of the second sequence.
If two tokens are of different types,
.B
spiff
.R
deems them to be different, regardless of their content.
If both tokens are literal tokens,
.B
spiff
.R
will deem them
to be different if any of their characters differ.
When comparing two real numbers,
.B
spiff
.R
will deem them to be different only if
the difference in their values exceeds a user settable tolerance.
.SH
Altering Spiff's Operation
.PP
To make
.B
spiff
.R
more generally useful, the user can control:
.IP \(bu
how text strings are parsed into tokens
.IP \(bu
how tokens of the same type are compared
.IP \(bu
the choice of differencing algorithm used
.IP \(bu
and the granularity of edit considered by the differencing algorithm.
.LP
.PP
These features are described next.
.SH
Altering the Parse
.PP
The operation of the parser can be altered in several ways.
The user can specify that delimited sections of text are to be ignored
completely. This is useful for selectively ignoring the contents of
comments in programs. Similarly, the user can specify that
delimited sections of text (including white space)
be treated as a single literal token. So, literal strings in program
text can be treated appropriately.
Multiple sets of
delimiters may be specified at once (to handle cases such as the
Modula-2 programming language
where there are two ways to specify quoted strings). At present,
the delimiters must be fixed string (possibly restricted to the
beginning of the line) or end of line.
As a consequence of the mechanism for specifying literal strings,
multicharacter operators (such as the += operator in C)
can be parsed into a single token.
.PP
As yet, no provision is made for allowing delimiter
specification in terms of regular expressions. This omission
was made for the sake of simplifying the parser.
Nothing prevents the addition of regular expressions in the
future. However, the simple mechanism
already in place handles the literal string and commenting conventions
for most well known programming languages.\**
.FS
See the manual page in the appendix for examples of handling
C, Bourne Shell, Fortran, Lisp, Pascal, and Modula-2. The only
cases that are known not to work are comments in BASIC and
Hollerith strings in Fortran.
.FE
.PP
In addition to controlling literal string and comments, the user
may also specify whether to treat white space characters as any other
non-alphanumeric character (in other words, parse each white space
character into its own literal token),
whether to parse sign markers as part
of the number that they precede or as separate tokens, whether
to treat numbers without printed decimal markers (e.g. "1988")
as real numbers rather than as literal strings, and whether
to parse real numbers into literal tokens.
.SH
Altering the Comparison of Individual Tokens
.PP
As mentioned earlier, the user can set a tolerance below which differences
between real numbers are ignored.
.B
Spiff
.R
allows two kinds of tolerances:
absolute and relative.
Specifying an absolute tolerance will cause
.B
spiff
.R
to ignore differences
that are less than the specified value.
For instance, specifying an absolute tolerance of 0.01 will
cause only those differences greater than or equal to 0.01 to be reported.
Specifying a relative tolerance will cause
.B
spiff
.R
to ignore differences that are
smaller than some fraction of the number of larger magnitude.
Specifically, the value of the tolerance is interpreted
as a fraction of the larger (in absolute terms)
of the two floating point numbers being compared.
For example,
specifying a relative tolerance of 0.1
will cause the two floating point numbers 1.0 and 0.91 to be deemed within
tolerance. The numbers 1.0 and 0.9 will be outside the tolerance.
Absolute and relative tolerances can be OR'ed together. In fact,
the most effective way to ignore differences that are due to roundoff errors
in floating point calculations is to use both
a relative tolerance (to handle limits in precision) as well as an absolute
tolerance (to handle cases when one number is zero and the other number is
almost zero).\**
.FS
All numbers differ from zero by 100% of their magnitude. Thus, to handle
numbers that are near zero, one would have to specify a relative tolerance
of 100% which would be unreasonably large when both numbers are non-zero.
.FE
In addition, the user can specify an infinite tolerance. This is useful
for checking the format of output while ignoring the actual numbers
produced.
.SH
Altering the Differencing Algorithm
.PP
By default,
.B
spiff
.R
produces a minimal edit sequence (using the Miller/Myers differencing algorithm[4])
that will convert the first file into the second.
However, a minimal edit sequences is not always desirable.
For example, for the following two tables of numbers:
.DS
0.1 0.2 0.3 0.2 0.3 0.4
0.4 0.5 0.6 0.5 0.6 0.7
.DE
a minimal edit sequence to convert the table on
the left into the table on the right be to
would delete the first number (0.1) and insert 0.7 at the end.\**
.FS
The problem of having the elements of tables become misaligned when
the differencing algorithm is trying
to find a minimal number of edits can be reduced somewhat
by retaining newlines and not using tolerances.
Unfortunately, it does not go away.
.FE
Such a result, while logically correct, does not provide a good picture
of the differences between the two files.
In general, for text with a very definite structure (such as tables),
we may not want to consider insertions and deletions at all, but
only one-to-one changes.\**
.FS
A "change" can be expressed as one deletion and one insertion at the same
point in the text.
.FE
So, rather than look for a minimal edit script, we
merely want to compare each token in the first file with
the corresponding token in the second file.
.PP
The user can choose which differencing algorithm to use
(the default Miller/Myers or
the alternative one-to-one comparison)
based upon what is known about the input files. In general,
files produced mechanically
(such the output from test suites) have a very regular structure
and the one-to-one comparison works surprisingly well.
For files created by humans, the Miller/Myers
algorithm is more appropriate.
There is nothing in
.B
spiff's
.R
internal design that limits
the number of differencing algorithms that it can run.
Other differencing algorithms,
in particular the one used in
.B
diff,
.R
will probably be added later.
.SH
Altering the Granularity of the Edit Sequence
.PP
By default,
.B
spiff
.R
produces an edit sequence
in terms of insertions and deletions of individual tokens.
At times it may be more useful to
treat the contents of the files as tokens when looking for differences
but
express the edit script in terms of entire lines of the files rather
than individual tokens.\**
.FS
For instance, if one wants to have
.B
spiff
.R
produce output that can be fed into
the
.B
ed
.R
editor.
.FE
.B
Spiff
.R
provides a facility for restricting the edits to entire lines.
.SH
Treating Parts of the Files Differently
.PP
For complex input files, it is important that different parts of the
file be treated in different ways. In other words, it may be impossible
to find one set of parsing/differencing rules that work well for the
entire file.
.B
Spiff
.R
can differentiate between parts of the input files on two bases:
within a line and between lines.
Within a line, a different tolerance can be applied to each real number.
The tolerances are specified in terms of the ordinal position of the
numbers on the line (i.e. one tolerance is applied to the first real number
on each line, a different tolerance is applied to the second number on
each line, a third tolerance is applied to the third, and so on). If more
numbers appear on a line than there are tolerances specified, the last
tolerance is applied to all subsequent numbers on the line (i.e., if the user
specifies three tolerances, the third is applied to the third, fourth
fifth, . . . number on each line). This feature is useful for applying
different tolerances to the different columns of a table of numbers.
.PP
Between lines, the user can place "embedded commands" in the input files.
These commands
are instructions to parser that can change what tolerances are attached
to real numbers and the commenting and literal string conventions used by the
parser. Embedded commands are flagged to the parser
by starting the line with a user-specified
escape string. By combining within line and between line differentiation,
it is possible for the user to specify a different tolerance
for every single real number in the input files.
.SH
Visual Mode
.PP
So far,
.B
spiff's
.R
operation as an intelligent filter has been described.
.B
Spiff
.R
also has an interactive mode.
When operating in interactive mode,
.B
spiff
.R
places corresponding sections of the input files
side by side on user's screen.\**
.FS
Although the current implementation of
.B
spiff
.R
runs in many environments,
interactive mode works only under the MGR window manager.[5]
Other graphics interfaces will probably be added over time.
.FE
Tokens are compared using a one-to-one ordinal comparison, and any tokens that
are found to be different are highlighted in reverse video.
The user can interactively change the tolerances and
.B
spiff
.R
will alter the display
to reflect which real numbers exceed the new tolerances.
Other commands allow the user to page through the file and exit.
.SH
Performance
.PP
Two components of
.B
spiff,
.R
the parser and the differencing algorithm,
account for most of the execution time. Miller and Myers compare their
algorithm to the one used in the diff program. To restate their results,
the Miller/Myers algorithm is faster for files
that have relatively few differences but much
slower (quadratic time) for files with a great many differences.
.PP
For cases where the files do not differ greatly,
parsing the input files takes most of the time (around 80% of the total).\**
.FS
No effort has yet been made to make the parser run more quickly.
A faster parser could no doubt be written by generating a special state machine.
.FE
The performance of the parser is roughly similar to programs that do a similar
level of parsing (i.e. programs that must examine each character in the file).
For files where roughly half of the tokens are real numbers,
.B
spiff
.R
takes about twice as long to parse the input files
as an
.B
awk
.R
program that counts the number of words in a file:\**
.FS
For
.B
awk,
.R
a word is any string separated by white space.
.FE
.B
.DS
awk '{total += NF}' firstfile secondfile
.DE
.R
.PP
The time that it takes
.B
spiff
.R
to parse a file is substantially
increased if scanning is done for comments
and delimited literal strings. The precise effect depends upon the length of
the delimiters, whether they are restricted to appear at beginning of line, and
the frequency with which literals and comments appear in the input files.
As an example, adding the 12 literal conventions\**
.FS
One literal convention is for C literal strings. The rest enumerate multicharacter
operators.
.FE
and 1 commenting convention
required for C code roughly doubles the time required to parse input files.\**
.FS
So in total, it takes
.B
spiff
.R
about 4 times longer to parse a C program than it takes
.B
awk
.R
to count the number of words in the same file.
.FE
.PP
A more complete approach to evaluating
.B
spiff's
.R
performance must measure the total time that it takes for the user to complete a
differencing task. For example, consider one of the
test suites for the S statistical
analysis package mentioned at the beginning of this paper.
The output file for each machine is 427 lines long and contains
1090 floating point numbers. It takes
.B
diff
.R
approximately 2 seconds on one of our "6 MIPS"\** computers
.FS
We will not comment on the usefulness of "MIPS" as a measure
of computing speed. The numbers provided are only intended to
give the reader some vague idea of how fast these programs run.
.FE
to compare the two files and produce
an edit script that is 548 lines long containing 1003 "differences"
in the floating point numbers. It takes the average tester
5 minutes to print out the edit script and roughly 2 hours to examine
the output by hand to determine that the machines are, in fact,
both giving nearly identical answers. The total time needed is
2 hours 5 minutes and 2 seconds.
.PP
In contrast, it takes
.B
spiff
.R
approximately 6 seconds on one of our "6 MIPS" computers to
produce an output file that is 4 lines long.\**
.FS
The output would be zero length except that the output of the
.B
time
.R
command is built into the S tests.
The timing information could easily be ignored using
.B
spiff's
.R
embedded commands. But, as we shall see, it hardly seems worth the trouble.
.FE
It takes the average tester 30 seconds to examine
.B
spiff's
.R
output. The total for
.B
spiff
.R
is 36 seconds. Therefore for this case,
.B
spiff
.R
will get the job done roughly 208.88 times faster than
.B
diff.
.R
.PP
In general, it is misleading to compare
.B
spiff's
.R
speed with that of
.B
diff.
.R
While both programs are looking for differences between files,
they operate on very different types of data (tokens vs. bytes).
An analogous comparison could be made between the speed of an assembler
and the speed of a C compiler. They are both language translators.
One runs much faster than the other.
None the less, most programmers use the slower program
whenever possible.
.SH
Using Spiff For Making Regression Tests Of Software
.PP
We envision
.B
spiff
.R
to be the first of several tools for aiding in the now
arduous task of making regression tests.\**
.FS
In software engineering parlance, a "regression test" is the process by
which a tester checks to make sure that the new version of a piece of
software still performs the same way as the older versions
on overlapping tasks.
.FE
Given
.B
spiff's
.R
current capabilities, the regression test designer can
take the output of an older version of software and through
the use of literal string and commenting conventions,
specify what parts of the output must remain identical and
what sections can change completely. By specifying tolerances, the test
designer can take into account how much of a difference in floating
point calculations is acceptable.
.PP
The test designer is also free to
edit the output from the older version of the software and add embedded
commands that can instruct
.B
spiff
.R
to treat various parts of the output
differently. The newly edited output can then serve as a template for
the output of later versions of the software.
.PP
Obviously, editing output by hand is a very low level mechanism for adding
specification information. It is our intention that
.B
spiff
.R
will become
the last element in a pipeline of programs. Programs (as yet unwritten) located
earlier in the pipeline
can implement a higher level representation of the specification information.
They read in the old and new input files, add the appropriate embedded commands,
and then pass the results to
.B
spiff
.R
which will do the actual differencing.
.SH
Future Work
.PP
There are many features that could be added to
.B
spiff
.R
(if there are not
too many already). Some of these include:
.IP \(bu
Using separate differencing algorithms on separate sections of the file
and/or limiting the scope of an edit sequence (fencing)
.IP \(bu
Providing a more general mechanism for specifying comments and literals
(perhaps allowing specification in terms of regular expressions).
As yet, we have not encountered any important cases where regular expressions
have been needed. Until such a case is encountered, we will leave regular
expressions out in the name of simplicity.
.IP \(bu
Allowing for a more general specification of what lines should look like.
At present, the user can only specify tolerances for numbers as a function
of their ordinal position on a line. The difficulty in expanding the
specification abilities of
.B
spiff
.R
is knowing when to stop. In the extreme,
we might add all of the functionality of a program such as
.B
awk.\**
.R
.FS
Imagine handling the case such as
"apply this tolerance to all numbers that appear
on a line starting with the word `foo' but only if the number is between 1.9
and 3.6 and the word `bar' does not appear on the line".
.FE
We hope to keep
.B
spiff
.R
as simple as possible. Our first efforts in
this direction will try to implement higher level specification functions
outside of
.B
spiff.
.R
.SH
Acknowledgements
.PP
First and foremost, we thank Stu Feldman for his endless patience, constant encouragement
and numerous good ideas. We also extend thanks to Doug McIlroy for bringing the Miller/Myers
algorithm to our attention, Nat Howard for a key insight
and for his editorial comments
and Steve Uhler and Mike Bianchi for their editorial comments.
.SH
References
.IP [1]
Hunt,J.W. and M.D. McIlroy.
.I
An Algorithm For Differential File Comparisons,
.R
.B
Bell Labs Computer Science Technical Report,
.R
Number 41, 1975.
.IP [2]
Becker,R.A. and J.M. Chambers (1984).
.B
S \- An Interactive Environment For Data Analysis And
Graphics.
.R
Belmont, CA: Wadsworth Inc.
.IP [3]
Watterson, B. (1987).
.B
Calvin and Hobbes.
.R
New York: Andrews, McMeel & Parker.
.IP [4]
Miller, W. and E.W. Myers.
.I
A File Comparison Program,
.R
.B
Software \-
Practice and Experience
.R
15, 11, 1025-1040, 1985.
.IP [5]
Uhler, S.A.
.I
MGR -- A Window Manager For UNIX,
.R
Sun User's Group Meeting. September 1986.
.LP