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&#39;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&#39;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&#39;d argue that in this long-form, if we know we&#39=
;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&#39;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 &lt;<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 &lt;<a href=3D"mailto:ma=
[email protected]" target=3D"_blank">maxima-discuss@lists.=
sourceforge.net</a>&gt; 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 &#3=
9;(a b c)))</div><div dir=3D"auto">(defun g (x) (member x &#39;(a b c) :tes=
t #&#39;eq))</div><div dir=3D"auto"><br></div><div dir=3D"auto">I suppose t=
hat&#39;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 &#39;B) (EQ Y &#39;C)), which is reasonable. But why does F get=
 compiled differently?</div><div dir=3D"auto"><br></div><div dir=3D"auto">S=
houldn&#39;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 &quot;:test #&#39;eq&quot; 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&#39;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&#39;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==--