Back to index

indicator-appmenu  12.10.0
hud-performance.c
Go to the documentation of this file.
00001 /*
00002  * Copyright © 2012 Canonical Ltd.
00003  *
00004  * This program is free software: you can redistribute it and/or modify it
00005  * under the terms of the GNU General Public License version 3, as
00006  * published by the Free Software Foundation.
00007  *
00008  * This program is distributed in the hope that it will be useful, but
00009  * WITHOUT ANY WARRANTY; without even the implied warranties of
00010  * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
00011  * PURPOSE.  See the GNU General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU General Public License along
00014  * with this program.  If not, see <http://www.gnu.org/licenses/>.
00015  *
00016  * Author: Ryan Lortie <desrt@desrt.ca>
00017  */
00018 
00019 #include "hudsettings.h"
00020 #include "hudquery.h"
00021 #include "hudtoken.h"
00022 #include "hudsource.h"
00023 
00024 #include <glib-object.h>
00025 
00026 #include "word-list.h"
00027 
00028 /* Max nested depth of menu items */
00029 #define MAX_DEPTH 6
00030 
00031 /* Max number of items per submenu */
00032 #define MAX_ITEMS 20
00033 
00034 /* Max number of words per label.
00035  * NB: keep MAX_WORDS * MAX_DEPTH under 32
00036  */
00037 #define MAX_WORDS 4
00038 
00039 /* Longest word in the word-list (upper bound) */
00040 #define MAX_LETTERS 20
00041 
00042 /* hardcode some parameters for reasons of determinism.
00043  */
00044 HudSettings hud_settings = {
00045   .indicator_penalty = 50,
00046   .add_penalty = 10,
00047   .drop_penalty = 10,
00048   .end_drop_penalty = 1,
00049   .swap_penalty = 15,
00050   .max_distance = 30
00051 };
00052 
00053 typedef struct
00054 {
00055   GObject object;
00056 
00057   GHashTable *items;
00058 } RandomSource;
00059 
00060 typedef GObjectClass RandomSourceClass;
00061 
00062 static void random_source_iface_init (HudSourceInterface *iface);
00063 G_DEFINE_TYPE_WITH_CODE (RandomSource, random_source, G_TYPE_OBJECT,
00064                          G_IMPLEMENT_INTERFACE (HUD_TYPE_SOURCE, random_source_iface_init))
00065 
00066 static void
00067 random_source_search (HudSource    *hud_source,
00068                       GPtrArray    *results_array,
00069                       HudTokenList *search_tokens)
00070 {
00071   RandomSource *source = (RandomSource *) hud_source;
00072   GHashTableIter iter;
00073   gpointer item;
00074 
00075   g_hash_table_iter_init (&iter, source->items);
00076   while (g_hash_table_iter_next (&iter, &item, NULL))
00077     {
00078       HudResult *result;
00079 
00080       result = hud_result_get_if_matched (item, search_tokens, 0);
00081       if (result)
00082         g_ptr_array_add (results_array, result);
00083     }
00084 }
00085 
00086 static void
00087 random_source_ignore_use (HudSource *source)
00088 {
00089 }
00090 
00091 static gchar *
00092 make_word (GRand *rand,
00093            gchar *buffer)
00094 {
00095   const gchar *word;
00096   gint choice;
00097   gint len;
00098 
00099   choice = g_rand_int_range (rand, 0, G_N_ELEMENTS (word_list));
00100   word = word_list[choice];
00101 
00102   while (*word)
00103     *buffer++ = *word++;
00104 
00105   return buffer;
00106 }
00107 
00108 static gchar *
00109 make_words (GRand *rand,
00110             gint   n_words)
00111 {
00112   gchar *buffer;
00113   gchar *ptr;
00114   gint i;
00115 
00116   buffer = g_malloc ((MAX_LETTERS + 1) * n_words);
00117 
00118   ptr = buffer;
00119   for (i = 0; i < n_words; i++)
00120     {
00121       if (i)
00122         *ptr++ = ' ';
00123 
00124       ptr = make_word (rand, ptr);
00125     }
00126 
00127   *ptr = '\0';
00128 
00129   return buffer;
00130 }
00131 
00132 static HudStringList *
00133 random_source_make_name (GRand         *rand,
00134                          HudStringList *context)
00135 {
00136   HudStringList *name;
00137   gchar *label;
00138 
00139   label = make_words (rand, g_rand_int_range (rand, 1, MAX_WORDS + 1));
00140   name = hud_string_list_cons (label, context);
00141   g_free (label);
00142 
00143   return name;
00144 }
00145 
00146 static void
00147 random_source_populate_table (GRand         *rand,
00148                               GHashTable    *items,
00149                               HudStringList *context,
00150                               gint           depth)
00151 {
00152   gint n_items;
00153   gint i;
00154 
00155   n_items = g_rand_int_range (rand, 1, MAX_ITEMS + 1);
00156 
00157   for (i = 0; i < n_items; i++)
00158     {
00159       HudStringList *name;
00160       gboolean is_submenu;
00161       HudItem *item;
00162 
00163       name = random_source_make_name (rand, context);
00164 
00165       if (depth != MAX_DEPTH)
00166         /* Decrease the chances of a particular item being a submenu as we
00167          * go deeper into the menu structure.
00168          */
00169         is_submenu = g_rand_int_range (rand, 0, depth + 1) == 0;
00170       else
00171         /* At the maximum depth, prevent any items from being submenus. */
00172         is_submenu = FALSE;
00173 
00174       item = hud_item_new (name, NULL, NULL, !is_submenu);
00175       g_hash_table_add (items, item);
00176 
00177       if (is_submenu)
00178         random_source_populate_table (rand, items, name, depth + 1);
00179 
00180       hud_string_list_unref (name);
00181     }
00182 }
00183 
00184 static void
00185 random_source_finalize (GObject *object)
00186 {
00187   RandomSource *source = (RandomSource *) object;
00188 
00189   g_hash_table_unref (source->items);
00190 
00191   G_OBJECT_CLASS (random_source_parent_class)
00192     ->finalize (object);
00193 }
00194 
00195 static void
00196 random_source_init (RandomSource *source)
00197 {
00198   source->items = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
00199 }
00200 
00201 static void
00202 random_source_iface_init (HudSourceInterface *iface)
00203 {
00204   iface->use = random_source_ignore_use;
00205   iface->unuse = random_source_ignore_use;
00206   iface->search = random_source_search;
00207 }
00208 
00209 static void
00210 random_source_class_init (RandomSourceClass *class)
00211 {
00212   class->finalize = random_source_finalize;
00213 }
00214 
00215 static HudSource *
00216 random_source_new (GRand *rand)
00217 {
00218   RandomSource *source;
00219 
00220   source = g_object_new (random_source_get_type (), NULL);
00221   random_source_populate_table (rand, source->items, NULL, 0);
00222 
00223   return HUD_SOURCE (source);
00224 }
00225 
00226 void
00227 test_query_performance (void)
00228 {
00229   HudSource *source;
00230   HudQuery *query;
00231   GRand *rand;
00232   gint i;
00233 
00234   rand = g_rand_new_with_seed (1234);
00235   source = random_source_new (rand);
00236 
00237   for (i = 1; i <= 6; i++)
00238     {
00239       guint64 start_time;
00240       gchar *search;
00241       gint j;
00242 
00243       g_print ("\n");
00244 
00245       search = make_words (rand, i);
00246 
00247       /* simulate the user typing it in, one character at a time */
00248       for (j = 1; search[j - 1]; j++)
00249         {
00250           gchar *part_search = g_strndup (search, j);
00251 
00252           start_time = g_get_monotonic_time ();
00253           query = hud_query_new (source, part_search, 1u<<30);
00254           g_print ("%-60s: %dus (%d hits)\n", part_search,
00255                    (int) (g_get_monotonic_time () - start_time),
00256                    hud_query_get_n_results (query));
00257           hud_query_close (query);
00258           g_object_unref (query);
00259           g_free (part_search);
00260         }
00261 
00262       g_free (search);
00263     }
00264 
00265   g_object_unref (source);
00266   g_rand_free (rand);
00267 }
00268 
00269 int
00270 main (int argc, char **argv)
00271 {
00272   g_type_init ();
00273 
00274   g_test_init (&argc, &argv, NULL);
00275   if (g_test_perf ())
00276     g_test_add_func ("/hud/query-performance", test_query_performance);
00277 
00278   return g_test_run ();
00279 }