MOD amélioration de la vitesse de euphorik_bd:messages (mais toujours en O(n)). Cette...
authorGreg Burri <greg.burri@gmail.com>
Sat, 4 Oct 2008 14:03:29 +0000 (14:03 +0000)
committerGreg Burri <greg.burri@gmail.com>
Sat, 4 Oct 2008 14:03:29 +0000 (14:03 +0000)
modules/Makefile
modules/erl/euphorik_bd.erl
modules/erl/euphorik_bd_admin.erl
modules/erl/euphorik_test.erl
modules/include/euphorik_bd.hrl
tools/start_yaws.sh

index 0a77892..859cc56 100755 (executable)
@@ -41,7 +41,7 @@ $(rep_ebin)/euphorik_bd.beam: $(rep_erl)/euphorik_bd.erl $(rep_include)/euphorik
        erlc $(erlc_params)
    
 # Module pour la mise à jour de la BD
-$(rep_ebin)/euphorik_bd_admin.beam: $(rep_erl)/euphorik_bd_admin.erl $(rep_erl)/euphorik_bd.erl $(rep_include)/euphorik_bd.hrl $(rep_include)/euphorik_defines.hrl
+$(rep_ebin)/euphorik_bd_admin.beam: $(rep_erl)/euphorik_bd_admin.erl $(rep_include)/euphorik_bd.hrl $(rep_include)/euphorik_defines.hrl
        erlc $(erlc_params)
 
 # Module permettant l'extraction des conversations du minichat
@@ -61,7 +61,7 @@ $(rep_ebin)/euphorik_protocole.beam: $(rep_erl)/euphorik_protocole.erl $(rep_inc
 #      erlc $(erlc_params)
       
 # Module effectuant periodiquement certaines tâches
-$(rep_ebin)/euphorik_daemon.beam: $(rep_erl)/euphorik_daemon.erl $(rep_include)/euphorik_defines.hrl $(rep_erl)/euphorik_bd_admin.erl
+$(rep_ebin)/euphorik_daemon.beam: $(rep_erl)/euphorik_daemon.erl $(rep_include)/euphorik_defines.hrl
        erlc $(erlc_params)
    
 # Module avec plein de bordel dedant
@@ -69,7 +69,7 @@ $(rep_ebin)/euphorik_common.beam: $(rep_erl)/euphorik_common.erl
        erlc $(erlc_params)
    
 # Module dédié au tests
-$(rep_ebin)/euphorik_test.beam: $(rep_erl)/euphorik_test.erl $(rep_erl)/euphorik_bd.erl $(rep_include)/euphorik_bd.hrl
+$(rep_ebin)/euphorik_test.beam: $(rep_erl)/euphorik_test.erl $(rep_include)/euphorik_bd.hrl
        erlc $(erlc_params)
 
 # Suppression des modules compilés
index cfd0bfd..4fecb73 100755 (executable)
@@ -275,13 +275,16 @@ user_by_mess(Id) ->
 nouveau_message(Mess, Auteur_id, Repond_A_ids) ->\r
    % regarde si les id 'Repond_A' existent\r
    F = fun() ->         \r
-      Repond_a = lists:map(\r
-         fun(Repond_a_id) ->\r
-            [M] = mnesia:read({minichat, Repond_a_id}),\r
-            M\r
-         end,\r
+      Repond_a = lists:foldr(\r
+         fun(Repond_a_id, Acc) ->\r
+            case mnesia:read({minichat, Repond_a_id}) of
+               [M] -> [M | Acc];\r
+               _ -> Acc % le message n'est pas trouvé
+            end\r
+         end,
+         [],\r
          Repond_A_ids\r
-      ),      \r
+      ),\r
       Racine_id = case Repond_a of\r
          [] -> undefined;\r
          [M | _] -> \r
@@ -293,16 +296,16 @@ nouveau_message(Mess, Auteur_id, Repond_A_ids) ->
                _ ->\r
                   {erreur, "Les messages ne font pas partie de la même conversation"}\r
             end\r
-      end,\r
-      case Racine_id of\r
-         {erreur, E} -> {erreur, E};\r
-         _ ->\r
-            % est-ce que l'auteur existe ?\r
-            case mnesia:wread({user, Auteur_id}) of\r
-               [#user{profile = Profile} = Auteur] ->\r
-                  if length(Repond_a) =/= length(Repond_A_ids) ->\r
-                        {erreur, "Un ou plusieurs messages introuvable"};\r
-                     true ->\r
+      end,      
+      if length(Repond_a) =/= length(Repond_A_ids) ->
+            {erreur, "Un ou plusieurs messages introuvable"};
+         true ->\r
+            case Racine_id of\r
+               {erreur, E} -> {erreur, E};\r
+               _ ->\r
+                  % est-ce que l'auteur existe ?\r
+                  case mnesia:wread({user, Auteur_id}) of\r
+                     [#user{profile = Profile} = Auteur] ->\r
                         % comparaison entre la date du dernier poste et maintenant (gestion du flood)\r
                         Now = now(),\r
                         Delta = euphorik_common:delta_date_ms(Auteur#user.date_derniere_connexion, Now),\r
@@ -330,11 +333,11 @@ nouveau_message(Mess, Auteur_id, Repond_A_ids) ->
                                  racine_id = if Racine_id =:= undefined -> Id; true -> Racine_id end\r
                               }),\r
                               Id\r
-                        end\r
-                  end;\r
-               _ ->\r
-                  {erreur, "L'auteur du message est introuvable"}\r
-            end\r
+                        end;\r
+                     _ ->\r
+                        {erreur, "L'auteur du message est introuvable"}\r
+                  end\r
+            end
       end\r
    end,\r
    resultat_transaction(mnesia:transaction(F)).\r
@@ -365,26 +368,29 @@ messages(N) ->
    messages(N, 1).\r
 \r
 \r
-% Renvoie N messages se trouvant sur la page P\r
+% Renvoie N messages se trouvant sur la page P
+% TODO FIXME : fonction en O(N * P) !\r
 messages(N, P) ->\r
-   F = fun() ->\r
-      C = cursor(\r
-         qlc:keysort(\r
-            #minichat.id, \r
-            q([E#minichat{contenu = contenu_message(E)} || E <- mnesia:table(minichat)]),\r
-            [{order, descending}]\r
-         ),\r
-         [{tmpdir, ?KEY_SORT_TEMP_DIR}]\r
-      ),\r
-      if P > 1 -> qlc:next_answers(C, N * (P - 1));\r
-         true -> ok\r
-      end,\r
-      R = qlc:next_answers(C, N),\r
-      qlc:delete_cursor(C),\r
-      R\r
+   F = fun() ->
+      % % #minichat{contenu = contenu_message(E)}
+      get_tuples_avant(minichat, reculer(minichat, mnesia:last(minichat), N * (P - 1)), N)     \r
    end,\r
-   lists:reverse(resultat_transaction(mnesia:transaction(F))).\r
-\r
+   resultat_transaction(mnesia:transaction(F)).
+   
+get_tuples_avant(Table, Id, N) ->
+   get_tuples_avant(Table, Id, N, []).
+get_tuples_avant(_, '$end_of_table', _, Tuples) -> Tuples;
+get_tuples_avant(_, _, 0, Tuples) ->
+   Tuples;
+get_tuples_avant(Table, Id, N, Tuples) ->
+   [T] = mnesia:read({Table, Id}),
+   get_tuples_avant(Table, mnesia:prev(Table, Id), N - 1, [T | Tuples]).
+
+reculer(_, '$end_of_table' = Fin, _) -> Fin;
+reculer(_, Id, 0) -> Id;
+reculer(Table, Id, N) ->
+   reculer(Table, mnesia:prev(Table, Id), N - 1).
+   \r
 \r
 % Renvoie les messages manquants pour la page P en sachant qu'il y a N message\r
 % par page et que le dernier message que l'on possède est Id\r
index 64ea4ac..a5a69f1 100644 (file)
@@ -84,6 +84,7 @@ create_tables() ->
       {disc_copies, [node()]}
    ]),
    mnesia:create_table(minichat, [
+      {type, ordered_set},
       {attributes, record_info(fields, minichat)},
       {disc_copies, [node()]}
    ]),
index c1bf49b..2cbc1ec 100644 (file)
@@ -22,6 +22,7 @@
 
 -module(euphorik_test).
 -export([
+   bench_write_minichat/1,
    start/2,
    stop/1,
    bench_get_messages/0,
@@ -34,6 +35,7 @@
 -define(INTERVALLE_MIN, 2).\r
 -define(INTERVALLE_MAX, 5).
 
+
 % N est le nombre d'utilisateur
 % M est le nombre de message que chaque utilisateur va poster
 start(N, M) ->
@@ -55,7 +57,7 @@ start(N, M) ->
 stop(Pids) ->
    lists:foreach(fun(Pid) -> exit(Pid, kill) end, Pids).
    
-
+% des trucs qui trainent
 bench_get_messages() ->
    T = [
       {page,"chat"},
@@ -67,8 +69,6 @@ bench_get_messages() ->
       {conversations,{array,[]}}
    ],
    moyenne_temps(euphorik_protocole, wait_event, [T], 20).
-
-
 bench_get_messages_avec_2_conversations() ->
    T = [
       {page,"chat"},
@@ -91,8 +91,6 @@ bench_get_messages_avec_2_conversations() ->
       ]}}
    ],
    moyenne_temps(euphorik_protocole, wait_event, [T], 20).
-
-   
 moyenne_temps(Module, Fun, Args, N) ->
    moyenne_temps(Module, Fun, Args, N, N, 0).
 moyenne_temps(_, _, _, 0, Total, Temps_acc) ->
@@ -129,9 +127,9 @@ mot_rand(L, Mot) ->
 % Tire au hasard de 0 à 3 messages sur les 10 derniers postés, renvoie une liste de int()
 % répartition : 
 %  0 : 0.1
-%  1 : 0.7
-%  2 : 0.15
-%  3 : 0.05
+%  1 : 0.95
+%  2 : 0.04
+%  3 : 0.01
 messages_id_rand() ->
    R = random:uniform(),
    if R =< 0.1 ->
@@ -139,9 +137,9 @@ messages_id_rand() ->
       true ->
          Messages = lists:map(fun(#minichat{id = Id}) -> Id end, euphorik_bd:messages(8)),
          if
-            R > 0.1 andalso R =< 0.8 ->
+            R > 0.1 andalso R =< 0.95 ->
                tire_element_rand(1, Messages);
-            R > 0.8 andalso R =< 0.95 ->
+            R > 0.95 andalso R =< 0.99 ->
                tire_element_rand(2, Messages);
             true ->
                tire_element_rand(3, Messages)
@@ -170,9 +168,8 @@ loop(User_id, M) ->
    % attend un temp aléatoire compris entre INTERVALLE_MIN sec et INTERVALLE_MAX sec
    timer:sleep(1000 * (random:uniform(?INTERVALLE_MAX - ?INTERVALLE_MIN + 1) + ?INTERVALLE_MIN - 1)),
    % poste un message aléatoire par une personne aléatoire répondant à des messages aléatoires
-   {Message, Repond_a} = {message_rand(), messages_id_rand()},\r
-   %{Message, Repond_a} = {"blablablablablabla", []},
-   io:format("~p poste ~p et repond a ~w~n", [User_id, Message, Repond_a]),
+   {Message, Repond_a} = {message_rand(), messages_id_rand()},
+   % io:format("~p poste ~p et repond a ~w~n", [User_id, Message, Repond_a]),   
    case euphorik_bd:nouveau_message(Message, User_id, Repond_a) of
       {erreur, E} -> 
          io:format("~p : erreur : ~p~n", [User_id, E]),
@@ -181,4 +178,35 @@ loop(User_id, M) ->
          loop(User_id, M - 1)
    end.
    
+   
+% Permet de tester la vitesse d'écriture en fonction de la 
+% taille de la BD
+% voir : http://erlang.org/pipermail/erlang-questions/2008-October/038697.html
+bench_write_minichat(Filename) ->
+   Times = bench_write_minichat(1, []),
+   {ok, File} = file:open(Filename, [write]),
+   lists:foreach(
+      fun({Id, Time}) ->
+         io:format(File, "~w ~w~n", [Id, Time])
+      end,
+      Times
+   ),
+   file:close(File).   
+bench_write_minichat(100000, Temps) -> Temps;
+bench_write_minichat(N, Temps) ->
+   {T, _} = timer:tc(mnesia, transaction, [fun() ->
+      Id = mnesia:dirty_update_counter(counter, minichat, 1),
+      mnesia:write(#minichat{
+         id = Id,
+         auteur_id = random:uniform(10000),
+         date = now(),
+         pseudo = "Test",
+         contenu = "Blabla blabla bla.",
+         racine_id = random:uniform(10000)
+      })
+   end]),
+   bench_write_minichat(N + 1, if N rem 500 =:= 0 -> [{N, T} | Temps]; true -> Temps end).
+   
+
+   
    
\ No newline at end of file
index ae69459..dbca45b 100755 (executable)
@@ -1,3 +1,4 @@
+% coding: utf-8
 % Copyright 2008 Grégory Burri
 %
 % This file is part of Euphorik.
@@ -82,6 +83,8 @@
    }).\r
    \r
    
+   
+   
 -record(user,
    {
       id,
index 87e6e4e..29be884 100755 (executable)
@@ -1,3 +1,4 @@
-#!/bin/bash\r
+#!/bin/bash
 # screen est utilisé par exemple pour lancé une version de preproduction et permettre de la faire tourner après un délog
-screen -S yawspreprod yaws --conf ./yaws.conf --sname yaws_dev --mnesiadir "../var/BD/" -I debian_yaws_dev
+#screen -S yawspreprod
+yaws --conf ./yaws.conf --sname yaws_dev --mnesiadir "../BD/" -I debian_yaws_dev