Improve performance of price editor dialog.

Add new functions to get the number of prices and the get a price by
index for a given commodity.  Use these instead of building a list of all
prices several times for each price.
This commit is contained in:
Mike Alexander 2015-11-09 23:37:43 -05:00
parent 601abdf47d
commit ed776a73f0
3 changed files with 158 additions and 21 deletions

View File

@ -1407,11 +1407,21 @@ gnc_pricedb_remove_old_prices(GNCPriceDB *db,
/* ==================================================================== */
/* lookup/query functions */
static PriceList *pricedb_price_list_merge (PriceList *a, PriceList *b);
static void
hash_values_helper(gpointer key, gpointer value, gpointer data)
{
GList ** l = data;
*l = g_list_concat(*l, g_list_copy (value));
if (*l)
{
GList *new_l;
new_l = pricedb_price_list_merge(*l, value);
g_list_free (*l);
*l = new_l;
}
else
*l = g_list_copy (value);
}
static PriceList *
@ -1511,10 +1521,7 @@ pricedb_get_prices_internal(GNCPriceDB *db, const gnc_commodity *commodity,
}
}
}
if (forward_list && !currency)
/* Multiple currencies, reverse_list doesn't exist and
forward_list must be sorted */
forward_list = g_list_sort (forward_list, compare_prices_by_date);
return forward_list;
}
@ -1933,7 +1940,124 @@ gnc_pricedb_get_prices(GNCPriceDB *db,
return result;
}
/* Return the number of prices in the data base for the given commodity
*/
static void
price_count_helper(gpointer key, gpointer value, gpointer data)
{
int *result = data;
GList *price_list = value;
*result += g_list_length(price_list);
}
int
gnc_pricedb_num_prices(GNCPriceDB *db,
const gnc_commodity *c)
{
int result = 0;
GHashTable *currency_hash;
if (!db || !c) return 0;
ENTER ("db=%p commodity=%p", db, c);
currency_hash = g_hash_table_lookup(db->commodity_hash, c);
if (currency_hash)
{
g_hash_table_foreach(currency_hash, price_count_helper, (gpointer)&result);
}
LEAVE ("count=%d", result);
return result;
}
/* Return the nth price for the given commodity
*/
GNCPrice *
gnc_pricedb_nth_price (GNCPriceDB *db,
const gnc_commodity *c,
const int n)
{
GNCPrice *result = NULL;
GHashTable *currency_hash;
if (!db || !c || n < 0) return NULL;
ENTER ("db=%p commodity=%p index=%d", db, c, n);
currency_hash = g_hash_table_lookup(db->commodity_hash, c);
if (currency_hash)
{
int num_currencies = g_hash_table_size(currency_hash);
if (num_currencies == 1)
{
/* Optimize the case of prices in only one currency, it's common
and much faster */
GHashTableIter iter;
gpointer key, value;
g_hash_table_iter_init(&iter, currency_hash);
if (g_hash_table_iter_next(&iter, &key, &value))
{
PriceList *prices = value;
result = g_list_nth_data(prices, n);
}
}
else if (num_currencies > 1)
{
/* Prices for multiple currencies, must find the nth entry in the
merged currency list. */
GList **price_array = (GList **)g_new(gpointer, num_currencies);
GList **next_list;
int i, j;
GHashTableIter iter;
gpointer key, value;
/* Build an array of all the currencies this commodity has prices for */
for (i = 0, g_hash_table_iter_init(&iter, currency_hash);
g_hash_table_iter_next(&iter, &key, &value) && i < num_currencies;
i++)
{
price_array[i] = value;
}
/* Iterate n times to get the nth price, each time finding the currency
with the latest price */
for (i = 0; i <= n; i++)
{
next_list = NULL;
for (j = 0; j < num_currencies; j++)
{
/* Save this entry if it's the first one or later than
the saved one. */
if (price_array[j] != NULL &&
(next_list == NULL || *next_list == NULL ||
compare_prices_by_date((*next_list)->data, (price_array[j])->data) > 0))
{
next_list = &price_array[j];
}
}
/* next_list points to the list with the latest price unless all
the lists are empty */
if (next_list && *next_list)
{
result = (*next_list)->data;
*next_list = (*next_list)->next;
}
else
{
/* all the lists are empty, "n" is greater than the number
of prices for this commodity. */
result = NULL;
break;
}
}
g_free(price_array);
}
}
LEAVE ("price=%p", result);
return result;
}
GNCPrice *
gnc_pricedb_lookup_day(GNCPriceDB *db,
const gnc_commodity *c,

View File

@ -587,6 +587,27 @@ gboolean gnc_pricedb_foreach_price(GNCPriceDB *db,
gpointer user_data,
gboolean stable_order);
/** @brief Get the number of prices, in any currency, for a given commodity.
* @param db The pricedb
* @param c The commodity
* @return The number of prices in the database for this commody, zero if none
*/
int
gnc_pricedb_num_prices(GNCPriceDB *db,
const gnc_commodity *c);
/** @brief Get the nth price for the given commodity in reverse date order
* @param db The pricedb
* @param c The commodity whose nth price is needed
* @param n Zero based index of the price wanted
* @return The nth price for this commodity in reverse chronological order, without
* regard for what currency the price is in
*/
GNCPrice *
gnc_pricedb_nth_price (GNCPriceDB *db,
const gnc_commodity *c,
const int n);
/* The following two convenience functions are used to test the xml backend */
/** @brief Return the number of prices in the database.
*

View File

@ -594,10 +594,8 @@ gnc_tree_model_price_get_iter (GtkTreeModel *tree_model,
}
/* Verify the third part of the path: the price. */
price_list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL);
i = gtk_tree_path_get_indices (path)[2];
price = g_list_nth_data (price_list, i);
gnc_price_list_destroy(price_list);
price = gnc_pricedb_nth_price(priv->price_db, commodity, i);
/* There's a race condition here that I can't resolve.
* Comment this check out for now, and we'll handle the
* resulting problem elsewhere. */
@ -860,9 +858,7 @@ gnc_tree_model_price_iter_next (GtkTreeModel *tree_model,
{
commodity = gnc_price_get_commodity((GNCPrice*)iter->user_data2);
n = GPOINTER_TO_INT(iter->user_data3) + 1;
list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL);
iter->user_data2 = g_list_nth_data(list, n);
gnc_price_list_destroy(list);
iter->user_data2 = gnc_pricedb_nth_price(priv->price_db, commodity, n);
if (iter->user_data2 == NULL)
{
LEAVE("no next iter");
@ -936,18 +932,18 @@ gnc_tree_model_price_iter_children (GtkTreeModel *tree_model,
if (parent->user_data == ITER_IS_COMMODITY)
{
GNCPrice *price;
commodity = (gnc_commodity *)parent->user_data2;
list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL);
if (list == NULL)
price = gnc_pricedb_nth_price(priv->price_db, commodity, 0);
if (price == NULL)
{
LEAVE("no prices");
return FALSE;
}
iter->stamp = model->stamp;
iter->user_data = ITER_IS_PRICE;
iter->user_data2 = g_list_nth_data(list, 0);
iter->user_data2 = price;
iter->user_data3 = GINT_TO_POINTER(0);
gnc_price_list_destroy(list);
LEAVE("price iter %p (%s)", iter, iter_to_string(model, iter));
return TRUE;
}
@ -1038,9 +1034,7 @@ gnc_tree_model_price_iter_n_children (GtkTreeModel *tree_model,
if (iter->user_data == ITER_IS_COMMODITY)
{
commodity = (gnc_commodity *)iter->user_data2;
list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL);
n = g_list_length(list);
gnc_price_list_destroy(list);
n = gnc_pricedb_num_prices(priv->price_db, commodity);
LEAVE("price list length %d", n);
return n;
}
@ -1099,13 +1093,11 @@ gnc_tree_model_price_iter_nth_child (GtkTreeModel *tree_model,
if (parent->user_data == ITER_IS_COMMODITY)
{
commodity = (gnc_commodity *)parent->user_data2;
list = gnc_pricedb_get_prices(priv->price_db, commodity, NULL);
iter->stamp = model->stamp;
iter->user_data = ITER_IS_PRICE;
iter->user_data2 = g_list_nth_data(list, n);
iter->user_data2 = gnc_pricedb_nth_price(priv->price_db, commodity, n);
iter->user_data3 = GINT_TO_POINTER(n);
gnc_price_list_destroy(list);
LEAVE("price iter %p (%s)", iter, iter_to_string(model, iter));
return iter->user_data2 != NULL;
}