metafun-debugging.tex /size: 18 Kb    last modification: 2023-12-21 09:43
1% language=us runpath=texruns:manuals/metafun
2
3\startcomponent mfun-debugging
4
5\environment metafun-environment
6
7\startchapter[reference=sec:debugging,title={Debugging}]
8
9\index{debugging}
10
11\startintro
12
13Those familiar with \CONTEXT\ will know that it has quite some visual debugging
14features build in. So, what can you expect of the \METAPOST\ macros that come
15with \CONTEXT ? In this chapter we will introduce a few commands that show some
16insight in what \METAPOST\ is doing.
17
18\stopintro
19
20\startsection[title=Showing paths]
21
22Since the outcome of \METAPOST\ code is in many respects more predictable than
23that of \TEX\ code, we don't need that advanced visual debugging features.
24Nevertheless we provide a few, that are all based on visualizing paths.
25
26\startbuffer
27path p ; p := fullcircle scaled 4cm ;
28drawpath p ; drawpoints p ; drawpointlabels p ;
29\stopbuffer
30
31\startlinecorrection[blank]
32\processMPbuffer
33\stoplinecorrection
34
35This visualization is achieved by using dedicated drawing commands:
36
37\typebuffer
38
39Since control points play an important role in defining the shape, visualizing
40them may shed some insight in what \METAPOST\ is doing.
41
42\startbuffer
43path p ; p := fullcircle xscaled 4cm yscaled 3cm ;
44drawpath p ; drawcontrollines p ;
45drawpoints p ; drawcontrolpoints p ; drawpointlabels p ;
46\stopbuffer
47
48\typebuffer
49
50The pre and post control points show up as small dots and are connected to their
51parent point with thin lines.
52
53\startlinecorrection[blank]
54\processMPbuffer
55\stoplinecorrection
56
57You can deduce the direction of a path from the way the points are numbered, but
58using an arrow to indicate the direction is more clear.
59
60\startbuffer
61path p ; p := fullcircle xscaled 4cm yscaled 3cm ;
62drawarrowpath p ; drawcontrollines p ;
63drawpoints p ; drawcontrolpoints p ; drawpointlabels p ;
64\stopbuffer
65
66\typebuffer
67
68The \type {drawarrowpath} is responsible for the arrow. Especially when you are
69in the process of defining macros that have to calculate intersections or take
70subpaths, knowing the direction may be of help.
71
72\startlinecorrection[blank]
73\processMPbuffer
74\stoplinecorrection
75
76The next table summarizes the special drawing commands:
77
78\starttabulate[|lT|l|]
79\HL
80\NC drawpath           \NC the path                         \NC \NR
81\NC drawarrowpath      \NC the direction of the path        \NC \NR
82\NC drawcontrollines   \NC the lines to the control points  \NC \NR
83\NC drawpoints         \NC the points that make up the path \NC \NR
84\NC drawcontrolpoints  \NC the control points of the points \NC \NR
85\NC drawpointlabels    \NC the numbers of the points        \NC \NR
86\HL
87\stoptabulate
88
89You can set the characteristics of these like you set \type {drawoptions}. The
90default settings are as follows:
91
92\starttyping
93drawpathoptions   (withpen pencircle scaled 5   withcolor .8white) ;
94drawpointoptions  (withpen pencircle scaled 4   withcolor   black) ;
95drawcontroloptions(withpen pencircle scaled 2.5 withcolor   black) ;
96drawlineoptions   (withpen pencircle scaled 1   withcolor .5white) ;
97drawlabeloptions  () ;
98\stoptyping
99
100Two more options are \type {draworiginoptions} and \type {drawboundoptions} which
101are used when visualizing the bounding box and origin.
102
103\startbuffer
104swappointlabels  := true ;
105path p ; p := fullcircle xscaled 4cm yscaled 3cm ;
106drawarrowpath p ; drawcontrollines p ;
107drawpoints p ; drawcontrolpoints p ; drawpointlabels p ;
108drawboundingbox p ; draworigin ;
109\stopbuffer
110
111\typebuffer
112
113In this example we have set \type {swappointlabels} to change the place of the
114labels. You can set the variable \type {originlength} to tune the appearance of
115the origin.
116
117\startlinecorrection[blank]
118\processMPbuffer
119\stoplinecorrection
120
121You can pass options directly, like you do with \type {draw} and \type {fill}.
122Those options override the defaults.
123
124\startbuffer
125path p ; p := fullcircle xscaled 6cm yscaled 3cm rotated 15 ;
126drawarrowpath     p ;
127drawcontrollines  p withcolor .625red ;
128drawpoints        p withcolor .625yellow ;
129drawcontrolpoints p withcolor .625yellow ;
130drawpointlabels   p withcolor .625yellow ;
131drawboundingbox   p ;
132draworigin          withcolor .625red ;
133\stopbuffer
134
135\startlinecorrection[blank]
136\processMPbuffer
137\stoplinecorrection
138
139Here we used the options:
140
141\typebuffer
142
143Sometimes it makes sense to draw a simple coordinate system, and for that purpose
144we have three more macros. They draw axis and tickmarks.
145
146\startbuffer
147drawticks unitsquare xscaled 4cm yscaled 3cm shifted (-1cm,-1cm) ;
148\stopbuffer
149
150\typebuffer
151
152The system drawn is based on the bounding box specification of the path passed to
153the macro. You can also draw one axis, using \type {drawxticks} or \type
154{drawyticks}. Here we show the previous command.
155
156\startlinecorrection[blank]
157\processMPbuffer
158\stoplinecorrection
159
160By default, the ticks are placed at .5cm distance, but you can change this by
161setting \type {tickstep} to a different value.
162
163\startbuffer
164tickstep := 1cm ; ticklength := 2mm ;
165drawticks fullsquare xscaled 4cm yscaled 3cm ;
166tickstep := tickstep/2 ; ticklength := ticklength/2 ;
167drawticks fullsquare xscaled 4cm yscaled 3cm ;
168\stopbuffer
169
170\typebuffer
171
172The \type {ticklength} variable specifies the length of a tick. Here we
173manipulated both the variables to get a more advanced system.
174
175\startlinecorrection[blank]
176\processMPbuffer
177\stoplinecorrection
178
179If visualizing a path would mean that we would have to key in al those
180draw||commands, you could hardly call it a comfortable tool. Therefore, we can
181say:
182
183\startbuffer
184drawwholepath fullsquare scaled 3cm rotated 30 randomized 5mm ;
185\stopbuffer
186
187\typebuffer
188
189The \type {drawwholepath} command shows everything except the axis.
190
191\startlinecorrection[blank]
192\processMPbuffer
193\stoplinecorrection
194
195If even this is too much labour, you may say:
196
197\starttyping
198visualizepaths ;
199\stoptyping
200
201This redefines the \type {draw} and \type {fill} command in such a way that they
202also show all the information.
203
204\startbuffer
205visualizepaths ;
206draw fullsquare scaled 3cm rotated 30 randomized 2mm ;
207\stopbuffer
208
209\typebuffer
210
211You may compare this feature to the \type {\showmakeup} command available in
212\CONTEXT, that redefines the \TEX\ primitives that deal with boxes, glues,
213penalties, and alike.
214
215\startlinecorrection[blank]
216\processMPbuffer
217\stoplinecorrection
218
219Of course you may want to take a look at the \METAPOST\ manual for its built in
220(more verbose) tracing options. One command that may prove to be useful is \type
221{show}, that you can apply to any variable. This command reports the current
222value (if known) to the terminal and log file.
223
224\startlinecorrection[blank]
225{\showmakeup\processMPbuffer}
226\stoplinecorrection
227
228The previous picture shows what is typeset when we also say \type {\showmakeup}.
229This command visualizes \TEX's boxes, skips, kerns and penalties. As you can see,
230there are some boxes involved, which is due to the conversion of \METAPOST\
231output to \PDF.
232
233\starttyping
234\startlinecorrection[blank]
235... the graphic ...
236\stoplinecorrection
237\stoptyping
238
239The small bar is a kern and the small rectangles are penalties. More details on
240this debugger can be found in the \CONTEXT\ manuals and the documentation of the
241modules involved.
242
243\stopsection
244
245\startsection[title=Comments]
246
247Sometimes, when trouble strikes, you might want to peek in the \PDF\ file to see
248what gets written there. Each graphic is marked with a number but when you
249have many it might make sense to add a comment to help you locate the code.
250
251\startbuffer
252\startMPcode
253    comment("test graphic") ;
254    message("processing a test graphic") ;
255    draw fullsquare scaled 1cm ;
256\stopMPcode
257\stopbuffer
258
259\typebuffer
260
261This renders as:
262
263\startlinecorrection[blank]
264    \getbuffer
265\stoplinecorrection
266
267On the console we get these messages:
268
269\starttyping
270metapost > message : processing a test graphic
271metapost > warning : processing a test graphic
272\stoptyping
273
274And in the \PDF\ file we will find:
275
276\starttyping
277% mps graphic 1: begin
278% mps graphic 1: test graphic
279q 0 g 0 G 10 M 1 j 1 J
2800.500000000 w
281-14.173233032 -14.173233032 m
28214.173233032 -14.173233032 l
28314.173233032 14.173233032 l
284-14.173233032 14.173233032 l
285-14.173233032 -14.173233032 l
286h S
2870 g 0 G Q
288% mps graphic 1: end
289\stoptyping
290
291Here are some examples of constructed messages:
292
293\starttyping
294message "2:                okay           (done)" ;
295message "1: " & dq      & "okay" & dq & " (done)" ;
296message "3: " & quotation "okay"      & " (done)" ;
297message "3: " & quote     "okay"      & " (done)" ;
298message "4: " & quotation 123         & " (done)" ;
299message "5: " & quotation true        & " (done)" ;
300message "6: " & quote     true        & " (done)" ;
301message "7: " & tostring  true        & " (done)" ;
302message "8: " & tostring  (1,2)       & " (done)" ;
303message "9: " & topair    (1,2)       & " (done)" ;
304\stoptyping
305
306and this is what you get:
307
308\starttyping
309metapost > message : 2: okay (done)
310metapost > message : 1: "okay" (done)
311metapost > message : 3: "okay" (done)
312metapost > message : 3: 'okay' (done)
313metapost > message : 4: "123" (done)
314metapost > message : 5: "true" (done)
315metapost > message : 6: 'true' (done)
316metapost > message : 7: true (done)
317metapost > message : 8: 1 2 (done)
318metapost > message : 9: (1,2) (done)
319\stoptyping
320
321\stopsection
322
323\startsection[title=Pens]
324
325A circular pen is applied to a path in a different way than for instance a
326square pen. Circular pens are mapped onto \POSTSCRIPT\ pens while for other
327pens an outline is calculated that gets filled. Take this code:
328
329\startbuffer[a]
330\startMPcode
331    draw fullcircle xscaled 6cm yscaled 3cm
332        withpen pensquare scaled 5mm rotated 30
333        withcolor .625yellow ;
334\stopMPcode
335\stopbuffer
336
337\startbuffer[b]
338\startMPcode
339    draw envelope pensquare scaled 5mm rotated 30 of
340        (fullcircle xscaled 6cm yscaled 3cm)
341        withpen pencircle scaled 1mm
342        withcolor .375white ;
343\stopMPcode
344\stopbuffer
345
346\startbuffer[c]
347\enabletrackers[metapost.forcestroke]
348\startMPcode
349    draw fullcircle xscaled 6cm yscaled 3cm
350        withpen pensquare scaled 5mm rotated 30
351        withcolor .625red ;
352\stopMPcode
353\disabletrackers[metapost.forcestroke]
354\stopbuffer
355
356\typebuffer[a]
357
358and this:
359
360\typebuffer[b]
361
362and:
363
364\typebuffer[c]
365
366When we overlay these three we get. The envelope only returns the outer curve.
367
368\startlinecorrection[blank]
369\startoverlay
370    {\getbuffer[a]}
371    {\getbuffer[b]}
372    {\getbuffer[c]}
373\stopoverlay
374\stoplinecorrection
375
376We show a few nore examples and let it to the user to come up with applications
377for this feature. We start by defining a scaled pen that we apply to a simple
378path that has three point.
379
380\startbuffer
381pen whateverpen ; whateverpen := makepen(fullcircle xyscaled(1/20,1/40));
382
383path p ; p := (origin .. (1,1/2) .. (1,1/4)) scaled 10 ;
384path q ; q := envelope whateverpen of p ;
385
386draw              p withpen pencircle xyscaled(1/2,1/4)
387                    withcolor "darkgreen"   withtransparency (1,.5) ;
388drawpoints        p withpen pencircle scaled 1
389                    withcolor "darkgray"    withtransparency (1,.5) ;
390drawcontrolpoints p withpen pencircle scaled 1/2
391                    withcolor "darkmagenta" withtransparency (1,.5) ;
392
393currentpicture := currentpicture shifted (-20,0) ;
394
395draw              p withpen pencircle xyscaled(1/2,1/4)
396                    withcolor "darkgreen"   withtransparency (1,.5) ;
397draw              q withpen pencircle scaled 1/5
398                    withcolor "darkblue"    withtransparency (1,.5) ;
399drawpoints        q withpen pencircle scaled 1
400                    withcolor "darkgray"    withtransparency (1,.5) ;
401drawcontrolpoints q withpen pencircle scaled 1/2
402                    withcolor "darkyellow"  withtransparency (1,.5) ;
403draw              q withpen pencircle scaled 1/50
404                    withcolor "white" ;
405
406currentpicture := currentpicture ysized 2cm ;
407\stopbuffer
408
409\typebuffer
410
411On the left we see the path drawn with a circular pen. The points and control
412points are also shown. The path is repeated on the right but there it gets the
413envelope overlayed. We show the points and control points of the envelope: they
414nearly overlap but that depends of course on the used pen.
415
416\startlinecorrection[blank]
417    \processMPbuffer
418\stoplinecorrection
419
420In order to illustrate that the envelope is an outline we blow up a piece of
421this image:
422
423\startlinecorrection[blank]
424    \scale[height=3cm]{\clip[nx=10,ny=20,x=9,y=7]{\processMPbuffer}}
425\stoplinecorrection
426
427Without further explanation we let you ponder the results of the following code.
428
429\startbuffer
430drawoptions(withcolor "darkred") ;
431draw (envelope pensquare of (up--left)) ;
432draw (up--left) shifted (4,0) withpen pensquare ;
433draw fullcircle shifted (8,0) ;
434draw (envelope pensquare of fullcircle) shifted (12,0) ;
435draw (fullcircle) shifted (16,0) withpen pensquare ;
436draw (fullcircle) shifted (20,0) withpen pencircle ;
437currentpicture := currentpicture xsized TextWidth ;
438\stopbuffer
439
440\typebuffer
441
442The efficiency of the output of each draw differs a lot because circles are made
443from eight points and because a transformed pen results in two paths. Normally
444that is not something you have to loose sleep over.
445
446\startlinecorrection[blank]
447    \processMPbuffer
448\stoplinecorrection
449
450\stopsection
451
452\startsection[title=Performance]
453
454On the average performance of \METAPOST\ is quite okay. The original program uses
455scaled numbers, which are floats packed into an integer. The library also
456supports doubles, decimal and binary number models. In \CONTEXT\ we only support
457scaled, double and decimal. Because the library has to support multiple models
458there is more overhead and therefore it is also slower. There's also more dynamic
459memory allocation going on. In the transition from \MKII\ to \MKIV\ some of the
460critical code (like the code involved in passing \TEX\ states to \METAPOST) had
461to be optimized, although when the \LUA\ interface was added, betters ways became
462possible. We have to accept the penalty in performance and often can gain back a
463lot because we have the \LUA\ interface.
464
465One of the main bottlenecks in storing quantities. \footnote {Recently, Taco
466Hoekwater has done some excellent explanations about the way \METAPOST\ scans the
467input and create variables and you can find his presentations at meetings on the
468\CONTEXT\ garden.} When we see something \type {a[1]} and \type {a[3]} the \type
469{a} is a root variable and the \type {1} and {3} are entries in a linked list
470from that root. It's not an array in the sense that there is some upper bound and
471that there's also a slot \type {2}. There is order but the list is sparse. When
472access is needed, for instance to do some calculations, a linear lookup (from the
473head of the list) takes place. This is quite okay performance wise because
474normally these list are small. The same is true for a path, which is also a
475linked list. If you need point 25, it is looked up by starting at the first knot
476of the path. The longer the path, the more time it takes to reach arbitrary
477points. In the \LUA\ chapter we give an example of how to get around that
478limitation.
479
480Concerning the arrays, here is s trick to get around a performance bottleneck:
481
482\starttyping
483numeric foo[];
484
485def set_foo(expr c, s) =
486    foo[c] := s ;
487enddef ;
488
489def get_foo(expr c) =
490    foo[c]
491enddef ;
492\stoptyping
493
494If you use this as follows:
495
496\starttyping
497numeric n ; n = 123 ;
498
499for i=1 upto 20000 :
500    set_foo(i,n) ;
501endfor ;
502
503for i=1 upto 20000 :
504    n := get_foo(i) ;
505endfor ;
506\stoptyping
507
508the runtime can (for instance) be 3.3 seconds, but when you use the following
509variant, it goes down to 0.13 seconds.
510
511\starttyping
512numeric foo[][][][]; % 12345 : 1  12  123  44 instead of 12344
513
514def set_foo(expr c, s) =
515    foo[c div 10000][c div 1000][c div 100][c] := s ;
516enddef ;
517def get_foo(expr c) =
518    foo[c div 10000][c div 1000][c div 100][c]
519enddef ;
520\stoptyping
521
522This time the lookup is not split into phases each being relatively fast. So, in
523order to reach slot 1234 the engine doesn't have to check and jump over what
524comes before that. You basically create a tree here: 0 (hit), 1000 (hit in one),
525200 (hit in two), 34 (hit in 34). We could go to a single digit but that doesn't
526save much. Before we had ways to store data at the \LUA\ end we used this a few
527times in macros that dealt with data (like Alan Braslau's node and graphics
528modules). This is typically something one can figure out by looking at the (non
529trivial) source code.
530
531Here is another example. In \LUA\ we can easily create a large file, like this:
532
533\starttyping
534\startluacode
535  local t = { }
536  for i=1,10000 do
537    t[i] = string.rep(
538      "here we have number " ..
539      tostring(i) ..
540      " out of the 10000 numbers that we will test"
541    ,100)
542  end
543  t = table.concat(t,"\n")
544  io.savedata("foo1.tmp",t)
545  io.savedata("foo2.tmp",t)
546  io.savedata("foo3.tmp",t)
547\stopluacode
548\stoptyping
549
550We make two copies because we do two experiments and we want to treat them equal with
551respect to caching.
552
553\starttyping
554\startMPcode
555  string f ; f := "foo1.tmp" ;
556  string s[] ;
557  numeric n ; n := 0 ;
558  for i=1 upto 10000 :
559    s[i] := readfrom f ;
560    exitif s[i] = EOF ;
561    n := n + 1 ;
562  endfor ;
563\stopMPcode
564\stoptyping
565
566Say that this runs in 2.2 seconds, how come that the next one runs in 1.7 seconds
567instead?
568
569\starttyping
570\startMPcode
571  string f ; f := "foo2.tmp" ;
572  string s[] ;
573  string ss ;
574  numeric n ; n := 0 ;
575  for i=1 upto 10000 :
576    ss := readfrom f ;
577    exitif ss = EOF ;
578    s[i] := ss ;
579    n := n + 1 ;
580  endfor ;
581\stopMPcode
582\stoptyping
583
584The main reason is that the first case we have two lookups in the linked list
585that determines variable \type {s} and the longer the list, the more time it will
586take. In the second case we use an intermediate variable. Although that means
587extra memory (de)allocation it still pays of. In practice you don't need to worry
588too much about it but of course we can again follow the tree approach:
589
590\startMPcode
591  string f ; f := "foo3.tmp" ;
592  string s[][][] ;
593  string ss ;
594  numeric n ; n := 0 ;
595  for i=1 upto 10000 :
596    ss := readfrom f ;
597    exitif ss = EOF ;
598    s[i div 1000][i div 100][i] := ss ;
599    n := n + 1 ;
600  endfor ;
601\stopMPcode
602
603This time we go down to 1.5 second. Timings could be a bit different in \MKIV\ and
604\LMTX\ because in \LUAMETATEX\ all \METAPOST\ file \IO\ goes through \LUA\ but the
605relative performance gains are the same. With \LUATEX\ and \MKIV\ I measures
6062.9, 2.5 and 2.1 and with \LUAMETATEX\ and \LMTX\ I got 2.3, 1.7 and 1.5.
607
608\stopsection
609
610\stopchapter
611
612\stopcomponent
613