audioconvert: optimize mask calculation
authorPetr Kulhavy <brain@jikos.cz>
Fri, 14 Oct 2016 20:31:41 +0000 (22:31 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Tue, 1 Nov 2016 18:02:14 +0000 (20:02 +0200)
find_suitable_mask() had complexity O(n^2) on the number of bits.
For common case like 2-channel audio the mask was calculated in about 4k loop
cycles.

Optimize both n_bits_set() and find_suitable_mask() to O(n) where n is the
number of bits set in the mask.

https://bugzilla.gnome.org/show_bug.cgi?id=772864

gst/audioconvert/gstaudioconvert.c

index d320af4..cdc7778 100644 (file)
@@ -302,41 +302,41 @@ gst_audio_convert_transform_caps (GstBaseTransform * btrans,
   return result;
 }
 
+/* Count the number of bits set
+ * Optimized for the common case, assuming that the number of channels
+ * (i.e. bits set) is small
+ */
 static gint
 n_bits_set (guint64 x)
 {
-  gint i;
-  gint c = 0;
-  guint64 y = 1;
-
-  for (i = 0; i < 64; i++) {
-    if (x & y)
-      c++;
-    y <<= 1;
-  }
+  gint c;
+
+  for (c = 0; x; c++)
+    x &= x - 1;
 
   return c;
 }
 
+/* Reduce the mask to the n_chans lowest set bits
+ *
+ * The algorithm clears the n_chans lowest set bits and subtracts the
+ * result from the original mask to get the desired mask.
+ * It is optimized for the common case where n_chans is a small
+ * number. In the worst case, however, it stops after 64 iterations.
+ */
 static guint64
 find_suitable_mask (guint64 mask, gint n_chans)
 {
-  guint64 intersection;
-  gint i;
-
-  i = 0;
+  guint64 x = mask;
 
-  g_assert (n_bits_set (mask) >= n_chans);
+  for (; x && n_chans; n_chans--)
+    x &= x - 1;
 
-  intersection = mask;
-  do {
-    intersection = intersection & ((~G_GUINT64_CONSTANT (0)) >> i);
-    i++;
-  } while (n_bits_set (intersection) > n_chans && i < 64);
+  g_assert (x || n_chans == 0);
+  /* assertion fails if mask contained less bits than n_chans
+   * or n_chans was < 0 */
 
-  if (i < 64)
-    return intersection;
-  return 0;
+  return mask - x;
 }
 
 static void