Diferenças de desempenho na comparação de símbolos e seqüências de caracteres


7

Em sx.el, temos um caso em que precisamos verificar se passamos GETou POSTcomo argumento.

Atualmente, temos o argumento passando como uma string e estamos usando (string= "GET" url-method)para compará-lo "GET".

Existe alguma vantagem de compilação elisp / byte para alterá-lo para um símbolo (equal url-method 'GET)?


Independentemente da velocidade, os símbolos são idiomáticos para esse fim no Lisp. Além disso, use eqpara comparar com um símbolo.

eqirá converter objetos do Emacs inte compará-los. Comparar ints é muito mais rápido que comparar strings.
abo-abo

11
string=funcionará como esperado, seja url-methodum símbolo ou uma string. OTOH se você usar eqou equal, precisará garantir que os argumentos sejam do mesmo tipo.
YoungFrog

Respostas:


11

Os novos usuários do Lisp que vêm de outros idiomas às vezes usam cadeias para tais fins por hábito.

Os símbolos podem ser comparados ao eqinvés de apenas equal. string=usa código semelhante a equal, para testar cada caractere até encontrar uma incompatibilidade. Então, sim, a comparação de símbolos pode ser um pouco mais rápida. Se você estiver usando a comparação em um loop, por exemplo, a diferença pode ser importante.

Além disso, dependendo do código específico, muitas vezes pode tornar o código mais simples ou mais legível para usar símbolos em vez de cadeias. E há funções (e macros etc.) que esperam símbolos ou que comparam coisas usando eq(ou eql) por padrão. Para usar aqueles com strings, você precisa converter em símbolos.


7

Observe que o lisp reader estende símbolos, de forma que as referências independentes a um determinado símbolo fornecem exatamente o mesmo objeto lisp e, consequentemente, você é capaz de compará-los eq(que só precisa comparar os endereços dos objetos).

Por outro lado, strings independentes são sempre objetos lisp diferentes e, portanto, você deve comparar o conteúdo deles.

Portanto, você esperaria que a eqcomparação ganhasse pelo desempenho.

Curiosamente (estou certamente muito surpreso), alguns testes triviais benchmark-runestão dando string=a vitória por uma margem bastante. Isso me parece muito estranho. YMMV?


Edit: Então, eu apenas notei essa resposta (e seu comentário) novamente e me senti inspirado para ver se eu poderia recriar e explicar o resultado.

Nota: nada é compilado em bytes inicialmente.

A primeira constatação foi que meus símbolos eram quoted e as seqüências não eram, e imediatamente descobri que quoteera o responsável pela maior parte da diferença de velocidade:

(benchmark-run 10000000
  (string= "foo" "foo"))

é, para cadeias pequenas, consistentemente mais rápido que:

(benchmark-run 10000000
  (eq 'foo 'foo))

no entanto, se também citarmos a string:

(benchmark-run 10000000
  (string= '"foo" '"foo"))

que quase equilibra completamente as coisas.

No entanto, em média, a menos que a corda seja muito grande, a comparação de cordas ainda parece ganhar por um fio de cabelo.

Uma abordagem alternativa é vincular os objetos a variáveis:

(let ((foo 'test)
      (bar 'test))
  (benchmark-run 10000000
    (eq foo bar)))

(let ((foo "test")
      (bar "test"))
  (benchmark-run 10000000
    (string= foo bar)))

Mais uma vez, estou vendo a comparação das cordas mais rapidamente, em média.

Após a compilação de bytes, vejo apenas um resultado consistente para os casos de variáveis ​​vinculadas, onde eqé consistentemente mais rápido do que string=(demorando cerca de 2/3 do tempo).

Para os outros exemplos, estou obtendo resultados sem sentido (para mim), como as seqüências de caracteres citadas, sendo mais rápidas do que as seqüências de caracteres não citadas, o que só posso supor é efetivamente ruído de outros aspectos da execução e / ou de benchmark-runsi próprio. Havia variedade suficiente entre execuções diferentes da mesma função para ocultar completamente quaisquer diferenças entre execuções de funções diferentes.


Minhas conclusões são:

(a) eqcomparações com um símbolo podem (de maneira um pouco contra-intuitiva) ser mais lenta que uma comparação de cadeias, se o símbolo for citado.

(b) A menos que as strings sejam bastante grandes, as diferenças práticas são totalmente desprezíveis e, portanto, eu não me incomodaria em converter uma na outra por razões puramente de desempenho.


3
Possivelmente porque do jeito que você tenha configurado seu teste faz com que cada símbolo a ser gerado de novo em cada execução do loop, assim que você está colocando intern+ eqcontra string=. Ou possivelmente por algum motivo completamente diferente: é impossível saber sem ver seu código. Você deve postar seu código de teste como uma pergunta neste site.
Gilles 'SO- stop be evil'

Tentei usar benchmark-run-compiled(com a ideia de que isso reduziria o custo de intern), mas várias vezes obtive tempos de resultados negativos. Eu acho que as duas operações são muito rápidas para medir com segurança.
N
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.