ebook img

ost-computer-science-chopt PDF

2009·0.95 MB·English
Save to my drive
Quick download
Download
Most books are stored in the elastic cloud where traffic is expensive. For this reason, we have a limit on daily download.

Preview ost-computer-science-chopt

CHAPTER 20 Performance Lisp programmers know the value of everything and the cost of nothing. —Alan Perlis Introduction This chapter examines methods for discovering why a Lisp program might be under- performing. I take it that you already know all about the perils of premature optimiza- tion: we can take it that the program is believed to be working correctly in all aspects other than performance. The experimental techniques discussed here are great for lo- cating bottlenecks in large applications. They apply equally whether you wrote the code yourself or have never seen it before. Therefore I will assume that you do not necessarily know anything about the internals of the program under investigation. Typically, once you know where your bottlenecks are, the correct response is to recode them for efficiency. • Think carefully about algorithms. • Use appropriate data structures (see “Data” on page 15). • Avoid expensive calculations; cache intermediate results. • Don't expect the application to discover facts which you could have told it in ad- vance. Sometimes you'll get to the end of this process and find that you're still running too slow. This is the point at which you should turn to Lisp's built-in support for optimi- zation; we'll look at this at the end of the chapter. As a driving example, we’ll return to the ch-image library of Chapter 17 and consider the time it spends loading a largish JPEG image. We’ll continue to work with Clozure Common Lisp under SLIME on FreeBSD. 1 `Ž—‡…†’=¬=k‡‰=iƒ”‡Œƒ=E†’’ŽWLLŠ‡‘ŽJ€‰K…LF=OMMV=J=OMNNK=p‹ƒ=‡…†’‘=ƒ‘ƒ”ƒ‚K q†‡‘=•‰=‡‘=Š‡ƒŒ‘ƒ‚=“Œ‚ƒ=’†ƒ=`ƒ~’‡”ƒ=`‹‹Œ‘=^’’‡€“’‡ŒJkŒ`‹‹ƒ‡~ŠJkaƒ‡”‘=PKM=rŒŽ’ƒ‚=i‡ƒŒ‘ƒK q=”‡ƒ•=~=Ž—=„=’†‡‘=Š‡ƒŒ‘ƒI=”‡‘‡’=†’’ŽWLLƒ~’‡”ƒ‹‹Œ‘K…LŠ‡ƒŒ‘ƒ‘L€—JŒJŒ‚LPKML==‘ƒŒ‚=~=Šƒ’’ƒ ’=`ƒ~’‡”ƒ=`‹‹Œ‘I=QQQ=`~‘’=p’ƒƒ’I=p“‡’ƒ=VMMI=j“Œ’~‡Œ=s‡ƒ•I=`~Š‡„Œ‡~I=VQMQNI=rp^K CL-USER> (time (ch-image:read-image-file "~/chapter-20/test.jpg")) (CH-IMAGE:READ-IMAGE-FILE "~/chapter-20/test.jpg") took 4,102 milliseconds ... #<CH-IMAGE:ARGB-8888-IMAGE #x34EAFBB6> CL-USER> Our mission then is to find out why ch-image takes over four seconds to load a 300k image file (on this particular machine). Is this time reasonable? What can we do to improve on it? Let’s find out. I really don’t want to suggest that I have any complaints whatsoever about the performance of ch-image; it just happens to make a good example which fits in with earlier chapters. In practice the question is always: are you meeting your requirements? If your application takes too long to run then something needs to be done to make it faster. If execution times are satisfactory then inefficiency is not a problem and you should not waste effort on performance. If your code doesn’t work correctly then your attention should be elsewhere. Profiling Tools Needless to say, before you start looking at performance issues you will ensure that all of your code is compiled (see Chapter 12). Interpreted code has one or two interesting benefits but speed isn’t one of them. We know that a certain operation takes four seconds but we don’t know why. We’d like to break the operation down into components, and break these components down, and so on until—maybe—we find a culprit. We can cl:time the operation as a whole but can’t naively apply this to every function call it makes. Exercise Why not? There is however a powerful tool which can manage this for us, namely a code pro- filer. Although this is not part of Common Lisp, most implementations ship with one. The details differ from place to place but they all work along similar principles and are used in more or less the same way; you should have little difficulty transferring the specifics of this chapter to whatever system you’re using. Unfortunately when we turn to Clozure CL we find that although it ships two profilers, one only works on Mac OS and the other on Linux. On FreeBSD we’ll have to look elsewhere for help. 2 | Chapter 20: Performance `Ž—‡…†’=¬=k‡‰=iƒ”‡Œƒ=E†’’ŽWLLŠ‡‘ŽJ€‰K…LF=OMMV=J=OMNNK=p‹ƒ=‡…†’‘=ƒ‘ƒ”ƒ‚K q†‡‘=•‰=‡‘=Š‡ƒŒ‘ƒ‚=“Œ‚ƒ=’†ƒ=`ƒ~’‡”ƒ=`‹‹Œ‘=^’’‡€“’‡ŒJkŒ`‹‹ƒ‡~ŠJkaƒ‡”‘=PKM=rŒŽ’ƒ‚=i‡ƒŒ‘ƒK q=”‡ƒ•=~=Ž—=„=’†‡‘=Š‡ƒŒ‘ƒI=”‡‘‡’=†’’ŽWLLƒ~’‡”ƒ‹‹Œ‘K…LŠ‡ƒŒ‘ƒ‘L€—JŒJŒ‚LPKML==‘ƒŒ‚=~=Šƒ’’ƒ ’=`ƒ~’‡”ƒ=`‹‹Œ‘I=QQQ=`~‘’=p’ƒƒ’I=p“‡’ƒ=VMMI=j“Œ’~‡Œ=s‡ƒ•I=`~Š‡„Œ‡~I=VQMQNI=rp^K If your implementation provides a profiler then you should use that in preference to anything else. It will load and run without a fuss and it might be able to monitor more “system” functions than a portable pro- filer could. We’ll be working here with a public domain library called the Metering System. You’ll find a download link for this here: http://cl-user.net/asp/libs/Metering The Metering System was written in the 1980s and appears not to have been maintained at all since 1994. The dates indicate that it might not quite conform to the ANSI stand- ard. Indeed it predates CLOS. Also there are no developer mailing lists to which you can turn for help if you get stuck. On the other hand we’ll see that its age hasn’t de- tracted from its usefulness, so please don’t be put off. Building the Profiler The Metering System consists of a single source file, metering.cl. (You will encounter the .cl filetype for Lisp files now and then.) There are some obstacles to using the file as-is: it’s not quite ANSI Lisp, and it needs to be configured for the implementation we're using. SLIME for both Clozure CL and CLISP automatically loads a copy of metering in which these problems have already been fixed. (That’s an- other good reason for using SLIME.) Don’t regard the following as a wasted lesson though: occasionally as a Lisp programmer you will come across files which need this level of attention. Having the confidence to proceed with them is a valuable asset. In the first edition of Common Lisp the Language (CLtL1), in-package was a function which took keyword arguments :nicknames and :use and which was used to create new packages on the fly. In the second edition (CLtL2) and subsequent ANSI specification, an incompatible change was introduced and in-package became the macro it is today. It does not take any keywords and it may only refer to pre-existing packages; to define and configure a new package you’ll typically use defpackage (which takes :nick names, :use and seven other keywords). The Metering System tries to work around this using the :cltl2 feature (see line 378): #-:cltl2 (in-package "MONITOR" :nicknames '("MON")) followed by package definitions appropriate to non-CLtL2 compatible Lisps (which at the time meant: CLtL1 compatible). Unfortunately, although this code would have been fine when it was first written, ANSI gave #+:cltl2 a specific meaning: it implies Introduction | 3 `Ž—‡…†’=¬=k‡‰=iƒ”‡Œƒ=E†’’ŽWLLŠ‡‘ŽJ€‰K…LF=OMMV=J=OMNNK=p‹ƒ=‡…†’‘=ƒ‘ƒ”ƒ‚K q†‡‘=•‰=‡‘=Š‡ƒŒ‘ƒ‚=“Œ‚ƒ=’†ƒ=`ƒ~’‡”ƒ=`‹‹Œ‘=^’’‡€“’‡ŒJkŒ`‹‹ƒ‡~ŠJkaƒ‡”‘=PKM=rŒŽ’ƒ‚=i‡ƒŒ‘ƒK q=”‡ƒ•=~=Ž—=„=’†‡‘=Š‡ƒŒ‘ƒI=”‡‘‡’=†’’ŽWLLƒ~’‡”ƒ‹‹Œ‘K…LŠ‡ƒŒ‘ƒ‘L€—JŒJŒ‚LPKML==‘ƒŒ‚=~=Šƒ’’ƒ ’=`ƒ~’‡”ƒ=`‹‹Œ‘I=QQQ=`~‘’=p’ƒƒ’I=p“‡’ƒ=VMMI=j“Œ’~‡Œ=s‡ƒ•I=`~Š‡„Œ‡~I=VQMQNI=rp^K that the implementation does not conform to the standard (because it conforms instead to CLtL2). So, quite reasonably, no modern Lisp carries the :cltl2 feature; the old- style in-package above will not be skipped by the reader and an error will be signaled. We can fix this by commenting out the above form (and the #-:cltl2) and replacing them with: (defpackage "MONITOR" (:nicknames "MON")) (in-package "MONITOR") Let's turn now to configuration for Clozure CL. Almost all of the Metering System is written in portable (if somewhat ancient) Common Lisp. Four optional implementa- tion-dependent functions provide hooks into the underlying Lisp for tailoring it. I don’t want to repeat the level of archaeological details above; suffice it to say that these func- tions are already defined in a form suitable for Clozure but that further feature skew renders them invisible. You can remedy this, by pushing both :mcl and :ccl-2 onto *features*. Unfortunately, again, the :mcl feature causes minor problems of its own. You’ll have to comment out two forms: the #+:mcl defpackage which imports provide and require from the CCL package, and the #+:mcl form (ccl:provide "monitor")—or change this to (provide "monitor"). Exercise Volunteer to maintain the Metering System: make it conform to ANSI Common Lisp and use features appropriate to recent versions of all the implementations you think it ought to support. The :mcl feature happens to work in this context. This should not be taken to imply that MCL (Macintosh Common Lisp) and Clozure CL are compatible, even though they are forks off the same code base. Remove this feature before compiling or loading any other libraries. Now that we’ve got all this out of the way we can return to the problem in hand. Profiling in Action The task of a profiler is to run your code and reporting back on execution times. The exact list of features on offer will vary from case to case, but you can expect any profiler to provide at least these three basic pieces of information about each function under investigation: Call count How many times was this function called? 4 | Chapter 20: Performance `Ž—‡…†’=¬=k‡‰=iƒ”‡Œƒ=E†’’ŽWLLŠ‡‘ŽJ€‰K…LF=OMMV=J=OMNNK=p‹ƒ=‡…†’‘=ƒ‘ƒ”ƒ‚K q†‡‘=•‰=‡‘=Š‡ƒŒ‘ƒ‚=“Œ‚ƒ=’†ƒ=`ƒ~’‡”ƒ=`‹‹Œ‘=^’’‡€“’‡ŒJkŒ`‹‹ƒ‡~ŠJkaƒ‡”‘=PKM=rŒŽ’ƒ‚=i‡ƒŒ‘ƒK q=”‡ƒ•=~=Ž—=„=’†‡‘=Š‡ƒŒ‘ƒI=”‡‘‡’=†’’ŽWLLƒ~’‡”ƒ‹‹Œ‘K…LŠ‡ƒŒ‘ƒ‘L€—JŒJŒ‚LPKML==‘ƒŒ‚=~=Šƒ’’ƒ ’=`ƒ~’‡”ƒ=`‹‹Œ‘I=QQQ=`~‘’=p’ƒƒ’I=p“‡’ƒ=VMMI=j“Œ’~‡Œ=s‡ƒ•I=`~Š‡„Œ‡~I=VQMQNI=rp^K Inclusive time How much time was spent either in this function or in the functions it called? In other words, how often was this function on the execution stack? Exclusive time How much time was spent running the code in this function alone? Equivalently, how often was this function at the top of the stack? Some profilers present their results in the form of a call tree, which can be incredibly useful. It’s one thing to find out that you spent 15% of your runtime in format, it’s quite another to know which of dozens of callers to format from your application was re- sponsible for this drain. The Metering System doesn’t offer this facility though, so we’ll have to get along without it. Configuring the Profiler The basic steps to using a profiler are: 1. Tell it which functions you want it to watch (“mark them for profiling”). 2. Run the code you want profiled. 3. Examine results; refine procedure and/or modify code; repeat the cycle. Most of the fine details are library-specific; if you’re using your implementation’s pro- filer, you’re going to have to read about them in product documentation. The general picture though will be similar whichever profiling library you use. (By the way, for the Metering System, library documentation takes the form of a 200-line comment block near the head of metering.cl.) Profilers such as the Metering System work by redefining a specified set of functions so that they record call counts, timing, allocation, etc., in addition to performing their normal actions. Other profilers might use statistical sampling of the call stack in addi- tion to (or instead of) function redefinition. In either case profiled code will run an order of magnitude slower than it would normally. Depending on your profiling library, any redefinition and associated slow-down will apply either after step 1 above, or for the duration of step 2 but no longer, or once you’ve commenced step 2 and until you explicitly unprofile things. Three other consequences of this redefinition: • If you redefine functions yourself they might cease to be profiled unless you ex- plicitly mark them again. (This is the case with the Metering System. It’s less likely to be the case with implementation-specific profilers.) • All this might interact badly with tracing, which also works by redefining functions. The Metering System doesn’t do well here (if you trace a function and then mark it, untracing it will have no effect) but again you can expect better behavior from the implementations’ profilers. Profiling in Action | 5 `Ž—‡…†’=¬=k‡‰=iƒ”‡Œƒ=E†’’ŽWLLŠ‡‘ŽJ€‰K…LF=OMMV=J=OMNNK=p‹ƒ=‡…†’‘=ƒ‘ƒ”ƒ‚K q†‡‘=•‰=‡‘=Š‡ƒŒ‘ƒ‚=“Œ‚ƒ=’†ƒ=`ƒ~’‡”ƒ=`‹‹Œ‘=^’’‡€“’‡ŒJkŒ`‹‹ƒ‡~ŠJkaƒ‡”‘=PKM=rŒŽ’ƒ‚=i‡ƒŒ‘ƒK q=”‡ƒ•=~=Ž—=„=’†‡‘=Š‡ƒŒ‘ƒI=”‡‘‡’=†’’ŽWLLƒ~’‡”ƒ‹‹Œ‘K…LŠ‡ƒŒ‘ƒ‘L€—JŒJŒ‚LPKML==‘ƒŒ‚=~=Šƒ’’ƒ ’=`ƒ~’‡”ƒ=`‹‹Œ‘I=QQQ=`~‘’=p’ƒƒ’I=p“‡’ƒ=VMMI=j“Œ’~‡Œ=s‡ƒ•I=`~Š‡„Œ‡~I=VQMQNI=rp^K • The Metering System is not aware of CLOS. It can’t profile individual methods and you can’t add to or redefine the methods of a generic function while it is marked for profiling. Exercise Implement a basic trace / untrace facility: one function which closes over an original definition, printing out call arguments and return val- ues, and is otherwise unobtrusive to the application; another using the same closure to restore the original definition. Don’t worry about all the fine details of cl:trace. How straightforward is it (a) to ensure a function stays traced when you redefine it and (b) to co-operate with SLIME’s implementation hooks to figure out a function’s argument list? Exercise Delve into metering.cl. Redefine the functions monitoring-encapsulate and monitoring-unencapsulate so that CLOS methods are profiled too. Don’t run timing tests in an image in which you have been using a pro- filer. Don’t run a profiler in a live server unless you really know what you’re doing. The Metering System provides a number of methods for marking functions. I take the attitude that it’s better to profile too much than too little, so I tend to mark whole packages at a time. My choice is therefore to use the function (monitor:monitor-all &optional (package *package*)) which marks every function in package, along with the macro (monitor:unmonitor &rest names) which—in the style of untrace—either unmarks the named functions or if no names are specified unmarks everything. In our ch-image example, we really don’t know in advance why image loading takes as long as it does. So it’s worth groveling through the source and making a list of the packages in which ch-image and its dependencies are defined. Exercise Write a utility for listing the packages created during the load of the "ch-image" system. 6 | Chapter 20: Performance `Ž—‡…†’=¬=k‡‰=iƒ”‡Œƒ=E†’’ŽWLLŠ‡‘ŽJ€‰K…LF=OMMV=J=OMNNK=p‹ƒ=‡…†’‘=ƒ‘ƒ”ƒ‚K q†‡‘=•‰=‡‘=Š‡ƒŒ‘ƒ‚=“Œ‚ƒ=’†ƒ=`ƒ~’‡”ƒ=`‹‹Œ‘=^’’‡€“’‡ŒJkŒ`‹‹ƒ‡~ŠJkaƒ‡”‘=PKM=rŒŽ’ƒ‚=i‡ƒŒ‘ƒK q=”‡ƒ•=~=Ž—=„=’†‡‘=Š‡ƒŒ‘ƒI=”‡‘‡’=†’’ŽWLLƒ~’‡”ƒ‹‹Œ‘K…LŠ‡ƒŒ‘ƒ‘L€—JŒJŒ‚LPKML==‘ƒŒ‚=~=Šƒ’’ƒ ’=`ƒ~’‡”ƒ=`‹‹Œ‘I=QQQ=`~‘’=p’ƒƒ’I=p“‡’ƒ=VMMI=j“Œ’~‡Œ=s‡ƒ•I=`~Š‡„Œ‡~I=VQMQNI=rp^K In the first instance, you should steer clear of profiling implementation packages (such as CCL in Clozure CL). You’d like to start your investi- gations hoping that the underlying Lisp implementation is not at fault; also there may be problems if a profiler which wasn't shipped by your Lisp's implementors attempts to redefine system internals. (As it hap- pens, on Clozure CL the Metering System can’t monitor either COMMON- LISP or Clozure’s internals package CCL.) We’re going to spend a lot of time looking at function definitions. I recommend using SLIME to locate them. Incidentally SLIME comes with a profiling interface. I didn’t mention it in Chapter 18 as I am not convinced that there’s much value in using it. Exercise Why not? So, finally: CL-USER> (dolist (package '(ch-image ch-util clem jpeg)) (monitor:monitor-all package)) NIL CL-USER> Run the Profiler With the Metering System, functions are redefined ready for profiling as soon as you mark them. To profile your code, simply run it. CL-USER> (ch-image:read-image-file "~/chapter-20/test.jpg") ;; This won’t be quick. Go make yourself a cup of tea. #<CH-IMAGE:ARGB-8888-IMAGE #x36686C76> CL-USER> With other profilers you may have to tell your system that you’re actively profiling something: (system:profile (my-function-call)), or whatever. Look it up. If you want to profile your application’s response to an end user interaction (for ex- ample, mouse events in a GUI, or page requests for a web server), you should write a function which mimics the user’s gesture and profile that. If the function which you intend to feed into the profiler has arguments which are hard to recreate by hand, use trace or break or outright re- definition to capture them. Profiling in Action | 7 `Ž—‡…†’=¬=k‡‰=iƒ”‡Œƒ=E†’’ŽWLLŠ‡‘ŽJ€‰K…LF=OMMV=J=OMNNK=p‹ƒ=‡…†’‘=ƒ‘ƒ”ƒ‚K q†‡‘=•‰=‡‘=Š‡ƒŒ‘ƒ‚=“Œ‚ƒ=’†ƒ=`ƒ~’‡”ƒ=`‹‹Œ‘=^’’‡€“’‡ŒJkŒ`‹‹ƒ‡~ŠJkaƒ‡”‘=PKM=rŒŽ’ƒ‚=i‡ƒŒ‘ƒK q=”‡ƒ•=~=Ž—=„=’†‡‘=Š‡ƒŒ‘ƒI=”‡‘‡’=†’’ŽWLLƒ~’‡”ƒ‹‹Œ‘K…LŠ‡ƒŒ‘ƒ‘L€—JŒJŒ‚LPKML==‘ƒŒ‚=~=Šƒ’’ƒ ’=`ƒ~’‡”ƒ=`‹‹Œ‘I=QQQ=`~‘’=p’ƒƒ’I=p“‡’ƒ=VMMI=j“Œ’~‡Œ=s‡ƒ•I=`~Š‡„Œ‡~I=VQMQNI=rp^K Some profilers (not this one) work by repeatedly interrupting the computation to ex- amine the stack. This has implications on exactly what you should profile. • There’s little to be learned from profiling a thread that’s waiting; so if processing the interaction involves a thread switch then profile calls within the thread which does the hard work. • If it’s all over too quickly you won’t get enough readings to form an accurate pic- ture. A good rule of thumb is to profile code which (unprofiled) would take a few seconds to run. If necessary, put the code you’re interested in into a loop and profile that. Results: Exclusive Times Profiling is an experimental process. By the end of the afternoon you’ll have filled your listener with the results of a whole load of profiler runs and if you’re not careful you’ll find yourself buried under a pile of numbers. For the sake of brevity I’m going to gloss over my own mess here and present just the essential findings, as if magically I’d gone straight to them. Make careful records as you go so that your results are reproducible and you can reconstruct later precisely what state the code was in each time you ran the profiler. A good way to do this is to leave complete versions in the source files of everything you’ve tried, appropriately condition- alized (the first two changes we make below might be marked #+mref- as-macro and #+defun). If you’ve had to activate the profiler explicitly (by wrapping your code in a call to system:profile or some such) then maybe it will print its results automatically. Oth- erwise, your documentation will supply you with a simple call to make. In our case, the function we want is monitor:report. This generates more information than I want to discuss here; I’ve cut from the table below three columns which detail each function’s total allocation (referred to there as consing—see “How It Works, Op- tional Second Pass” on page 0 in Chapter 16). Sometimes this data can be helpful, particularly if you come to suspect that performance is GC-bound. But you should start your search without any such assumptions, and that’s what we’re going to do here*. CL-USER> (monitor:report) % Total Function Time Calls Sec/Call Time ----------------------------------------------------------- CH-IMAGE:SET-ARGB-VALUES: 0.75 1920000 0.000019 36.97800 CH-IMAGE:MAP-PIXELS: 0.10 1 4.930997 4.930997 * Don’t let the % upset you too much. 8 | Chapter 20: Performance `Ž—‡…†’=¬=k‡‰=iƒ”‡Œƒ=E†’’ŽWLLŠ‡‘ŽJ€‰K…LF=OMMV=J=OMNNK=p‹ƒ=‡…†’‘=ƒ‘ƒ”ƒ‚K q†‡‘=•‰=‡‘=Š‡ƒŒ‘ƒ‚=“Œ‚ƒ=’†ƒ=`ƒ~’‡”ƒ=`‹‹Œ‘=^’’‡€“’‡ŒJkŒ`‹‹ƒ‡~ŠJkaƒ‡”‘=PKM=rŒŽ’ƒ‚=i‡ƒŒ‘ƒK q=”‡ƒ•=~=Ž—=„=’†‡‘=Š‡ƒŒ‘ƒI=”‡‘‡’=†’’ŽWLLƒ~’‡”ƒ‹‹Œ‘K…LŠ‡ƒŒ‘ƒ‘L€—JŒJŒ‚LPKML==‘ƒŒ‚=~=Šƒ’’ƒ ’=`ƒ~’‡”ƒ=`‹‹Œ‘I=QQQ=`~‘’=p’ƒƒ’I=p“‡’ƒ=VMMI=j“Œ’~‡Œ=s‡ƒ•I=`~Š‡„Œ‡~I=VQMQNI=rp^K CH-IMAGE:SET-PIXEL: 0.09 1920000 0.000002 4.306000 JPEG::DECODE-CHUNK: 0.03 1 1.482998 1.482998 CLEM::MATRIX-VALS: 0.03 7680008 0.000000 1.237982 ----------------------------------------------------------- TOTAL: 0.99 11520010 48.935978 ... CL-USER> Interpretation The report above shows two of the three pieces of information we listed in “Profiling in Action” on page 4: call count and exclusive time. Straight away we know that three quarters of the execution time was spent in calls to just one function: ch-image:set- argb-values. Let’s confirm this. There’s another reason for writing the following test: in order to meas- ure success when we start making changes to the code, it can be useful to time these calls in isolation from the rest of the application. CL-USER> (monitor:unmonitor) ; No value CL-USER> (defun test (n) (let ((img (make-instance 'ch-image:argb-8888-image :width 1 :height 1))) (dotimes (i n) (ch-image:set-argb-values img 0 0 0 0 0 0)))) TEST CL-USER> (time (test 1920000)) (TEST 1920000) took 3,256 milliseconds (3.256 seconds) to run ... CL-USER> When we take a look at ch-image:set-argb-values, it turns out to be a generic function and SLIME’s meta-. offers us a choice of two methods, one specializing on ch- image:argb-image, the other on its direct subclass ch-image:argb-8888-image. How do we know which one to look at? Well, we don’t at first. So we’re going to have to devise a way of finding out. Exercise Tracing a function which is going to be called two million times is in- advisable. (Why?) Write a :before method for ch-image:set-argb-val ues which records the class of its first argument. If you want to try this out with the Metering System, don’t forget to (monitor:unmonitor ch- image:set-argb-values) first. When you’re done, remove your method. You need to know for certain that what you’re testing is “clean”; if you get badly tangled, restart Lisp. Profiling in Action | 9 `Ž—‡…†’=¬=k‡‰=iƒ”‡Œƒ=E†’’ŽWLLŠ‡‘ŽJ€‰K…LF=OMMV=J=OMNNK=p‹ƒ=‡…†’‘=ƒ‘ƒ”ƒ‚K q†‡‘=•‰=‡‘=Š‡ƒŒ‘ƒ‚=“Œ‚ƒ=’†ƒ=`ƒ~’‡”ƒ=`‹‹Œ‘=^’’‡€“’‡ŒJkŒ`‹‹ƒ‡~ŠJkaƒ‡”‘=PKM=rŒŽ’ƒ‚=i‡ƒŒ‘ƒK q=”‡ƒ•=~=Ž—=„=’†‡‘=Š‡ƒŒ‘ƒI=”‡‘‡’=†’’ŽWLLƒ~’‡”ƒ‹‹Œ‘K…LŠ‡ƒŒ‘ƒ‘L€—JŒJŒ‚LPKML==‘ƒŒ‚=~=Šƒ’’ƒ ’=`ƒ~’‡”ƒ=`‹‹Œ‘I=QQQ=`~‘’=p’ƒƒ’I=p“‡’ƒ=VMMI=j“Œ’~‡Œ=s‡ƒ•I=`~Š‡„Œ‡~I=VQMQNI=rp^K In this case, it turns out that the joke’s on me because when we go to look at the source we find the two definitions side by side and apart from their specializers they are iden- tical. A possible explanation for this coding style is that the compiler can depend on the types of specialized arguments thus sometimes making slot accesses faster. As CLOS guarantees run-time type checks before selecting the method, this optimization is not at the expense of safety: if an argument is not of the promised type the method cannot be selected. Exercise Remove one of the two methods (does it matter which one?) and time the test again. Did this make any improvement? (Why might it?) Anyway here, thanks to SLIME and M-., is the definition we’re after along with some supporting classes and methods. (in-package "CH-IMAGE") (defclass argb-image (multichannel-image) ((a :accessor image-a) (r :accessor image-r) (g :accessor image-g) (b :accessor image-b))) (defclass argb-8888-image (argb-image) ()) (defmethod set-argb-values ((img argb-8888-image) (row fixnum) (col fixnum) (a fixnum) (r fixnum) (g fixnum) (b fixnum)) (setf (mref (image-a img) row col) a) (setf (mref (image-r img) row col) r) (setf (mref (image-g img) row col) g) (setf (mref (image-b img) row col) b)) and (in-package "CLEM") (defclass matrix () ((m :accessor matrix-vals) ...) ...) (defmethod (setf mref) (v (m matrix) &rest indices) (setf (apply #'aref (matrix-vals m) indices) v)) Exercise Use SLIME to track down these definitions yourself. 10 | Chapter 20: Performance `Ž—‡…†’=¬=k‡‰=iƒ”‡Œƒ=E†’’ŽWLLŠ‡‘ŽJ€‰K…LF=OMMV=J=OMNNK=p‹ƒ=‡…†’‘=ƒ‘ƒ”ƒ‚K q†‡‘=•‰=‡‘=Š‡ƒŒ‘ƒ‚=“Œ‚ƒ=’†ƒ=`ƒ~’‡”ƒ=`‹‹Œ‘=^’’‡€“’‡ŒJkŒ`‹‹ƒ‡~ŠJkaƒ‡”‘=PKM=rŒŽ’ƒ‚=i‡ƒŒ‘ƒK q=”‡ƒ•=~=Ž—=„=’†‡‘=Š‡ƒŒ‘ƒI=”‡‘‡’=†’’ŽWLLƒ~’‡”ƒ‹‹Œ‘K…LŠ‡ƒŒ‘ƒ‘L€—JŒJŒ‚LPKML==‘ƒŒ‚=~=Šƒ’’ƒ ’=`ƒ~’‡”ƒ=`‹‹Œ‘I=QQQ=`~‘’=p’ƒƒ’I=p“‡’ƒ=VMMI=j“Œ’~‡Œ=s‡ƒ•I=`~Š‡„Œ‡~I=VQMQNI=rp^K

See more

The list of books you might like

Most books are stored in the elastic cloud where traffic is expensive. For this reason, we have a limit on daily download.