2010-06-15 2 views
1

Laquelle de ces deux formes d'initialisation de matrice est la meilleure dans Ruby?Laquelle de ces initialisations de tableau est la meilleure dans Ruby?

Méthode 1:

DAYS_IN_A_WEEK = (0..6).to_a 
HOURS_IN_A_DAY = (0..23).to_a 

@data = Array.new(DAYS_IN_A_WEEK.size).map!{ Array.new(HOURS_IN_A_DAY.size) } 

DAYS_IN_A_WEEK.each do |day| 
    HOURS_IN_A_DAY.each do |hour| 
    @data[day][hour] = 'something' 
    end 
end 

Méthode 2:

DAYS_IN_A_WEEK = (0..6).to_a 
HOURS_IN_A_DAY = (0..23).to_a 

@data = {} 

DAYS_IN_A_WEEK.each do |day| 
    HOURS_IN_A_DAY.each do |hour| 
    @data[day] ||= {} 
    @data[day][hour] = 'something' 
    end 
end 

La différence entre le premier procédé et le second procédé est que le second ne pas allouer la mémoire initialement. Je pense que le second est un peu inférieur quand il s'agit de performances en raison de la quantité de copies de tableau qui doit arriver.

Cependant, il n'est pas simple dans Ruby de trouver ce qui se passe. Donc, si quelqu'un peut m'expliquer ce qui est mieux, ce serait vraiment génial!

Merci

+1

Avez-vous envisagé de faire quelque chose comme 'time ruby ​​myscript.rb' pour savoir lequel est le plus rapide? Rappelez-vous le vieil adage "mesurer deux fois, couper une fois" de la menuiserie? En informatique c'est "mesurer une fois, ne perdez pas votre temps à demander". –

+0

@Just MY correct OPINION: Je ne suis pas seulement préoccupé par ce qui est plus rapide, je suis également préoccupé par ce qui se passe dans les coulisses. Voir, en C, Java chaque fois que j'initialise un tableau, je suis sûr que c'est un emplacement de mémoire contiguë saisi en mémoire. Et si une version en croissance dynamique est nécessaire, beaucoup de copies de tableaux se produisent à l'intérieur. Donc, j'étais juste curieux de voir quelle serait la meilleure pratique. – bragboy

+1

@Bragaadeesh: Vous devez apprendre à laisser glisser votre monstre de contrôle interne, sauterelle. : D Assurez-vous d'abord que votre code est correct et maintenable. Ensuite, il faut s'inquiéter des performances, de la maintenabilité nuisible (mais ** jamais ** de la correction!) Dans des zones limitées et bien documentées seulement si nécessaire.Dans un langage comme Ruby, vous pouvez améliorer les performances de façon algorithmique (meilleur choix), en utilisant des astuces de code (moins optimales) ou en sautant dans l'implémentation C si c'est absolument nécessaire (dernier recours désespéré). Faites cela à la fin de la mise en œuvre, cependant, pas avant même d'avoir commencé. –

Répondre

3

Avant de répondre à la question que vous avez posée, je vais répondre à la question que vous auriez dû demander, mais n'a pas:

Q: Dois-je mettre l'accent en rendant mon code lisible en premier, ou devrais-je me concentrer sur la performance en premier?

A: Faites votre code lisible et correct d'abord, puis, et que s'il y a un problème de performance, commencez à vous soucier de la performance par mesure où le problème de performance est premier et seulement puis apporter des modifications à votre code.

Maintenant, pour répondre à la question que vous avez posée, mais ne devrait pas avoir:

method1.rb:

DAYS_IN_A_WEEK = (0..6).to_a 
HOURS_IN_A_DAY = (0..23).to_a 

10000.times do 

    @data = Array.new(DAYS_IN_A_WEEK.size).map!{ Array.new(HOURS_IN_A_DAY.size) } 

    DAYS_IN_A_WEEK.each do |day| 
    HOURS_IN_A_DAY.each do |hour| 
     @data[day][hour] = 'something' 
    end 
    end 

end 

method2.rb:

Résultats de référence de mort cérébrale :

$ time ruby method1.rb 

real 0m1.189s 
user 0m1.140s 
sys 0m0.000s 

$ time ruby method2.rb 

real 0m1.879s 
user 0m1.780s 
sys 0m0.020s 

me semble que l'utilisation du temps de l'utilisateur (le facteur important) a method1.rb beaucoup plus rapide. Vous, bien sûr, ne devriez pas faire confiance à ce point de référence et devriez faire votre propre reflet de votre utilisation réelle du code. Ceci, cependant, est quelque chose que vous devriez faire seulement après vous avez déterminé quel code est votre goulot d'étranglement de performance dans la réalité. (Astuce: 99,44% des programmeurs informatiques sont 100% mauvais quand ils devinent où leurs goulets d'étranglement sont sans mesure!)

+0

J'ai compris ce que vous vouliez dire. – bragboy

2

J'enveloppé les deux extraits de code dans des méthodes distinctes et fait une analyse comparative. Voici les résultats:

Benchmark.bm(7) do |x| 
    x.report ("method1") { 100000.times { method1 } } 
    x.report ("method2") { 100000.times { method2 } } 
end 

      user  system  total  real 
method1 11.370000 0.010000 11.380000 (11.392233) 
method2 17.920000 0.010000 17.930000 (18.328318) 
+0

Hey merci beaucoup !! Je déchiffre que le premier est presque deux fois aussi bon que le second !! – bragboy

3

Quel est le problème avec juste

@data = Array.new(7) { Array.new(24) { 'something' }} 

Ou, si vous êtes content d'avoir l'objet même partout:

@data = Array.new(7) { Array.new(24, 'something') } 

Il est beaucoup plus rapide, pas que ce serait la matière. Il est également beaucoup plus lisible, ce qui est la chose la plus importante. Après tout, le but du code est de communiquer l'intention aux autres parties prenantes, et non de communiquer avec l'ordinateur.

    user system  total  real 
method1 8.969000 0.000000 8.969000 (9.059570) 
method2 16.547000 0.000000 16.547000 (16.799805) 
method3 6.468000 0.000000 6.468000 (6.616211) 
method4 0.969000 0.015000 0.984000 (1.021484)
Cette dernière ligne montre également une autre chose intéressante: le temps d'exécution est dominé par le temps nécessaire pour créer les chaînes 7 * 24 * 100000 = 16.8 millions 'something'.

Et bien sûr, il y a une autre remarque importante: vos method1 et method2 que vous comparez les uns aux autres font deux choses complètement différentes! Cela n'a même pas de sens de les comparer les uns aux autres. method1 crée un Array, method2 crée un Hash.

Votre method1 est équivalent à mon premier exemple ci-dessus:

@data = Array.new(7) { Array.new(24) { 'something' }} 

Alors que method2 est (très à peu près) équivalent à:

@data = Hash.new {|h, k| h[k] = Hash.new {|h, k| h[k] = 'something' }} 

Eh bien, sauf que votremethod2 initialise le Hash entier, alors que ma méthode exécute paresseusement le code d'initialisation dans le cas d'un non initial La clé est lue.

En d'autres termes, après l'exécution du code d'initialisation ci-dessus, le Hash est encore vide:

@data # => {} 

Mais chaque fois que vous essayez d'accéder à une clé, il apparaîtra comme par magie:

@data[5][17] # => 'something' 

Et il restera là:

@data # => {5 => {17 => 'something'}} 

Depuis ce code ne est pas réel Initialisez le Hash, il est évidemment bien plus rapide:

    user system  total  real 
method5 0.266000 0.000000 0.266000 (0.296875)

+0

Salut Jörg !! J'attendais 'votre' réponse. La raison pour laquelle je mets quelque chose, c'est que je fais quelque chose là-bas. Disons simplement que ce n'est pas une valeur constante à chaque fois. Et merci d'avoir éclairci le concept Hash vs Array dans Ruby. – bragboy

Questions connexes