run how globally ruby performance rubocop

ruby - globally - how to run rubocop



¿Por qué RuboCop sugiere reemplazar.times.map con Array.new? (3)

RuboCop sugiere:

Use Array.new con un bloque en lugar de .times.map.

En los docs para la policía:

Esta policía comprueba las llamadas .times.map. En la mayoría de los casos, dichas llamadas pueden reemplazarse con una creación de matriz explícita.

Ejemplos:

# bad 9.times.map do |i| i.to_s end # good Array.new(9) do |i| i.to_s end

Sé que se puede reemplazar, pero creo que 9.times.map está más cerca de la gramática inglesa y es más fácil entender lo que hace el código.

¿Por qué debería ser reemplazado?


Cuando necesita asignar el resultado de un bloque invocado una cantidad fija de veces, tiene una opción entre:

Array.new(n) { ... }

y:

n.times.map { ... }

El último es un 60% más lento para n = 10 , que se reduce a alrededor del 40% para n > 1_000 .

Nota: escala logarítmica!


El último es más eficaz, aquí hay una explicación: Solicitud de extracción donde se agregó esta policía

Comprueba llamadas como esta:

9.times.map { |i| f(i) } 9.times.collect(&foo)

y sugiere usar esto en su lugar:

Array.new(9) { |i| f(i) } Array.new(9, &foo)

El nuevo código tiene aproximadamente el mismo tamaño, pero utiliza menos métodos, consume menos memoria, funciona un poco más rápido y, en mi opinión, es más legible.

He visto muchas veces: {map, collect} en diferentes proyectos conocidos: Rails, GitLab, Rubocop y varias aplicaciones de código cerrado.

Puntos de referencia:

Benchmark.ips do |x| x.report(''times.map'') { 5.times.map{} } x.report(''Array.new'') { Array.new(5){} } x.compare! end __END__ Calculating ------------------------------------- times.map 21.188k i/100ms Array.new 30.449k i/100ms ------------------------------------------------- times.map 311.613k (± 3.5%) i/s - 1.568M Array.new 590.374k (± 1.2%) i/s - 2.954M Comparison: Array.new: 590373.6 i/s times.map: 311612.8 i/s - 1.89x slower

Ahora no estoy seguro de que Lint sea el espacio de nombres correcto para la policía. Avísame si debo moverlo a Performance.

Además, no implementé la corrección automática porque potencialmente puede romper el código existente, por ejemplo, si alguien tiene el método Fixnum # times redefinido para hacer algo sofisticado. Aplicar la autocorrección rompería su código.


Si sientes que es más legible ve con ello.

Esta es una regla de rendimiento y la mayoría de los códigos de ruta en su aplicación probablemente no sean críticos para el rendimiento. Personalmente, siempre estoy abierto a favorecer la legibilidad sobre la optimización prematura.

Que dicho

100.times.map { ... }

  • times crea un objeto Enumerator
  • map enumera sobre ese objeto sin poder optimizarlo, por ejemplo, el tamaño de la matriz no se conoce por adelantado y podría tener que reasignar más espacio dinámicamente y tiene que enumerar los valores al llamar Enumerable#each ya que el map se implementa de esa manera

Mientras

Array.new(100) { ... }

  • new asigna una matriz de tamaño N
  • Y luego usa un bucle nativo para completar los valores