Re: (MEMBER ...) with literal symbol(s)
David Scherfgen via Maxima-discuss <[email protected]> Fri, 2 May 2025 17:56:07 +0200
| Newsgroups | gmane.comp.mathematics.maxima.general,gmane.lisp.gcl.devel |
|---|---|
| Message-ID | <CAMTHLKhdOMKfj+HbHOUeU8ztOuknTLKCR3kTKd569YZd7wa+YQ@mail.gmail.com> |
--===============2958781252193830474== Content-Type: multipart/alternative; boundary="000000000000671e9206342932f4" --000000000000671e9206342932f4 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable eq is a single machine instruction in most Lisp implementations and is inlined, i.e. doesn't require a function call. In contrast, eql is usually not inlined - it incurs the overhead of a function call and performs additional type checks if eq returns nil. Some experiments with SBCL show a factor ~10 difference in performance between eq and eql. Does it matter within Maxima? Maybe. member is called extremely often, and during a test suite run, it's the top #7 function where time is being spent= . Maxima basically uses member as a short way of writing: (or (eq ...) (eq ...) ...) I'd argue that in this long-form, if we know we're working with symbols, we would definitely use eq, not eql. So if we optimize for eq in long-form code, shouldn't we be just as deliberate when using member? Am Fr., 2. Mai 2025 um 17:29 Uhr schrieb Raymond Toy <[email protected] >: > > > On Thu, May 1, 2025 at 10:21=E2=80=AFPM David Scherfgen via Maxima-discus= s < > [email protected]> wrote: > >> Hello, >> >> I noticed that GCL 2.7.1 compiles these functions differently: >> >> (defun f (x) (member x '(a b c))) >> (defun g (x) (member x '(a b c) :test #'eq)) >> >> I suppose that's because F uses the default EQL as the test. I tried to >> understand the C code it generated, but it was incomprehensible to me. >> Anyways, I saw that the second function, G, had inlined the MEMBER call >> into (OR (EQ X 'A) (EQ X 'B) (EQ Y 'C)), which is reasonable. But why do= es >> F get compiled differently? >> >> Shouldn't the compiler notice that the second argument to MEMBER is a >> literal list of symbols and automatically use EQ instead of the default = EQL >> test? That would be an easy and safe optimization. >> >> The same goes for the case where the first argument to MEMBER is a >> literal symbol. >> >> Maxima is full of such instances where MEMBER is used as a shorter way t= o >> write (OR (EQ ...) (EQ ...) ...), and ":test #'eq" has mostly been remov= ed >> because it was believed that any modern Lisp compiler would apply this >> optimization automatically. At least SBCL does. (CCL doesn't, I have fil= ed >> a report already.) >> > Does it really matter that this conversion isn't done? > > Another reason why we should have left memq around. Then we could have > written a compiler macro to do the optimization we want. Assuming it > really makes a difference. > > --000000000000671e9206342932f4 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable <div dir=3D"ltr"><font face=3D"monospace">eq</font> is a single machine ins= truction in most Lisp implementations and is inlined, i.e. doesn't requ= ire a function call.<div>In contrast, <font face=3D"monospace">eql</font> i= s usually not inlined - it incurs the overhead of a function call and perfo= rms additional type checks if <font face=3D"monospace">eq</font> returns <f= ont face=3D"monospace">nil</font>.</div><div>Some experiments with SBCL sho= w a factor ~10 difference in performance between <font face=3D"monospace">e= q</font> and <font face=3D"monospace">eql</font>.</div><div><br></div><div>= Does it matter within Maxima? Maybe. <font face=3D"monospace">member</font>= is called extremely often, and during a test suite run, it's the top #= 7 function where time is being spent.</div><div>Maxima basically uses <font= face=3D"monospace">member</font> as a short way of writing:</div><div><br>= </div><div><font face=3D"monospace">(or (eq ...) (eq ...) ...)</font></div>= <div><br></div><div>I'd argue that in this long-form, if we know we'= ;re working with symbols, we would definitely use <font face=3D"monospace">= eq</font>, not <font face=3D"monospace">eql</font>. So if we optimize for <= font face=3D"monospace">eq</font> in long-form code, shouldn't we be ju= st as deliberate when using <font face=3D"monospace">member</font>?</div></= div><br><div class=3D"gmail_quote gmail_quote_container"><div dir=3D"ltr" c= lass=3D"gmail_attr">Am Fr., 2. Mai 2025 um 17:29=C2=A0Uhr schrieb Raymond T= oy <<a href=3D"mailto:[email protected]">[email protected]</a>&g= t;:<br></div><blockquote class=3D"gmail_quote" style=3D"margin:0px 0px 0px = 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir=3D"= ltr"><div dir=3D"ltr"><div class=3D"gmail_default" style=3D"font-family:ari= al,helvetica,sans-serif;font-size:small"><br></div></div><br><div class=3D"= gmail_quote"><div dir=3D"ltr" class=3D"gmail_attr">On Thu, May 1, 2025 at 1= 0:21=E2=80=AFPM David Scherfgen via Maxima-discuss <<a href=3D"mailto:ma= [email protected]" target=3D"_blank">maxima-discuss@lists.= sourceforge.net</a>> wrote:<br></div><blockquote class=3D"gmail_quote" s= tyle=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);pad= ding-left:1ex"><div dir=3D"auto">Hello,<div dir=3D"auto"><br></div><div dir= =3D"auto">I noticed that GCL 2.7.1 compiles these functions differently:</d= iv><div dir=3D"auto"><br></div><div dir=3D"auto">(defun f (x) (member x = 9;(a b c)))</div><div dir=3D"auto">(defun g (x) (member x '(a b c) :tes= t #'eq))</div><div dir=3D"auto"><br></div><div dir=3D"auto">I suppose t= hat's because F uses the default EQL as the test. I tried to understand= the C code it generated, but it was incomprehensible to me. Anyways, I saw= that the second function, G, had inlined the MEMBER call into (OR (EQ X &#= 39;A) (EQ X 'B) (EQ Y 'C)), which is reasonable. But why does F get= compiled differently?</div><div dir=3D"auto"><br></div><div dir=3D"auto">S= houldn't the compiler notice that the second argument to MEMBER is a li= teral list of symbols and automatically use EQ instead of the default EQL t= est? That would be an easy and safe optimization.</div><div dir=3D"auto"><b= r></div><div dir=3D"auto">The same goes for the case where the first argume= nt to MEMBER is a literal symbol.</div><div dir=3D"auto"><br></div><div dir= =3D"auto">Maxima is full of such instances where MEMBER is used as a shorte= r way to write (OR (EQ ...) (EQ ...) ...), and ":test #'eq" h= as mostly been removed because it was believed that any modern Lisp compile= r would apply this optimization automatically. At least SBCL does. (CCL doe= sn't, I have filed a report already.)</div></div></blockquote><div><spa= n class=3D"gmail_default" style=3D"font-family:arial,helvetica,sans-serif;f= ont-size:small">Does it really matter that this conversion isn't done?<= /span></div><div><span class=3D"gmail_default" style=3D"font-family:arial,h= elvetica,sans-serif;font-size:small"><br></span></div><div><span class=3D"g= mail_default" style=3D"font-family:arial,helvetica,sans-serif;font-size:sma= ll">Another reason why we should have left memq around.=C2=A0 Then we could= have written a compiler macro to do the optimization we want.=C2=A0 Assumi= ng it really makes a difference.</span> </div><br></div></div> </blockquote></div> --000000000000671e9206342932f4-- --===============2958781252193830474== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline --===============2958781252193830474== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Maxima-discuss mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/maxima-discuss --===============2958781252193830474==--