> On 3/17/23 4:14 AM, Christophe Rhodes wrote:
>>
>> For one-shot code, effectively just macroexpanding and then calling
>> functions that are already defined and optimized, you might find that
>> using a non-compiling evaluator will help. You could try setting
>> sb-ext:*evaluator-mode* to :interpret and seeing if that causes a
>> measurable change in behaviour.
>
> Thanks, that approach has an upside and a downside. I tried evaluating a
> vop form normally and it took 9ms/5.5mb consed/36mil cycles. Evaluating
> the same form in :interpret mode took 1ms/65kb consed/2.3mil cycles. So
> that's an improvement. But then when I try running the function SBCL
> freezes until I send an interrupt to stop it. Is something in the
> :interpret mode preventing the vop from compiling correctly?
Well, if you evaluated the function definition in :interpret mode, then
it will be attempting to interpret. Judging by your definition below
> (setf (symbol-function 'vop-ph)
> (lambda (a)
> (declare (optimize (speed 3) (safety 0)))
> (vop-ph a)))))
this won't end well. If you are in *evaluator-mode* = :compile, then
this lambda will be compiled, and the compiler will use the templates
(VOPs) it knows about. If you are in *evaluator-mode = :interpret, the
this lambda won't be explicitly compiled, so the interpreter will
interpret it, and in order to evaluate a function call to VOP-PH it will
attempt to call the function... VOP-PH.
You can explicitly request compilation using the COMPILE function:
(setf (symbol-function 'vop-ph)
(compile nil '(lambda (a)
(declare (optimize speed (safety 0)))
(vop-ph a))))
> I was wondering if it could be possible to directly engage with the
> functions behind define-vop that convert the forms into binary
> opcodes, thus skipping all the compiler's optimization
> heuristics. Those compiler features shouldn't be necessary since I'm
> specifying the exact assembly instructions to generate.
Defining VOPs for known functions *is* how to directly engage with the
functions behind it. No other "optimization heuristics" will be
applied. (With the usual caveat that this is not a supported interface
at all.)
> The functions I'm building are customized for the specific dimensions
> and element width of the array being transformed and they compose
> multiple array transformations into a single loop.
I wonder to what extent it might be possible to parameterize this some
other way, so that you compile a somewhat more generic kernel that takes
constant (:INFO, in VOP language) arguments for some of these.
Dimensions and element-width feel like things that could be :info
parameters, for example. Then you would not need a new VOP each time
you applied the same operation on different-sized or different-width
arrays.
Likewise, it might be that the loop over elements could be regular lisp
(as simple as nested DOTIMES) and the kernel could be custom. Again,
this might mean generating rather fewer distinct VOPs.
VOPs were designed substantially before I started working on SBCL, but
judging by the apparent properties of their use they seem to me to be
intended as reusable components to implement generic-ish operations
expressed in regular Common Lisp, and the distance that the user strays
from that will tend to increase the friction in using them. I would
suggest that the "normal" interface to use SBCL as a compiler for
another language would be to translate that language into Common Lisp
source, applying all the language-specific optimizations that you might
want to (in this case, recognizing a sequence of array transformations
and deforesting -- detabling? -- the intermediate arrays), emitting
Common Lisp source, and then allowing the SBCL compiler to optimize that
Common Lisp source to machine code. (And then defining VOPs for
particular operations in the translated language that do not map well to
Common Lisp, or are not optimized well by the SBCL compiler).
Christophe