|
|
DataMuseum.dkPresents historical artifacts from the history of: RC4000/8000/9000 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about RC4000/8000/9000 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 75264 (0x12600)
Types: TextFile
Names: »mdsortpr6tx «
└─⟦01e83a425⟧ Bits:30008166 Bånd med SW8010 og SW8500 source code
└─⟦75ff9bef3⟧
└─⟦this⟧ »mdsortpr6tx «
mdsortproc jw 1/964e 091271.17
corrected in many ways 23.11.1973 by jw
changed with vnc and ordering of synonyms 18.08.1976 by jw
much changed in claimcalculation and strategy 7.1.1977 by jw
extended to handle sq files 17.07.1978 by ib.
trap specially for spill in key comparison 1.1.1979 by jw
assign af segm.no in headpart of sq-file 1.4.81 by eah
erroneous assisgn of segm.no in headpart of sq-file 1.4.81
var. 'passes' unitialized in 'select_workfile (situation=1) fb.1982.08.18
'segno' in filehead (sq) sometimes one too great fb.1982.09.02
claim procedure made monitor 9 compatible fb.1983.03.01
integer exception in select_work_file with large no of free segs fgs.1991.01.03
backing storage release 15.1 january 1988.
fb 1987.11.26 correction in claim procedure caused by the monitor procedures
changed handling of non fieled array parameters.
external
procedure mdsortproc(param, keydescr, names, eof, noofrecs,
result, explanation);
value eof;
real eof;
integer array param, keydescr;
real array names;
integer noofrecs, result, explanation;
comment
\f
Algol fortran standard procedure mdsortproc page 1
Purpose.
Mdsortproc, merge_disc_sorting_procedure, is a procedure,
intended for fast sorting of one backing storage area.
The procedure can be called from a program coded in algol or
fortran for RC 4000, RC 6000, and RC 8000.
Function.
The procedure sorts a backing storage file holding records of either
fixed or variable length, using backing storage throughout.
The basic sorting method, is the merge technique:
Sorted strings, as long as possible, are generated by internal sor-
ting during the first reading of the input file.
After that, these strings are merged repeatedly until only one sor-
ted string is left.
The procedure will try to minimize the sorting time by variation of
mergepowers, blocklengths, use of single- or double-buffering, and
by utilization of two disc-stores, if available.
The procedure needs in total a backing storage area of about twice
the size of the data to be sorted.
It can be specified that the input file shall be cleared, so its
area can be used for the merge.
The free core, when the procedure is called, must be more than
about 10000 halfwords, depending on the blocklengths and record-
lengths specified.
The value of 10000 is valid for blocklengths up to 2 segments.
The sort can use any amount of free core to speed up the sorting
and room for a work file on two different discs will reduce the
time for input output.
\f
Algol fortran standard procedure mdsortproc page 2
Call.
mdsortproc (param, keydescr, names, eof, noofrecs,
result, explanation)
param(1:7) (call value, integer array)
This array holds various parameters of type integer
and type boolean, describing the files and the
records.
param(1) segsperinblock.
Blocklength of the input file, given as a number
of segments. 1 <= segsperinblock <= 40.
Supplied by sq system if content = 21.
param(2) clearinput.
1: The input file is cleared, and its area can be
used for the merge.
0: The input file must not be cleared.
param(3) segsperoutblock.
Blocklength of the final output file, given as a
number of segments. 1 <= segsperoutblock <= 40.
If param(3) = 0 , segsperoutblock:= segsperinblock.
param(4) fixedlength.
1: Fixed recordlength. Inrec6/outrec6 are used.
0: Variable recordlength. Invar/outvar are used.
2: variable recordlength but no checksum.
param(5) maxlength.
The maximum length of variable length records, and
the length of fixed length records, measured in
halfwords.
Maxlength >= 2 and maxlengh <= segsperinblock * 512
and maxlength <= segsperoutblock * 512.
Maxlength must be even.
It is important for the efficiency of the sort
that maxlength reflects the real maximum length
of variable length records.
param(6) noofkeys.
The number of keyfields in the sorting key.
1 <= noofkeys <= maxlength and <= 169.
param(7) concerns the reaction on resource troubles.
0: Resource troubles will not stop the execution,
the procedure returns with result > 1.
<>0: Resource troubles causes runtime alarm.
\f
Algol fortran standard procedure mdsortproc page 3
keydescr(1: (call value, integer array)
noofkeys, 1:2) The description of the sorting key.
Keyfield n is specified as: +/- type, position,
in keydescr(n, 1:2).
The type ranges from 1 to 5, indicating: signed
halfword, integer, long, real, or abshalfword.
The sign of the type specifies the sequencing:
+ for ascending, and - for descending order.
The position of the keyfield is specified as the
number of the last halfword in the field, as for
algol field variables.
The position may not excede 2047.
The entire keyfield must be within a maximum length
record.
The length of variable length records must not be
less than the position of keyfield 1, the highest
priority keyfield.
records having equal values in all keyfields are
sorted according to their occurrence in the input
file, i.e. their mutual order is not changed.
In connection with very long records (maxlength=
param(5) >= 2046) the facility is switched off.
names(1:6) (call and return value, real array)
Contains 3 file and disc names.
names(1:2) inputfile.
The name of a backing storage area.
The procedure asumes that the size of the area re-
flects the amount of data to be sorted.
names(3:4) outputfile.
If names(3) = real<::> then the name of the output-
file is returned in names(3:4), otherwise the name
given is used for the final output file.
An existing file of this name on scope temp is
cleared without warning, just before the end.
The sort is not able to use the resources of such
a file.
names(5:6) outdisc.
If names(5) = real<::> then the output disc is
selected according to the most efficient strategy,
otherwise the disc specified is used.
\f
Algol fortran standard procedure mdsortproc page 4
eof (call value, real)
If the parameter noofrecs is negative, then the
end of the input file is indicated by a record
holding the bitpattern given by eof in the first
4 halfwords of the userpart. Halfword 1 to 4 in
case of fixed length and halfword 5 to 8 in case
of variable length records.
The final output file is terminated by an end
of file record of maximum length in this case.
noofrecs (call and return value, integer)
If noofrecs is non negative, then the number of
records in the input file is given by the value of
noofrecs and an eof record is not created.
The number of sorted records is returned in any
case in this parameter.
With an sq file noofrecs <= 0 means that noofrecs
is supplied by the sq system, noofrecs > 0 means
that only this number of records are to be sorted.
result (return value, integer)
The value of result specifies the result of the
call of the procedure.
In general, resource problems will yield a result
different from 1, whereas errors concerning the
parameters or hard errors will stop the execution
by a runtime alarm.
If param(7) <> 0 only result = 1 will occur, the
other results are transformed to alarms.
explanation (return value, integer)
The value of this parameter should give a further
explanation of result. See the next section.
Sq system files.
If the content field of the catalog entry of the input file
is equal to 21, it is supposed that the file conforms to the
conventions of the sq file system.
The output file will be created as an sq file as well.
Results.
result explanation comment
1 segments output the sort was ok
2 -lacking core halfwords not sufficient core
3 see alarm disc not sufficient backing storage
4 see alarm out disc backing store specified by names
(5:6) does not exist or has too
few resources.
Results > 1 are only given if param(7) = 0.
The parameter noofrecs will contain the number of sorted records
if result = 1 , otherwise it is unchanged.
The output file will, provided result = 1, be cut to the minimum
size, and tail(6) of the catalog entry will contain the same
value as noofrecs, if not sq file.
\f
Algol fortran standard procedure mdsortproc page 5
Requirements.
The available amount of core storage before the call of the proce-
dure must satisfy the condition:
free_half_words
> 7000
+ 512*(segsperinblock + segsperoutblock)
+ 4*maxlength + 24*noofkeys.
The procedure requires as working areas two disc files of the
size of the input file.
This means in the case when the input file is removed that
the procedure must be able to create one work file of that size.
If the input file is not removed the procedure must be able
to create two work files of the size of the input file.
If an output disc is specified this disc must be able to
hold the final output file.
already at the beginning of the procedure it is checked that
the output disc is capable of holding a file of the size of the
input file. (this is of course the case if the inputfile is
placed on the disc specified and has to be removed).
the blocking of records may be changed by the sorting, so the
output file may have a greater size than the input file.
Work files are kept at minimum size by concatenation of the
records without regards to block limits.
The procedure needs 2 catalog entries, 2 area processes, and
1 message buffer. So, the job process should at least be the owner
of 4 area processes, and 2 message buffers.
But it is recommended to have a greater number of message buffers,
(10 to 20), especially in the case of a sort of small records,
(about 2 to 20 halfwords), with great core size.
Variable length records.
The sum check facility of invar is used during the reading of the
input file if param(4) = 0 (invar with checksum control).
The record length must not excede maxlength.
The minimum record length is given by the greatest of the two val-
ues: 4 (if noofrecs < 0 then 8) and keydescr(1, 2).
Thus some of the keyfields of a short record may in fact be situ-
ated outside the record.
Such a record is sorted as if all the bits of keyfields outside
the record were equal to zero.
\f
Algol fortran standard procedure mdsortproc page 6
Alarms.
Parameter errors and hard file errors will stop the run with a run
time alarm.
alarmtext integer comment
param param number error at param(param number)
keyfield keyfield illegal position or type of keyfield
<integer>.
create monitor result abnormal result in call of the mon-
itor procedure create entry.
lookup monitor result abnormal result from lookup entry.
This alarm will normally indicate
that names(1:2) does not specify a
catalog entry.
change monitor result abnormal result in call of change
entry. Should not occur.
rename monitor result it is impossible to rename the final
output file to names(3:4).
remove monitor result abnormal result in call of remove
entry. The alarm will normally con-
cern the original input file.
infile tail(1) names(1:2) does not point to a catalog
entry describing an area.
r.length record length variable length record of a length
greater than maxlength, less than key-
descr(1, 2), or less than 8 if eof used.
passes 20 the sort could not be done in 20 mer-
ging passes. This alarm should never
occur.
reccount record count this is a hard error or a programming
error. The counts of records in the
first pass and the last, are not
equal, the last count is shown.
size -lacking halfwords not enough core.
disc -lacking entries too few entries in main catalog.
segments not enough segments, the value
of segments is the size of
one workfile.
out disc -1 the wanted output disc is not
mounted.
segments not room for one workfile on the
wanted output disc.
trap passnumber normally spill in key comparison.
nrecs sq noofrecs param noofrecs > records in sq file.
The alarms: size, disc and out disc (resources) will only be given
in case param(7) <> 0, otherwise the corresponding results, 2 to 4,
with explanation will be given.
In addition, index alarms may occur, if the parameter arrays are
incorrectly declared, and alarms from opensq or stderror may occur,
if file or record formats are illegal or in case of hard errors.
Alarms, with the exception of index alarms, are preceded by the
text, ***mdsortproc alarm:.
The alarm r.length, and stderror alarms occurring during the rea-
ding of the input file are also preceded by a line, specifying the
number of input records accepted before the error was detected.
\f
;
begin
boolean
fixedlength;
integer
bytes_per_inblock,
bytes_per_outblock,
content,
discstores,
freebytes,
i,
maxkeyposition,
maxlength,
maxlength_plus_four,
messbufsforinp,
min_segs_per_block,
noofkeys,
outbytes,
outsegment,
pass,
passes,
recs,
recsout,
segsin,
segsoutzero,
segsperinblock,
segsperoutblock,
shares,
shortclock,
strings1;
real time;
integer array
in_segs, merge_power, segsout(1:20);
real array
sortfiles(1:4);
comment -test-<
procedure printkey(i1, i2, r) -test-<
value i1, i2 -test-<
integer i1, i2 -test-<
real array r -test-<
begin
integer i, pos -test-<
boolean field bfld -test-<
integer field ifld -test-<
long field lfld -test-<
real field rfld -test-<
pos:= write(out, <:<10>key :>, << ddddd>, i1, i2, <:::>) -test-<
for i:= 3 step 2 until noofkeys*2+1 do
begin
if pos > 100 then
pos:= write(out, <:<10> :>) -test-<
bfld:= ifld:= lfld:= rfld:= keydescr(i+1) -test-<
pos:= pos + (case abs keydescr(i) of (
write(out, << -dddd>, r.bfld extract 12),
write(out, << -ddddddd>, r.ifld),
write(out, <<-dddddddddddd>, r.lfld),
write(out, <<-ddddddddddd>, r.rfld),
write(out, << -dddd>, r.bfld extract 12))) -test-<
end -test-<
write(out, <:<10>:>) -test-<
end printkey ;
\f
procedure print_rec(recno, rec);
value recno; integer recno; real array rec;
comment
prints recno and the keyfields of rec;
begin
integer i, pos;
boolean field bfld;
integer field ifld;
long field lfld;
real field rfld;
pos:= if pass = 0 then
write(out, <:<10>no::>, <<ddddddd>, recno, <: key::>)
else
write(out, <:<10>key::>);
for i:= 3 step 2 until noofkeys*2 + 1 do
begin
bfld:= ifld:= lfld:= rfld:= keydescr(i+1);
if pos > 62 then pos:= write(out, <:<10> :>);
pos:= pos +
(case (abs keydescr(i)) extract 2 + 1 of (
<* 4 *> write(out, <<-dddddddd.dddd'-dd>, rec.rfld),
<* 1,5 *> write(out, << dddd>, rec.bfld extract 12),
<* 2 *> write(out, << -ddddddd>, rec.ifld),
<* 3 *> write(out, << -ddddddddddddddd>, rec.lfld)));
end print keyfields;
write(out, <:<10>:>);
end print_rec;
procedure print_zsort(zsort);
zone zsort;
comment
prints the two last compared records in zsort.
the comparison must have been done by outsort or lifesort;
begin
integer i;
integer field recnumber;
integer array ia(1:20), bases(1:2);
recnumber:= maxlength + 2;
getzone6(zsort, ia);
system(5, ia(2) - 2, bases);
for i:= 1, 2 do
begin
ia(14):= bases(i); <* recordbase *>
setzone6(zsort, ia);
print_rec(zsort.recnumber, zsort);
end print the two records;
end print_zsort;
\f
procedure alarm(text, int);
value text, int;
integer text, int;
comment
this is the common alarm and error procedure.
it removes the workfiles.
the integer text selects the alarmtext, and the integer int
is the alarm integer.
if text >= 100 return is performed to docerror.
if text >= 12 and <= 14 return may be performed with result and
explanation by a jump to return_from_discsort, otherwise
system is called by a jump to the bottom of procedure
discsort in order to get a simple alarm address.
the string value of the text is transmitted in
sortfiles(1), and the integer in segsin.
;
begin
integer i;
for i:= 1, 3 do clear_file(sortfiles, i);
if text >= 12 and text <= 14 then
begin
comment return with result <> 1 depending on param(7);
if param(7) = 0 then
begin
result:= text - 10;
explanation:= int;
goto return_from_discsort;
end continuation wanted;
end text 12 to 14;
write(out, <:<10><10>***mdsortproc alarm:<10>:>);
if text = 9 or text = 100 then
write(out, <:<10> accepted records: :>, recs, <:<10>:>);
if text < 100 then
begin
comment not called from docerror;
sortfiles(1):= real (case text of(
<:<10>param :>,
<:<10>keyfield:>,
<:<10>create :>,
<:<10>lookup :>,
<:<10>change :>,
<:<10>rename :>,
<:<10>remove :>,
<:<10>infile :>,
<:<10>r.length:>,
<:<10>passes :>,
<:<10>reccount:>,
<:<10>size :>,
<:<10>disc :>,
<:<10>out disc:>,
<:<10>trap :>,
<:<10>nrecs sq:>));
segsin:= int;
comment -test-< system(9, segsin, string sortfiles(1));
goto alarmcall;
end not called from docerror;
end alarm;
\f
procedure clear_file(name, i);
value i; integer i; real array name;
begin
zone z(1, 1, stderror);
open(z, 0, string name(increase(i)), 0);
monitor(48, z, 0, in_segs); <* remove entry *>
end clear_file;
procedure docerror(z, s, b);
zone z;
integer s, b;
comment
the procedure calls alarm to have the sortfiles removed and
the alarm headline printed, and calls stderror;
begin
alarm(100 + pass, 0);
stderror(z, s, b);
end docerror;
\f
procedure endoffilerec(zout);
zone zout;
comment
note that this procedure finishes the sort by a jump to the
label return_from_disc_sort.
if the parameter noofrecs < 0, the procedure creates a
record of the maximum length holding the bitpattern given by eof
in the first 4 bytes of the userpart.
if content is not zero, the remaining part of the block, or per-
haps a new block is filled with integers of the value -1.
remember that the final output is super blocked
if made in the mergephase;
begin
integer i, maxlength, remaining, remaining_blocks, segments;
integer array ia(1:20);
integer field ifld;
real array field raf;
if recs <> recsout then alarm(11, recsout);
if noofrecs < 0 then
begin
comment create a maximum length eof record;
maxlength:= param(5); <* use the original maxlength *>
ifld:= outrec6(zout, 0) mod bytes_per_out_block;
if ifld < maxlength then
begin
outrec6(zout, ifld);
for ifld:= ifld step -2 until 2 do zout.ifld:= 0;
end not room for maxlength in current block;
outrec6(zout, maxlength);
for ifld:= 2 step 2 until maxlength do zout.ifld:= 0;
if fixedlength then zout(1):= eof
else
begin
ifld:= 2;
zout.ifld:= maxlength;
zout(2):= eof;
checkvar(zout);
end variable length;
end noofrecs < 0;
\f
comment find the size of the file;
for i:= 1 step 1 until 10 do ia(i):= 0; <* tail for changeentry *>
remaining:= outrec6(zout, 0);
getposition(zout, 0, segments);
if pass = 0 then segments:= segments + segs_per_outblock
else
begin
comment superblocking is used;
remaining_blocks:= (remaining - 2)//bytes_per_out_block;
remaining:= remaining - remaining_blocks * bytes_per_out_block;
segments:= segments + segs_out(pass) - remaining_blocks * segs_per_out_block;
end after merge;
if content <> 0 then
begin
comment
fill the remaining part according to bs and sq system;
outrec6(zout, remaining);
for ifld:= 2 step 2 until remaining do
zout.ifld:= -8388608;
ia(7):= segments // segs_per_out_block - 1; <* blocknumber *>
ia(8):= bytes_per_out_block - remaining;
ia(9):= content shift 12 add segs_per_out_block;
remaining:= 0;
if content = 21 then
begin
integer array field headpart;
outrec(zout, 0);
getposition (zout, 0, ia(7));
ia(7) := ia(7)-segs_per_out_block;
setposition(zout, 0, 0);
headpart:= 0;
invar(zout);
i:= zout.headpart(1);
setposition(zout, 0, 0);
swoprec6(zout, i);
headpart:= zout.headpart(3);
headpart:= zout.headpart(1);
resultsq:= 1;
ia(6):= shortclock:= systime(7, 0, time);
for i:= 2 step 1 until 7 do
zout.headpart(i):= case i -1 of
(1,
recs,
ia(7),
ia(8),
0,
shortclock
);
checkvar(zout);
ia(7):= ia(8):= 0;
end sq file;
end bs system;
close(zout, true);
ia(1):= explanation:= segments - remaining//512;
ia(if content = 21 then 10 else 6):= noofrecs:= recs;
i:= monitor(44, zout, 0, ia); <* change entry *>
if i <> 0 then alarm(5, i);
comment rename the file or return workname;
if names(3) <> real<::> then
begin
raf:= -2*4;
for i:= 3 step 1 until 4 do ia.raf(i):= names(i);
i:= monitor(46, zout, 0, ia); <* rename entry *>
if i = 3 <* the name exists already *> then
begin
clear_file(names, 3);
i:= monitor(46, zout, 0, ia); <* repeat rename *>
end i = 3;
if i <> 0 then alarm(6, i);
end rename
else
begin
comment return workname;
getzone6(zout, ia);
raf:= 2 - 2*4;
for i:= 3 step 1 until 4 do names(i):= ia.raf(i);
end return name;
result:= 1;
goto return_from_discsort;
end endoffilerec;
\f
procedure endoutstring(zout, out_base, block_size);
value block_size;
zone zout;
real array field out_base;
integer block_size;
comment
the procedure outrecs a string chaining record of 6 bytes,
pointing to the start of the current outstring.
the first 4 bytes contain the values of the variables outsegment
and outbytes, specifying the position of the preceding chain
record.
the last two bytes hold the value of recsout, specifying the number
of records in the current outstring.
outsegment and outbytes are updated to point to the new chain
record, and recsout is set to zero.
global quantities:
outsegment the segment number of the block holding the
last created string chaining record.
outbytes the number of bytes preceding the chain record
in this block.
recsout the number of records in the current outstring.
;
begin
integer i, segment;
integer field new_base;
getposition(zout, 0, segment);
new_base:= out_base;
for i:= outsegment, outbytes, recsout do
begin
if new_base >= block_size then
begin
outrec6(zout, block_size);
new_base:= 2;
end change output block
else
new_base:= new_base + 2;
zout.new_base:= i;
end for i;
comment -test-< write(out, <:<10>test 14 , outsegment, outbytes,:>,
<: recsout: :>, outsegment, outbytes, recsout);
comment set outsegment, outbytes, recsout for the new string;
outsegment:= out_base // 512 + segment;
outbytes := out_base mod 512;
recsout:= 0;
out_base:= new_base;
end endoutstring;
\f
procedure errorinoutfile(z, s, b);
zone z;
integer s, b;
comment
this procedure is used as the blockprocedure of the output zones.
if not end of document then docerror is called.
else alarm is called;
begin
if s shift (-18) extract 1 <> 1 then docerror(z, s, b);
alarm(13, segsin); <* may give result 3 *>
end errorinoutfile;
\f
procedure select_merge_strategy;
comment
this procedure selects the most efficient strategy for the merge
under the constraints chosen.
the quantities, which can be altered, are the number of passes, the
mergepowers and the blocklengths corresponding to each pass,
and the number of shares to be used for input/output.
important global quantities:
discstores if discstores=2 then the transfertime can be
cut to the half by the use of doublebuffering.
freebytes the amount of core available for buffers.
a reasonable amount of room for programsegments
and variables has been subtracted in advance.
passes at entry, the value of passes defines the pos-
sible numbers of passes:
0: any number of passes is possible.
1: only an odd number of passes is possible.
2: only an even number of passes is possible.
at return, passes contains the selected number
of merging passes.
recs the total number of records.
segsin the number of segments of the input file.
segsperoutblock blocklength of the final output file.
shares the selected number of shares for input/output.
mergepower(1:20) the selected mergepowers for the merge passes.
insegs(1:20) input blocklengths for merging passes.
segsout(1:20) output blocklengths for merging passes.
strings1 the number of strings generated in pass 0.
;
begin
integer test_passes, test_shares,
bytes_per_in_seg, bytes_per_power, bytes_per_out_seg,
first_pass, pass_step,
last_maxpower, maxpower;
real great, min_time, time, time_best_passes;
integer array test_mergepower, test_in_segs, test_segs_out(1:20);
\f
boolean procedure find_powers;
comment
given a specific number of testpasses, the string generation pass not
counted, this procedure will, if sensible, return a series of
test_mergepowers.
;
begin
integer i, last_maxstrings, maxstrings, p;
maxstrings:= p:= if test_passes > 1 then 1
else
if strings1 < last_maxpower then strings1
else last_maxpower;
for i:= 1 step 1 until test_passes do test_mergepower(i):= p;
find_powers:= true;
if maxstrings >= strings1 then goto return;
last_maxstrings:= 0;
for p:= p + 1 while last_maxstrings < maxstrings do
begin
last_maxstrings:= maxstrings;
for i:= 1 step 1 until test_passes do
if p <= (if i < test_passes then maxpower else last_maxpower) then
begin
test_merge_power(i):= p;
maxstrings:= maxstrings // (p-1) * p;
if maxstrings >= strings1 then goto return;
end mergepower can be increased to p;
end for p;
find_powers:= false;
return:
end find_powers;
\f
real
procedure segs_and_time;
comment
given a set of test_mergepowers, the procedure will find the
best blocklengths.
the procedure calculates the time needed for the merge, and if this
time is less than mintime, it is stored in mintime, and the
relevant values are saved in the global variables used to control
the merge.
the time calculation is based upon empirical data for the disc, as
well as for the program.
;
begin
integer i, max_buffer_bytes,
p, p_times_bytes_in, segments_in, segments_out,
test_segments_in, test_segments_out;
real time, disc_for_pass, disc_for_in, disc_for_out,
test_time,
accesstime, transfertime, cpu_for_pass;
\f
comment
define empirical time constants in milliseconds:
for the disc store the following approximation is used:
disctimepersegment = accesstime/segsperblock + transfertime.
if only 1 discstore or only 1 share is used, the times for input
and output are added, otherwise the maximum value is used;
accesstime := 30;
transfertime:= 25/12 <* 12 = some mean track length *>;
comment
if several disc store types are used, the time constants should
be set according to a device type in the external process
for the disc storage device.
it is very difficult to calculate the time used for the disc
in a realistic manner. it should f.ex. be taken into account
that the disc-controller may allow parallel head positioning,
but not parallel data transfers, also
the transfertime may be greater if shorter disc slices chained
together are physically spread over the disc.
the cpu time is estimated as a linear function of the number of
records and the number of segments.
the following time constants should not be quite unrealistic:
but the machinetype should be fetched from the monitor and the
timeconstants set according to the speed of the cpu.
cpu_per_record : 2.0
cpu_per_segment : 3.2
;
cpu_for_pass := recs * 2.0 + segsin * 3.2;
comment
in addition to the times mentioned above, 2 seconds are added per
merging pass, and for 2 shares 10 percent is added to total time
to cover a persumed interference between the discs and the cpu.
another reason for this punishment of double buffering has been
the fact that the total load of the machinery, i.e. the sum of
the individual working times of discs and cpu, is greater with
double buffering due to smaller blocklengths or more passes;
\f
time:= 0;
for i:= 1 step 1 until test_passes do
begin
comment set some usefull variables;
p:= test_merge_power(i);
p_times_bytes_in:= bytes_per_in_seg * p;
max_buffer_bytes:= free_bytes - bytes_per_power * p;
disc_for_pass:= great;
test_segments_in:= min_segs_per_block;
calc_segments_out:
test_segments_out:=
(max_buffer_bytes - test_segments_in * p_times_bytes_in)
// bytes_per_out_seg;
if i = test_passes <* the last pass *> then
testsegmentsout:=
(if content = 21 and testsegmentsout > segsperoutblock then
segsperoutblock else testsegmentsout)
// segs_per_out_block * segs_per_out_block;
if test_segments_out >= min_segs_per_block then
begin
comment calculate the disctime for 1 segment;
disc_for_in := access_time/test_segments_in + transfer_time;
disc_for_out:= access_time/test_segments_out + transfer_time;
test_time:=
if test_shares = 1 or discstores = 1 then
disc_for_in + disc_for_out
else
if disc_for_in > disc_for_out then disc_for_in
else
disc_for_out;
if disc_for_pass > test_time then
begin
disc_for_pass:= test_time;
segments_in := test_segments_in;
segments_out := test_segments_out;
test_segments_in:= test_segments_in + 1;
goto calc_segments_out;
end a better result;
end test_segments_out >= min_segs_per_block;
disc_for_pass:= disc_for_pass * segsin;
time:= (if test_shares = 1 then
disc_for_pass + cpu_for_pass
else
(if disc_for_pass > cpu_for_pass then disc_for_pass
else cpu_for_pass) * 1.10)
+ 2000.0 + time;
test_in_segs (i):= segments_in;
test_segs_out(i):= segments_out;
end for i;
\f
segs_and_time:= time;
if time < mintime then
begin
comment return the hitherto best values;
mintime:= time;
passes:= test_passes;
shares:= test_shares;
for i:= 1 step 1 until test_passes do
begin
merge_power(i):= test_merge_power(i);
in_segs (i):= test_in_segs (i);
segs_out (i):= test_segs_out (i);
end;
end a better result;
end segs_and_time;
\f
comment before pass 1.
strings1 contains the number of strings generated during
pass 0, recs the real number of records, segsin the minimum size
of the output file from the string generation;
bytes_per_power:= maxlength + 106;
<* 1 record + 8 bytes + zonedescriptor and 2 sharedescriptors *>
first_pass:= if passes = 2 then 2 else 1;
pass_step := if passes = 0 then 1 else 2;
min_time:= great:= '10;
for test_shares:= 1 step 1 until 2 do
begin
bytes_per_in_seg:= bytes_per_out_seg:= test_shares * 512;
i:= minsegsperblock * bytes_per_in_seg + bytes_per_power;
max_power := (free_bytes - minsegsperblock
* bytes_per_out_seg) // i;
last_maxpower:= (free_bytes - segs_per_out_block
* bytes_per_out_seg) // i;
if test_shares = 2 then
begin
if maxpower > messbufsforinp then
maxpower:= messbufsforinp;
if last_maxpower > messbufsforinp then
last_maxpower:= messbufsforinp;
end doublebuffering;
time_best_passes:= great;
for test_passes:= first_pass step pass_step
until 20 do
begin
if find_powers then
begin
comment ok;
time:= segs_and_time;
if time < time_best_passes then
time_best_passes:= time
else
goto change_shares;
end find_powers;
end for testpasses;
change_shares:
end for shares;
if mintime >= great then alarm(10, 20);
comment
this situation, that the sort can not be done in 20 passes, after
the checks performed in checkparam, must never occur, and should be
considered a program error;
comment -test-< write(out, <:<10> mintime: :>, <<ddd ddd.dd>,
mintime/1000);
end select_merge_strategy;
\f
procedure select_work_file(situation, infile);
value situation; integer situation;
real array infile;
comment
the procedure creates the workfile needed in the situation:
1. before pass 0, the stringgeneration.
the original inputfile and the final outputdisc is checked.
passes = 0 at calltime signals that the workfile should be
the final outputfile.
if that is impossible passes is set to 1 .
discstores is set to 1 or 2 depending on whether the input-
file and the workfile is placed on the same disc or not.
2. before pass 1, the first merging pass.
the return value of passes specifies the possible numbers
of merging passes:
0: any number is allowed (1, 2, 3, ... )
1: only odd numbers (1, 3, 5, ... )
2: only even numbers (2, 4, 6, ... )
discstores as for situation 1.
;
begin
boolean small_input_disc;
integer best_bsno, best_segments, best_weight, bsno, entries,
free_segs_on_input_disc,
i, input_bsno, main_bsno, max_loss_per_block, output_bsno,
safe_segments, segments, segs_on_input_disc,
slice_length, weight;
integer array ia(1:20), tail(1:10);
integer array field headpart;
long array bestbsname, bsname(1:2);
real array field areaname, discname;
zone z(128, 1, stderror);
\f
boolean procedure claim
(keyno,bsno,bsname,entries,segm,slicelength);
value keyno;
integer keyno,bsno,entries,segm,slicelength;
long array bsname;
begin
own boolean init,monold;
own integer bsdevices,firstbs,ownadr,mainbs;
integer i;
long array field name;
integer array core(1:18);
if -,init then
begin
init:=true;
system(5,64,core);
monold:= core(1) < 9 shift 12 + 0;
system(5,92,core);
bsdevices:=(core(3)-core(1))//2;
firstbs:=core(1);
begin
integer i;
integer array ntable (0:bsdevices);
integer array field iaf;
iaf:= -2;
system(5,firstbs,ntable.iaf);
while ntable(mainbs)<>core(4) do mainbs:=mainbs+1;
end;
ownadr:=system(6,i,bsname);
end;
if bsno=-1 then bsno:=mainbs else
if bsno<0 or bsno>=bsdevices
or keyno<>0 and keyno<>2 and keyno<>3 then
begin
claim:=false;
goto exitclaim
end;
claim:=true;
begin integer array nametable(1:bsdevices);
name:=18;
system(5,firstbs,nametable);
system(5,nametable(bsno+1)-36,core);
if core(10) = 0 then goto exitclaim;
bsname(1):=core.name(1); bsname(2):=core.name(2);
slicelength:=core(15);
system(5,ownadr+core(1),core);
if monold then
begin comment monitor version older than 9;
entries:=core(keyno+1) shift (-12);
segm:=core(keyno+1) extract 12 * slicelength;
end else
begin
entries:=core(keyno*2+1);
segm:= core(keyno*2+2)*slicelength;
end;
end;
if false then
begin
exitclaim:
entries:=segm:=slicelength:=0;
bsname(1):=bsname(2):=0;
end;
end claim;
\f
comment lookup the input file;
areaname:= discname:= 2;
getzone6(z, ia);
for i:= 1 step 1 until 2 do ia.areaname(i):= infile(i);
setzone6(z, ia);
i:= monitor(42, z, 0, tail); <* lookup entry *>
if i <> 0 then alarm(4, i);
if situation=1 then
begin
segsin := tail(1);
if segsin<0 then alarm(8, segsin); <* no area*>
safe_segments:= 2*segsin; <* the safe workfile size *>
end situation=1
else
if situation=2 then
begin
comment calculate the safe size of the workfiles;
i:= bytesperoutblock // 2;
max_loss_per_block:=
if fixedlength then bytesperoutblock mod maxlength
else
((if maxlength > i then i else maxlength) - 2);
safe_segments:=
round (segsin / (bytesperoutblock - max_loss_per_block)
* bytesperoutblock)
+ 1 + segsperoutblock;
end situation = 2;
\f
comment
comment now find the best disc for the workfile.
the selection is based upon the following priorities:
1: the final outputdisc if necessary or wanted.
2: a disc with safe_segments free segments.
3: the disc with most free segments.
4: the disc with the greatest slicelength.
5: a disc giving discstores = 2.
6: the final outputdisc.
7: the disc with the highest bsno.
;
main_bsno:= bsno:= best_weight:= input_bsno:= output_bsno:= -1;
<* pseudo call to assign main_bsno. i.e. bs device of maincatalog *>
claim(0, main_bsno, bsname, entries, segments, slicelength);
for bsno:= bsno + 1
while claim(0, bsno, bsname, entries, segments, slicelength) do
if slicelength > 0 <* kit mounted *> then
begin
if bsno = main_bsno then
begin
comment check the number of entries in main catalog;
i:= (if situation = 1 then param(2) else 1) + entries;
if i < 2 then alarm(13, i - 2);
end bsno = 0;
weight:= if slicelength >= 80 then 16 else slicelength//5;
comment check inputdisc and final outputdisc;
if real bsname(1) = tail.discname(1) and
real bsname(2) = tail.discname(2) then
begin
comment this is the disc of the input file;
input_bsno:= bsno;
segs_on_input_disc:=
(tail(1) + slicelength - 1)//slicelength*slicelength + segments;
small_input_disc:=
segs_on_input_disc//2//slicelength*slicelength < safe_segments;
free_segs_on_input_disc:= segments;
segments:=
(segs_on_input_disc - segsin)//slicelength*slicelength;
<* segments = max unused segments on input disc *>
end inputdisc
else
weight:= weight + 2;
comment include segments in the weight;
weight :=
(if segments >= safe_segments
or extend segments * 20 + weight >= 8688000 then
8000000
else
segments * 20) + weight;
\f
if real bsname(1) = names(5) and
real bsname(2) = names(6) then
begin
comment this is the disc of the final outputfile;
output_bsno:= bsno;
weight:= weight + 1; <* after all the file shall end there *>
if situation = 1 then
begin
comment check that there is room;
if input_bsno <> output_bsno or
param(2) = 0 <* the inputfile is not removed *> then
begin
comment there must be room now;
if segments < segsin then alarm(14, segsin);
if passes = 0 <* sort without merge wanted *> then
weight:= 8100000;
end room must be there now
else
<* input_bsno = output_bsno and param(2) <> 0 *>
begin
if passes = 0 and segments >= safe_segments <* safe *> then
weight:= 8100000 <* sort without merge possible *>
else
passes:= 1;
end perhaps room on outputdisc;
end situation = 1
else
<* situation = 2 *>
if input_bsno <> output_bsno <* it must be selected *> then
weight:= 8100000;
end disc for final output;
if best_weight <= weight then
begin
best_weight:= weight;
best_bsno := bsno;
best_segments:= segments;
for i:= 1 step 1 until 2 do best_bsname(i):= bsname(i);
end better or equal;
end for bsno;
\f
comment now the best disc has been selected;
comment -test-< write(out, <:<10>test 1, inpbs, outbs, bestbs, bestw, :>,
<:bests, safe, soninp, sin::>, input_bsno, output_bsno, best_bsno, best_weight,
bestsegments, safe_segments, segs_on_input_disc, segsin);
if output_bsno = -1 then
begin
comment check that special outputdisc is not wanted;
if names(5) <> real<::> then alarm(14, -1); <* does not exist *>
end;
if best_segments < segsin then alarm(13, segsin);
discstores:= if input_bsno = best_bsno then 1 else 2;
if situation = 1 then
begin
best_segments:= segsin; <* a reasonable filesize *>
end
else
<* situation = 2 *>
begin
if discstores = 1 then
begin
passes:= 0; <* any number of merging passes *>
best_segments:= free_segs_on_input_disc; <* the free segments *>
if small_input_disc or best_segments < safe_segments then
begin
comment cut down the input file to a more safe size;
tail(1):= best_segments:=
if small_input_disc then segsin else safe_segments;
i:= monitor(44, z, 0, tail);
if i <> 0 then alarm(5, i);
end little room or biased distribution;
end discstores = 1
else
<* discstores = 2 *>
begin
passes:=
if output_bsno = -1 then
<* passes is used to select the safest disc for final out *>
(if segs_on_input_disc >= safe_segments and
best_segments >= safe_segments then 0
else
if segs_on_inputdisc < bestsegments then 1 else 2)
else
<* special outputdisc *>
if best_bsno = output_bsno then 1 else 2;
end discstores = 2;
end situation = 2;
\f
comment now, at last, create the workfile;
tail(1):= if best_segments > safe_segments then safe_segments
else best_segments;
for i:= 1 step 1 until 2 do
begin
ia.areaname(i):= real<::>;
tail.discname(i):= real best_bsname(i);
end;
setzone6(z, ia);
i:= monitor(40, z, 0, tail); <* create entry *>
if i <> 0 then alarm(3, i);
getzone6(z, ia);
for i:= 1 step 1 until 2 do
sortfiles(2 * situation - 2 + i):= ia.areaname(i);
end select_work_file;
\f
comment
the sortfiles are initialized with empty names, because they are
removed in case of alarms, see procedure alarm;
for segsin:= 1 step 1 until 4 do sortfiles(segsin):= real<::>;
comment
normalize the names in paramenter names;
for i:= 1 step 2 until 5 do
if names(i) extract 8 = 0 then names(i+1):= real <::>;
begin
integer array field headpart;
integer array tail(1:10);
integer i;
zone z (128, 1, stderror);
open(z, 4, names, 0);
i:=monitor(42,z,0,tail);
if i<>0 then alarm(4,i);
content:=tail(9) shift (-12);
if content<>20 and content<>21 then content:=0; <* not bs- nor sq-system. *>
if content=21 then
begin
invar(z);
headpart:= 0;
headpart:= z.headpart(3);
param(1):= z.headpart(10) extract 12;
headpart:= z.headpart(1);
i:= z.headpart(3);
if i<noofrecs then alarm(16, noofrecs);
if noofrecs<=0 then noofrecs:=i;
close (z, false);
end
end;
comment check the parameters;
segsperinblock:= param(1);
if segsperinblock < 1 or segsperinblock > 40 then alarm(1, 1);
comment clear input;
if param(2) <> 0 and param(2) <> 1 then alarm(1, 2);
segsperoutblock:= if param(3) = 0 then segsperinblock else param(3);
if segsperoutblock < 1 or segsperoutblock > 40 then alarm(1, 3);
bytes_perinblock := segsperinblock * 512;
bytes_peroutblock:= segsperoutblock * 512;
i:= param(4);
fixedlength:= i = 1;
if i < 0 or i > 3 then alarm(1, 4);
maxlength:= param(5);
if maxlength < 2 or
maxlength > bytes_perinblock or
maxlength > bytes_peroutblock or maxlength mod 2 <> 0 then
alarm(1, 5);
min_segs_per_block:= (maxlength + 510)//512;
maxlength_plus_four:= maxlength + 4;
comment these two values are often used in the planning;
noofkeys:= param(6);
if noofkeys < 1 or noofkeys > maxlength or noofkeys > 169 then
alarm(1, 6);
begin
comment block for check of keydescr, coresize and messagebuffers;
integer i, abstype, neededcorebytes, position;
real array rarr(1:2);
comment
check the contents of integer array keydescr;
maxkeyposition:= 0;
for i:= 1*2+1 step 2 until noofkeys*2+1 do
begin
abstype:= abs keydescr(i);
if abstype = 5 <* absbyte *> then abstype:= 1;
position:= keydescr(i+1);
if position < abstype or
position > maxlength or
position mod 2 <> 0 and abstype <> 1 or
abstype < 1 or abstype > 4 then alarm(2, i//2);
if position > maxkeyposition then
maxkeyposition:= (position + 1)//2*2;
end checkkeydescr;
if fixedlength then maxkeyposition:= 2;
comment see declaration of array shortrec in block for pass 0;
\f
comment
check the available amount of core;
system(2, freebytes, rarr);
neededcorebytes:= 6650 <* 350 bytes below the 7000 bytes *>
+ bytesperinblock + bytesperoutblock
+ 4*maxlength + 24*noofkeys;
i:= free_bytes - needed_core_bytes;
if i < 0 then alarm(12, i);
comment
subtract from the value of freebytes:
1: room for about 10 program segments + up to 12 segments extra.
2: room for 3 zone-descriptors, and 5 share-descriptors.
3: room for local variables.
4: room for keycode and 1 record in sort-zone;
freebytes:= freebytes
+ (-6650 + 510 + 2*106)
- maxlength
- 24*noofkeys
- ( if i > 3*12*512 then 12*512 else i//3);
comment
as i >= 0 freebytes will now satisfy the relation:
freebytes >= 510 + 2*106 + 3*maxlength
+ 512*(segsperinblock+segsperoutblock).
this is a very important fact since the remaining algorithm of
the program will ensure that the sorting can be done if
freebytes is not less than any of the following two expressions:
1. string generation:
512*(segs_per_in_block + min_segs_per_block)
+ 2*(maxlength + 4) + max_keyposition.
2. merging passes:
512*3*min_segs_per_block + 2*(maxlength + 106).
where min_segs_per_block = (maxlength + 510)//512.
find out the number of free message buffers from the job process;
i:= system(6, 0, rarr);
system(5, i+26, rarr);
messbufsforinp:= rarr(1) shift (-36) extract 12 - 2;
comment
the buffer claim is byte 26 of the job process description.
the output zone requires up to 2 message buffers during a block change,
one of these can also be utilized for write(out,...;
end local block;
\f
comment
now the parameters and the available core storage have been checked,
and various global variables have been initialized;
comment -test-< write(out, <:<10>test 2, freebytes, messbufsforinp: :>,
freebytes, messbufsforinp);
comment test of procedure strategy, mintime is printed at the end of
procedure strategy *test str.*
write(out, <:<10>test 10 <10>:>) *test str.*
for freebytes:= 80000, 40000, 20000,
7000 + 512*(segsperinblock + segsperoutblock) + 4*maxlength - 300,
freebytes - 12*512 - maxlength - 400 do
begin
for segsin:= 8, 30, 125, 500, 2000, 8000 do
begin
for discstores:= 1, 2 do
begin
for outbytes:= 0 step 1 until discstores*2 - 2 do
begin
write(out, <:<10>freebytes, segsin, discstores, passes: :>,
<< dddd>, freebytes, segsin, discstores, outbytes) *test str.*
noofrecs:= segsin * 512.0 / maxlength *test str.*
for strings1:= 1, 10, 100, 1000 do
begin
passes:= outbytes *test str.*
select_merge_strategy *test str.*
write(out, <: shares, strings1: :>, << dddd>,
shares, strings1, <:<10> segs:>) *test str.*
for pass:= 1 step 1 until passes do
write(out, << ddd>, segsout(pass)) *test str.*
write(out, <:<10> insegs :>) *test str.*
for pass:= 1 step 1 until passes do
write(out, << ddd>, insegs(pass)) *test str.*
write(out, <:<10> m.p. :>) *test str.*
for pass:= 1 step 1 until passes do
write(out, << ddd>, mergepower(pass)) *test str.*
end
end
end
end
end;
\f
comment
find out whether the sort can be done without merging passes.
passes = 0 signals to the procedure select_work_file, that
this is the case.
if passes = 0 upon the return it is possible to do without merge;
passes:= if noofrecs < 0 or
noofrecs >
(freebytes - maxkeyposition
- bytes_per_inblock - bytes_per_outblock)
// maxlength_plus_four
then 1 else 0;
selectworkfile(1, names);
comment -test-< write(out, <:<10>test 3 :>, discstores, passes,
segsin);
shares:= 1;
if passes = 0 then
begin
comment sort without merge;
segsoutzero:= segsperoutblock;
recs:= noofrecs; <* records in sortzone *>
end no merging
else
begin
comment
select a reasonable blocklength and number of shares for the
string generating pass (pass 0).
this is done in a quite intuitive manner, as a flat
optimum is assumed;
segsoutzero:=
(free_bytes - bytes_per_inblock - 3*maxlength_plus_four)//(3*512);
comment
as a start take a third of the available core minus room for
inputblock, 2 records in sortzone and maxkeyposition.
this ensures a minimum stringlength of 2 records which in turn
ensures that the string chaining records (6 bytes each) will
not take up as much room as the data records;
\f
if messbufs_for_inp > 0 then
begin
comment doublebuffering is possible;
i:= segsoutzero // segsperinblock;
if i >= 1+2*3 or ( i >= 1+2*1 and discstores = 2) then
begin
comment choose doublebuffering;
shares:= 2;
segsoutzero:= (segsoutzero - segsperinblock) // shares;
end enough core for doublebuffering;
end enough messagebuffers for doublebuffering;
i:= 4 * segsperinblock;
if segsoutzero > i then segsoutzero:= i;
if segsoutzero < minsegsperblock then
segsoutzero:= minsegsperblock;
comment calculate the number of records in sortzone;
recs:= (freebytes - maxkeyposition
- (segsperinblock + segsoutzero) * 512 * shares)
// maxlength_plus_four;
if noofrecs >= 0 and recs > noofrecs then
recs:= noofrecs;
end select segsoutzero and shares;
comment -test-< write(out, <:<10>test 4, shares, segsoutzero, recs::>,
shares, segsoutzero, recs);
\f
pass:= 0;
begin
comment
block for pass 0, string generation.
the algorithm used here does very much look like the correspon-
ding algorithm in the procedure tapesortadp.
in order to speed up the code a bit, most quantities used in
the inner loops are local;
boolean fixedlength;
integer i, active, dead, reclength, recsin, minlength,
actual_maxlength, out_block_size, trap_situation;
real endfile;
integer field ifld, length, recordnumber;
real field endmark;
real array shortrec(1:(maxkeyposition + 2)//4);
long field lfld;
real array field first_part, out_base;
zone zin(shares*segsperinblock*128, shares, docerror),
zout(shares*segsout_zero*128, shares, errorinoutfile),
zsort((recs+1)*maxlength_plus_four//4+3*(noofkeys+1)+6, 1, docerror),
zcomp(3*noofkeys+6, 1, stderror);
\f
comment initialization of constants;
fixedlength:= param(4) = 1;
length:= 2;
endmark:= if fixedlength then 4 else 8;
endfile:= eof;
recsin:= noofrecs;
reclength:= maxlength;
minlength:= keydescr(1*2+2);
if recsin < 0 and minlength < 8 then minlength:= 8;
comment
the minimum length of a variable length record is the position of the
first keyfield or endoffilemark, if it is used;
i:= 1;
if content = 21 then
opensq(zin, string names(increase(i)), 1 shift 18,
(if fixedlength then maxlength else 0) shift 12
add (if param(4) = 0 then 0 else 1))
else
open(zin, 4, names, 1 shift 18);
i:= 1;
if passes = 0 and content = 21 then
opensq(zout, string sortfiles(increase(i)), 0,
(if fixedlength then maxlength else 0) shift 12 add 2)
else
open(zout, 4, sortfiles, 0);
if param(4) = 0 and content <> 21 then
begin
comment checksum control wanted, set free zone parameter;
integer array ia(1:20);
getzone6(zin, ia);
ia(11):= 1 shift 23;
setzone6(zin, ia);
end invar with checksum;
actual_maxlength:= if fixedlength then maxlength else minlength;
out_block_size:= segs_out_zero * 512;
outrec6(zout, out_block_size);
out_base:=
strings1:= outsegment:= outbytes:= recsout:= recs:=
active:= dead:= 0;
if recsin = 0 then goto endoffile;
\f
comment initialize zsort and zcomp.
an extra keyfield, recordnumber, is included for zsort in
order to retain the ordering of synonyms.
zcomp is used in calls of sortcomp to determine whether
a record can participate in the current string or not.
in this comparison it is needless and impossible to in-
clude the recordnumber;
i:= 2*noofkeys + 4;
begin
integer array local_keydescr(3:i);
local_keydescr(i-1):= 2;
local_keydescr(i ):= recordnumber:= maxlength + 2;
for i:= i-2 step -1 until 3 do local_keydescr(i):= keydescr(i);
startsort6(zcomp, local_keydescr, noofkeys , recordnumber);
i:=
startsort6(zsort, local_keydescr,
noofkeys + (if recordnumber < 2047 then 1 else 0),
recordnumber);
end initialize zsort and zcomp;
for i:= i step -1 until 1 do
begin
comment
initial filling of zsort;
if recsin = 0 then goto endoffile;
if fixedlength then inrec6(zin, reclength)
else
begin
invar(zin);
reclength:= zin.length;
if reclength > actual_maxlength then
begin
actual_max_length:= reclength;
if reclength > maxlength then alarm(9, reclength);
end a greater record
else
if reclength < minlength then alarm(9, reclength);
end variable length;
if recsin > 0 then recsin:= recsin - 1
else
if zin.endmark = endfile then goto endoffile;
recs:= recs + 1;
newsort(zsort); active:= active + 1;
for lfld:= maxkeyposition step -4 until reclength + 2 do
zsort.lfld:= 0;
comment set undefined keyfields to zero;
tofrom(zsort, zin, reclength);
zsort.recordnumber:= recs;
end initial filling of zsort;
comment start the writing of the first string;
\f
trap(trap_pass_0);
nextout:
trap_situation:= 1; <* comparison in zsort *>
outsort(zsort); active:= active - 1;
comment test trap - sqrt(param(8)-2);
fromlifesort:
recsout:= recsout + 1;
if -,fixedlength then reclength:= zsort.length;
if out_base + reclength > out_block_size then
begin
comment change the output block;
if pass = passes then
begin
for ifld:= out_base + 2 step 2 until out_blocksize do
zout.ifld:= 0;
first_part:= 0;
end zero fill in last pass
else
begin
first_part:= out_blocksize - out_base;
tofrom(zout.out_base, zsort, first_part);
end not final output;
outrec6(zout, out_blocksize);
out_base:= reclength - first_part;
tofrom(zout, zsort.firstpart, out_base);
end change output block
else
begin
if reclength < 26 then
begin
comment move with a for statement;
for lfld:= reclength step -4 until 4 do
zout.outbase.lfld:= zsort.lfld;
if lfld = 2 then zout.outbase.length:= zsort.length;
end short rec
else
tofrom(zout.outbase, zsort, reclength);
outbase:= reclength + outbase;
end normal record change;
\f
if recsin = 0 then goto endoffile;
if fixedlength then inrec6(zin, reclength)
else
begin
invar(zin);
reclength:= zin.length;
if reclength > actual_maxlength then
begin
actual_maxlength:= reclength;
if reclength > maxlength then alarm(9, reclength);
end a greater record
else
if reclength < minlength then alarm(9, reclength);
end variable length;
if recsin > 0 then recsin:= recsin - 1
else
if zin.endmark = endfile then goto endoffile;
recs:= recs + 1;
\f
if reclength >= maxkeyposition then
begin
comment there are no keyfields outside the record;
trap_situation:= 2; <* in sortcomp zin *>
comment test trap - sqrt(param(8)-3);
if sortcomp(zcomp, zin, zsort) < 0 then
begin
comment
the record just read cannot participate in the current string,
because it should have preceded the last written;
deadsort(zsort); dead:= dead + 1;
end
else
begin
comment
the record can be included in the current string;
newsort(zsort); active:= active + 1;
end sortcomp;
if reclength < 26 then
begin
comment move with for statement;
for lfld:= reclength step -4 until 4 do zsort.lfld:= zin.lfld;
if lfld = 2 then zsort.length:= zin.length;
end short rec
else
tofrom(zsort, zin, reclength);
zsort.recordnumber:= recs;
if active > 0 then goto nextout;
end no reset of undefined
else
begin
comment
the input record does not contain all keyfields, the undefined
keyfields are set to zero before any comparison;
for lfld:= maxkeyposition step -4 until reclength + 2 do
shortrec.lfld:= 0;
tofrom(shortrec, zin, reclength);
trap_situation:= 3; <* in sortcomp shortrec *>
comment test trap - sqrt(param(8)-4);
if sortcomp(zcomp, shortrec, zsort) < 0 then
begin
comment see the comments above;
deadsort(zsort); dead:= dead + 1;
end
else
begin
newsort(zsort); active:= active + 1;
end;
tofrom(zsort, shortrec, maxkeyposition);
zsort.recordnumber:= recs;
if active > 0 then goto nextout;
end reset undefined;
\f
comment
only dead records are now left in the sortzone.
terminate the current string, activate the dead records, and
continue;
strings1:= strings1 + 1;
endoutstring(zout, out_base, out_block_size);
trap_situation:= 1; <* comparison in zsort *>
lifesort(zsort); active:= dead - 1; dead:= 0;
goto fromlifesort;
trap_pass_0:
if trap_situation = 1 then print_zsort(zsort)
else
begin
<* error in sortcomp *>
print_rec(zsort.recordnumber, zsort);
if trap_situation = 2 then print_rec(recs, zin)
else print_rec(recs, shortrec);
end in sortcomp;
alarm(15, 0);
endoffile:
recsin:= 0;
comment
the remaining active records are written to the current string.
recsin = 0 makes the program return to endoffile without at-
tempting further input;
if active > 0 then goto nextout;
strings1:= strings1 + 1;
if dead > 0 then
begin
comment
terminate the current string, activate the dead records, and
write the last string;
endoutstring(zout, out_base, out_block_size);
lifesort(zsort); active:= dead - 1; dead:= 0;
goto fromlifesort;
end remaining dead;
changerec6(zout, out_base);
if param(2) = 1 then
begin
comment remove the input file;
i:= monitor(48, zin, 0, insegs);
if i <> 0 then alarm(7, i);
end clearinput
else
close(zin, true);
if pass = passes then
endoffilerec(zout);
comment endoffilerec jumps to returnfromdiscsort;
comment find the length of the file generated;
getposition(zout, 0, segsin);
segsin:= (out_base - 1)//512 + 1 + segsin;
setposition(zout, 0, 0); <* forces the last block out *>
if actual_maxlength < max_keyposition then
actual_maxlength:= max_keyposition;
maxlength := actual_maxlength;
maxlength_plus_four:= actual_maxlength + 4;
comment -test-< write(out, <:<10>test 5 , strings1, segsin, recs: :>,
strings1, segsin, recs);
end string generation;
\f
comment
now sorted strings have been output to the file, the name of which is
stored in sortfiles(1:2);
select_work_file(2, sortfiles);
comment
a new workfile has been created. its name is stored in sortfiles
(3:4).
the number of disc stores available for the merge is specified by the
value of the integer discstores, and the value of passes specifies the
the possible numbers of passes;
comment -test-< write(out, <:<10>test 6 , discstores, passes: :>,
discstores, passes);
select_merge_strategy;
comment -test-< write(out, <:<10>test 7 , passes, shares: :>,
passes, shares);
comment -test-< for pass:= 1 step 1 until passes do
write(out, <:<10>test 8, s.in, s.out, m.p.: :>, insegs(pass), segsout(pass),
mergepower(pass));
comment
the value of passes defines the number of remaining passes.
insegs, segsout(1:passes) contains the blocklengths of all passes,
and mergepower(1:passes) contains the mergepowers.
the value of shares specifies the number of shares to be used for
input/output;
\f
pass:= 1;
next_merging_pass:
begin
comment
block for one merging pass;
boolean fixedlength;
integer i, reclength, instring, active, insegment, inbytes,
nextrecsin, in_block_size, out_block_size;
integer field ifld, length, stringno;
integer array recsin, save_in_base(1:mergepower(pass));
long field lfld;
real array field first_part, in_base, out_base;
zone array
z(mergepower(pass), shares*in_segs(pass)*128, shares,
docerror);
zone
zout(shares*segsout(pass)*128, shares, errorinoutfile),
zmerge((mergepower(pass)+1)*maxlength_plus_four//4+3*(noofkeys+1)+6,
1, docerror);
procedure end_of_in_block(z, rec);
zone z; real array rec;
comment
reads the next record from z, taking the first part from the
current block and the second part from the next;
begin
first_part:= in_blocksize - in_base;
tofrom(rec, z.in_base, first_part);
in_base:= inrec6(z, 0);
inrec6(z, in_base);
if reclength = 1 000 000 then reclength:= z.length;
in_base:= reclength - first_part;
tofrom(rec.first_part, z, in_base);
end end_of_in_block;
comment
initialize local constants;
fixedlength:= param(4) = 1;
reclength:= maxlength;
length:= 2;
comment
open all the input/output zones, and initialize the merge zone;
\f
for instring:= 1 step 1 until mergepower(pass) do
begin
i:= if pass extract 1 = 1 then 1 else 3;
open(z(instring), 4, string sortfiles(increase(i)), 1 shift 18);
end open instrings;
i:= if pass extract 1 = 1 then 3 else 1;
if pass = passes and content = 21 then
opensq(zout, string sortfiles(increase(i)), 0,
(if fixedlength then maxlength else 0) shift 12 add 2)
else
open(zout, 4, string sortfiles(increase(i)), 0);
in_block_size := in_segs (pass) * 512;
out_block_size:= (if pass = passes then segs_per_out_block
else segs_out(pass)) * 512;
outrec6(zout, out_block_size);
out_base:= 0;
insegment:= outsegment;
inbytes:= outbytes;
nextrecsin:= recsout;
comment
these variables defines the position and length of the first input
string for the merge, it is identical to the last created output
string;
outsegment:= outbytes:= recsout:= 0;
comment -test-< write(out, <:<10>test 20 , start pass: :>, pass);
if nextrecsin = 0 then goto terminatepass;
comment only relevant for zero records to sort;
comment initialize zmerge, stringno is included as the keyfield
of the lowest priority in order to retain the ordering of
synonyms.
the sorting on the stringnumber is in decreasing order
in passes with odd numbers because the strings here are
read in reverse order;
i:= 2*noofkeys + 4;
begin
integer array local_keydescr(3:i);
local_keydescr(i-1):= if pass extract 1 = 1 then -2 else 2;
local_keydescr(i ):= stringno:= maxlength + 2;
for i:= i-2 step -1 until 3 do local_keydescr(i):= keydescr(i);
startsort6(zmerge, local_keydescr,
noofkeys + (if stringno < 2047 then 1 else 0),
stringno);
end initialize zmerge;
active:= 0;
\f
trap(trap_pass_n);
if false then
begin
trap_pass_n:
if active >= 2 then print_zsort(zmerge);
alarm(15, pass);
end trap;
fillzmerge:
for instring:= 1, instring + 1 while instring <= mergepower(pass)
and insegment >= 0 do
begin
comment
position the zone corresponding to instring in front of the next
string, read the string chaining record, and the first real record.
the first record is inserted in zmerge;
setposition(z(instring), 0, insegment);
in_base:= in_bytes;
i:= inrec6(z(instring), 0);
inrec6(z(instring), i);
comment -test-< write(out, <:<10>test 21 , instring, insegment, :>,
<:inbytes, nextrecsin: :>, instring, insegment, inbytes, nextrecsin);
recsin(instring):= nextrecsin - 1;
comment
recsin holds a counter of records for each instring;
if insegment = 0 and inbytes = 0 then insegment:= -1
else
begin
comment read the string chaining record;
ifld:= 0;
for i:= 1 step 1 until 3 do
begin
if in_base >= in_blocksize then
begin
in_base:= inrec6(z(instring), 0);
inrec6(z(instring), in_base);
in_base:= 2;
end blockchange
else
in_base:= in_base + 2;
nextrecsin:= z(instring).in_base.ifld;
case i of
begin
insegment:= nextrecsin;
inbytes := nextrecsin;
<* nextrecsin is ok *>
end case i;
end for i;
end chain to next instring;
\f
newsort(zmerge); active:= active + 1;
if -,fixedlength then
reclength:=
if in_base < in_block_size then
z(instring).in_base.length else 1 000 000;
if in_base + reclength > in_block_size then
end_of_in_block(z(instring), zmerge)
else
begin
tofrom(zmerge, z(instring).in_base, reclength);
in_base:= reclength + in_base;
end normal record change;
save_in_base(instring):= in_base;
for ifld:= reclength + 2 step 2 until maxkeyposition do
zmerge.ifld:= 0;
comment set undefined keyfields to zero;
zmerge.stringno:= instring;
comment -test-< printkey(recsout, instring, zmerge);
end initial filling of zmerge;
\f
nextout:
outsort(zmerge);
comment test trap - sqrt(param(8)-5);
instring:= zmerge.stringno;
recsout:= recsout + 1;
comment -test-< if recsin(instring) mod param(8) = 0 then
printkey(recsout, instring, zmerge);
if -,fixedlength then reclength:= zmerge.length;
if out_base + reclength > out_block_size then
begin
comment change the output block;
if pass = passes then
begin
for ifld:= out_base + 2 step 2 until out_blocksize do
zout.ifld:= 0;
first_part:= 0;
end zero fill in last pass
else
begin
first_part:= out_blocksize - out_base;
tofrom(zout.out_base, zmerge, first_part);
end intermediate pass;
outrec6(zout, out_blocksize);
out_base:= reclength - first_part;
tofrom(zout, zmerge.first_part, out_base);
end change out block
else
begin
if reclength < 26 then
begin
comment move with for statement;
for lfld:= reclength step -4 until 4 do
zout.out_base.lfld:= zmerge.lfld;
if lfld = 2 then zout.out_base.length:= zmerge.length;
end short rec
else
tofrom(zout.out_base, zmerge, reclength);
out_base:= reclength + out_base;
end normal record change;
\f
if recsin(instring) = 0 then goto endofstring;
recsin(instring):= recsin(instring) - 1;
newsort(zmerge);
in_base:= save_in_base(instring);
if -,fixedlength then
reclength:=
if in_base < in_block_size then
z(instring).in_base.length else 1 000 000;
if in_base + reclength > in_block_size then
end_of_in_block(z(instring), zmerge)
else
begin
tofrom(zmerge, z(instring).in_base, reclength);
in_base:= reclength + in_base;
end normal record change;
save_in_base(instring):= in_base;
for ifld:= reclength + 2 step 2 until max_key_position do
zmerge.ifld:= 0;
comment set undefined keyfields to zero;
zmerge.stringno:= instring;
goto nextout;
endofstring:
active:= active - 1;
if active > 0 then goto nextout;
comment
now all strings have been emptied, and a new round should be
started if there are more strings left;
if insegment >= 0 then
begin
endoutstring(zout, out_base, out_block_size);
goto fillzmerge;
end take a new round;
\f
terminatepass:
changerec6(zout, out_base);
if pass = passes then
begin
comment remove input file;
i:= monitor(48, z(1), 0, recsin);
if i <> 0 then alarm(7, i);
comment end the sort by procedure endoffilerec,
which jumps to the label return_from_discsort;
endoffilerec(zout);
end it was the last pass;
setposition(zout, 0, 0);
comment setposition forces the last block out;
comment -test-< write(out, <:<10>test 9 , pass, segsin, recsout: :>,
pass, segsin, recsout);
pass:= pass + 1;
goto next_merging_pass;
end block for one merging pass;
alarmcall:
system(9, segsin, string sortfiles(1));
comment
the alarm is called here in order to obtain a simple alarm address;
returnfromdiscsort:
;comment -test-< write(out, <:<10>test 10, blocksread: :>, blocksread);
end discsort;
end
end
▶EOF◀