Skip to content

Commit f7ab6ae

Browse files
committed
bin: optimize the removal of element from the bin
tests/gstbin: add performance-test for add and remove
1 parent 7ddd3c6 commit f7ab6ae

File tree

2 files changed

+115
-65
lines changed

2 files changed

+115
-65
lines changed

subprojects/gstreamer/gst/gstbin.c

Lines changed: 76 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,13 @@ struct _GstBinPrivate
183183
* hits this number */
184184
gint numchildren_use_hash;
185185

186+
/* keep tabs on our childrens properties */
187+
gint num_is_sink;
188+
gint num_is_src;
189+
gint num_provides_clock;
190+
gint num_requires_clock;
191+
GHashTable *no_preroll_elements;
192+
GHashTable *children_links;
186193
};
187194

188195
typedef struct
@@ -517,6 +524,9 @@ gst_bin_init (GstBin * bin)
517524
bin->priv->asynchandling = DEFAULT_ASYNC_HANDLING;
518525
bin->priv->structure_cookie = 0;
519526
bin->priv->message_forward = DEFAULT_MESSAGE_FORWARD;
527+
528+
bin->priv->no_preroll_elements = g_hash_table_new (NULL, NULL);
529+
bin->priv->children_links = g_hash_table_new (NULL, NULL);
520530
}
521531

522532
static void
@@ -559,6 +569,9 @@ gst_bin_dispose (GObject * object)
559569
GST_STR_NULL (GST_OBJECT_NAME (object)));
560570
}
561571

572+
g_hash_table_destroy (bin->priv->no_preroll_elements);
573+
g_hash_table_destroy (bin->priv->children_links);
574+
562575
G_OBJECT_CLASS (parent_class)->dispose (object);
563576
}
564577

@@ -1185,6 +1198,7 @@ gst_bin_do_deep_add_remove (GstBin * bin, gint sig_id, const gchar * sig_name,
11851198
static gboolean
11861199
gst_bin_add_func (GstBin * bin, GstElement * element)
11871200
{
1201+
GstBinPrivate *priv = bin->priv;
11881202
gchar *elem_name;
11891203
GstIterator *it;
11901204
GList *walk;
@@ -1207,18 +1221,18 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
12071221

12081222
/* Check whether we need to switch to using the hash */
12091223
GST_OBJECT_LOCK (bin);
1210-
if ((bin->priv->children_hash == NULL) &&
1211-
(bin->priv->numchildren_use_hash >= 0) &&
1212-
(bin->numchildren + 1) >= (bin->priv->numchildren_use_hash)) {
1224+
if ((priv->children_hash == NULL) &&
1225+
(priv->numchildren_use_hash >= 0) &&
1226+
(bin->numchildren + 1) >= (priv->numchildren_use_hash)) {
12131227
/* Time to switch to using the hash */
1214-
bin->priv->children_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
1228+
priv->children_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
12151229

12161230
/* Initialise each existing element into the hash */
12171231
walk = bin->children;
12181232
for (; walk; walk = g_list_next (walk)) {
12191233
GstElement *child;
12201234
child = GST_ELEMENT_CAST (walk->data);
1221-
g_hash_table_insert (bin->priv->children_hash, g_strdup (GST_OBJECT_NAME(child)), child);
1235+
g_hash_table_insert (priv->children_hash, g_strdup (GST_OBJECT_NAME(child)), child);
12221236
}
12231237
}
12241238

@@ -1230,10 +1244,10 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
12301244
* you can safely change the element name after this check and before setting
12311245
* the object parent. The window is very small though... */
12321246

1233-
if (bin->priv->children_hash != NULL) {
1247+
if (priv->children_hash != NULL) {
12341248
/* Use the hash table to look up whether we already have an element with that
12351249
* name */
1236-
if (g_hash_table_contains (bin->priv->children_hash, elem_name)) {
1250+
if (g_hash_table_contains (priv->children_hash, elem_name)) {
12371251
goto duplicate_name;
12381252
}
12391253
} else {
@@ -1248,40 +1262,50 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
12481262
GST_OBJECT_CAST (bin))))
12491263
goto had_parent;
12501264

1265+
if (is_sink)
1266+
priv->num_is_sink++;
1267+
if (is_source)
1268+
priv->num_is_src++;
1269+
if (provides_clock)
1270+
priv->num_provides_clock++;
1271+
if (requires_clock)
1272+
priv->num_requires_clock++;
1273+
12511274
/* if we add a sink we become a sink */
1252-
if (is_sink && !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_SINK)) {
1275+
if (is_sink && !(priv->suppressed_flags & GST_ELEMENT_FLAG_SINK)) {
12531276
GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "element \"%s\" was sink",
12541277
elem_name);
12551278
GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_FLAG_SINK);
12561279
}
1257-
if (is_source && !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_SOURCE)) {
1280+
if (is_source && !(priv->suppressed_flags & GST_ELEMENT_FLAG_SOURCE)) {
12581281
GST_CAT_DEBUG_OBJECT (GST_CAT_PARENTAGE, bin, "element \"%s\" was source",
12591282
elem_name);
12601283
GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_FLAG_SOURCE);
12611284
}
12621285
if (provides_clock
1263-
&& !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_PROVIDE_CLOCK)) {
1286+
&& !(priv->suppressed_flags & GST_ELEMENT_FLAG_PROVIDE_CLOCK)) {
12641287
GST_DEBUG_OBJECT (bin, "element \"%s\" can provide a clock", elem_name);
12651288
clock_message =
12661289
gst_message_new_clock_provide (GST_OBJECT_CAST (element), NULL, TRUE);
12671290
GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
12681291
}
12691292
if (requires_clock
1270-
&& !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_REQUIRE_CLOCK)) {
1293+
&& !(priv->suppressed_flags & GST_ELEMENT_FLAG_REQUIRE_CLOCK)) {
12711294
GST_DEBUG_OBJECT (bin, "element \"%s\" requires a clock", elem_name);
12721295
GST_OBJECT_FLAG_SET (bin, GST_ELEMENT_FLAG_REQUIRE_CLOCK);
12731296
}
12741297

12751298
bin->children = g_list_prepend (bin->children, element);
1299+
g_hash_table_insert (priv->children_links, element, bin->children);
12761300

1277-
if (bin->priv->children_hash != NULL) {
1278-
g_hash_table_insert (bin->priv->children_hash, g_strdup (elem_name), element);
1301+
if (priv->children_hash != NULL) {
1302+
g_hash_table_insert (priv->children_hash, g_strdup (elem_name), element);
12791303
}
12801304

12811305
bin->numchildren++;
12821306
bin->children_cookie++;
12831307
if (!GST_BIN_IS_NO_RESYNC (bin))
1284-
bin->priv->structure_cookie++;
1308+
priv->structure_cookie++;
12851309

12861310
/* distribute the bus */
12871311
gst_element_set_bus (element, bin->child_bus);
@@ -1352,6 +1376,7 @@ gst_bin_add_func (GstBin * bin, GstElement * element)
13521376
case GST_STATE_CHANGE_NO_PREROLL:
13531377
/* ignore all async elements we might have and commit our state */
13541378
bin_handle_async_done (bin, ret, FALSE, GST_CLOCK_TIME_NONE);
1379+
g_hash_table_add (priv->no_preroll_elements, element);
13551380
break;
13561381
case GST_STATE_CHANGE_FAILURE:
13571382
break;
@@ -1611,10 +1636,11 @@ gst_bin_add (GstBin * bin, GstElement * element)
16111636
static gboolean
16121637
gst_bin_remove_func (GstBin * bin, GstElement * element)
16131638
{
1639+
GstBinPrivate *priv = bin->priv;
16141640
gchar *elem_name;
16151641
GstIterator *it;
1616-
gboolean is_sink, is_source, provides_clock, requires_clock;
1617-
gboolean othersink, othersource, otherprovider, otherrequirer, found;
1642+
gboolean is_sink, is_source, provides_clock, requires_clock, no_preroll;
1643+
gboolean found;
16181644
GstMessage *clock_message = NULL;
16191645
GstClock **provided_clock_p;
16201646
GstElement **clock_provider_p;
@@ -1644,52 +1670,25 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
16441670
GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
16451671
requires_clock =
16461672
GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_REQUIRE_CLOCK);
1673+
no_preroll = GST_STATE_RETURN (element) == GST_STATE_CHANGE_NO_PREROLL;
16471674
GST_OBJECT_UNLOCK (element);
16481675

1676+
if (!no_preroll)
1677+
g_hash_table_remove (priv->no_preroll_elements, element);
1678+
16491679
found = FALSE;
1650-
othersink = FALSE;
1651-
othersource = FALSE;
1652-
otherprovider = FALSE;
1653-
otherrequirer = FALSE;
1654-
have_no_preroll = FALSE;
1680+
have_no_preroll = g_hash_table_size (priv->no_preroll_elements) > 0;
16551681
/* iterate the elements, we collect which ones are async and no_preroll. We
16561682
* also remove the element when we find it. */
1657-
for (walk = bin->children; walk; walk = next) {
1658-
GstElement *child = GST_ELEMENT_CAST (walk->data);
1659-
1660-
next = g_list_next (walk);
16611683

1662-
if (child == element) {
1663-
found = TRUE;
1664-
/* remove the element */
1665-
if (bin->priv->children_hash != NULL) {
1666-
g_hash_table_remove (bin->priv->children_hash, elem_name);
1667-
}
1668-
bin->children = g_list_delete_link (bin->children, walk);
1669-
} else {
1670-
gboolean child_sink, child_source, child_provider, child_requirer;
1671-
1672-
GST_OBJECT_LOCK (child);
1673-
child_sink = GST_OBJECT_FLAG_IS_SET (child, GST_ELEMENT_FLAG_SINK);
1674-
child_source = GST_OBJECT_FLAG_IS_SET (child, GST_ELEMENT_FLAG_SOURCE);
1675-
child_provider =
1676-
GST_OBJECT_FLAG_IS_SET (child, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
1677-
child_requirer =
1678-
GST_OBJECT_FLAG_IS_SET (child, GST_ELEMENT_FLAG_REQUIRE_CLOCK);
1679-
/* when we remove a sink, check if there are other sinks. */
1680-
if (is_sink && !othersink && child_sink)
1681-
othersink = TRUE;
1682-
if (is_source && !othersource && child_source)
1683-
othersource = TRUE;
1684-
if (provides_clock && !otherprovider && child_provider)
1685-
otherprovider = TRUE;
1686-
if (requires_clock && !otherrequirer && child_requirer)
1687-
otherrequirer = TRUE;
1688-
/* check if we have NO_PREROLL children */
1689-
if (GST_STATE_RETURN (child) == GST_STATE_CHANGE_NO_PREROLL)
1690-
have_no_preroll = TRUE;
1691-
GST_OBJECT_UNLOCK (child);
1684+
GList *link = g_hash_table_lookup (priv->children_links, element);
1685+
if (link) {
1686+
if (priv->children_hash != NULL) {
1687+
g_hash_table_remove (priv->children_hash, elem_name);
16921688
}
1689+
found = TRUE;
1690+
/* remove the element */
1691+
bin->children = g_list_delete_link (bin->children, link);
16931692
}
16941693

16951694
/* the element must have been in the bin's list of children */
@@ -1701,28 +1700,37 @@ gst_bin_remove_func (GstBin * bin, GstElement * element)
17011700
bin->numchildren--;
17021701
bin->children_cookie++;
17031702
if (!GST_BIN_IS_NO_RESYNC (bin))
1704-
bin->priv->structure_cookie++;
1705-
1706-
if (is_sink && !othersink
1707-
&& !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_SINK)) {
1703+
priv->structure_cookie++;
1704+
1705+
if (is_sink)
1706+
priv->num_is_sink--;
1707+
if (is_source)
1708+
priv->num_is_src--;
1709+
if (provides_clock)
1710+
priv->num_provides_clock--;
1711+
if (requires_clock)
1712+
priv->num_requires_clock--;
1713+
1714+
if (priv->num_is_sink == 0
1715+
&& !(priv->suppressed_flags & GST_ELEMENT_FLAG_SINK)) {
17081716
/* we're not a sink anymore */
17091717
GST_DEBUG_OBJECT (bin, "we removed the last sink");
17101718
GST_OBJECT_FLAG_UNSET (bin, GST_ELEMENT_FLAG_SINK);
17111719
}
1712-
if (is_source && !othersource
1713-
&& !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_SOURCE)) {
1720+
if (priv->num_is_src == 0
1721+
&& !(priv->suppressed_flags & GST_ELEMENT_FLAG_SOURCE)) {
17141722
/* we're not a source anymore */
17151723
GST_DEBUG_OBJECT (bin, "we removed the last source");
17161724
GST_OBJECT_FLAG_UNSET (bin, GST_ELEMENT_FLAG_SOURCE);
17171725
}
1718-
if (provides_clock && !otherprovider
1719-
&& !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_PROVIDE_CLOCK)) {
1726+
if (priv->num_provides_clock == 0
1727+
&& !(priv->suppressed_flags & GST_ELEMENT_FLAG_PROVIDE_CLOCK)) {
17201728
/* we're not a clock provider anymore */
17211729
GST_DEBUG_OBJECT (bin, "we removed the last clock provider");
17221730
GST_OBJECT_FLAG_UNSET (bin, GST_ELEMENT_FLAG_PROVIDE_CLOCK);
17231731
}
1724-
if (requires_clock && !otherrequirer
1725-
&& !(bin->priv->suppressed_flags & GST_ELEMENT_FLAG_REQUIRE_CLOCK)) {
1732+
if (priv->num_requires_clock == 0
1733+
&& !(priv->suppressed_flags & GST_ELEMENT_FLAG_REQUIRE_CLOCK)) {
17261734
/* we're not a clock requirer anymore */
17271735
GST_DEBUG_OBJECT (bin, "we removed the last clock requirer");
17281736
GST_OBJECT_FLAG_UNSET (bin, GST_ELEMENT_FLAG_REQUIRE_CLOCK);
@@ -2539,9 +2547,12 @@ gst_bin_element_set_state (GstBin * bin, GstElement * element,
25392547
* anyway. */
25402548
if (G_UNLIKELY (ret == GST_STATE_CHANGE_NO_PREROLL)) {
25412549
GST_DEBUG_OBJECT (element, "element is NO_PREROLL, ignore async elements");
2550+
g_hash_table_add (bin->priv->no_preroll_elements, element);
25422551
goto no_preroll;
25432552
}
25442553

2554+
g_hash_table_remove (bin->priv->no_preroll_elements, element);
2555+
25452556
GST_CAT_INFO_OBJECT (GST_CAT_STATES, element,
25462557
"current %s pending %s, desired next %s",
25472558
gst_element_state_get_name (child_current),

subprojects/gstreamer/tests/check/gst/gstbin.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2190,6 +2190,43 @@ GST_START_TEST (test_locked_state_sets_base_time_on_children)
21902190

21912191
GST_END_TEST;
21922192

2193+
GST_START_TEST (test_add_remove_performance)
2194+
{
2195+
gint num_elements = 5000 * 3;
2196+
GstBin *bin = GST_BIN (gst_bin_new (NULL));
2197+
GstElement **el = g_new0 (GstElement *, num_elements);
2198+
gint i;
2199+
gint64 start;
2200+
double dur;
2201+
2202+
for (i = 0; i < num_elements; i += 3) {
2203+
el[i + 0] = gst_element_factory_make ("identity", NULL);
2204+
el[i + 1] = gst_element_factory_make ("fakesrc", NULL);
2205+
el[i + 2] = gst_element_factory_make ("fakesink", NULL);
2206+
}
2207+
2208+
start = g_get_monotonic_time ();
2209+
2210+
for (i = 0; i < num_elements; i++) {
2211+
gst_bin_add (bin, el[i]);
2212+
}
2213+
2214+
for (i = 0; i < num_elements / 2; i++) {
2215+
gst_bin_remove (bin, el[i]);
2216+
}
2217+
for (i = 0; i < num_elements / 2; i++) {
2218+
gst_bin_remove (bin, el[num_elements - i - 1]);
2219+
}
2220+
2221+
dur = (g_get_monotonic_time () - start) / (double) G_TIME_SPAN_SECOND;
2222+
2223+
GST_ERROR ("duration = %lf", dur);
2224+
2225+
gst_object_unref (bin);
2226+
}
2227+
2228+
GST_END_TEST;
2229+
21932230
static Suite *
21942231
gst_bin_suite (void)
21952232
{
@@ -2235,6 +2272,8 @@ gst_bin_suite (void)
22352272
if (0)
22362273
tcase_add_test (tc_chain, test_many_bins);
22372274

2275+
tcase_add_test (tc_chain, test_add_remove_performance);
2276+
22382277
return s;
22392278
}
22402279

0 commit comments

Comments
 (0)