Max - the AlsaInputPlugin now consumes 120% CPU on my 2-core processor, roughly 
60% on each processor. I think the alsa poll event is firing too often.

Since my last update, I've been thinking that the alsa poll is really just a 
timer, it fires every time a fixed number of frames have been written to its 
internal buffer, and as a real-time stream this equates to a fixed number of 
milli-seconds. So the MPD code would be much simpler if we could just use a 
TimeoutMonitor to produce the wake-up instead of trying to integrate with the 
alsa lib here. 


The attached patch implements this idea. I get CPU load ~0.7%, which is the 
same as playing line-in via arecord | aplay, so I think there is little wasted 
overhead here. There is also an advantage (I think) that the alsa handle is now 
only referenced from one thread, I was unsure about using it in both threads. 
There is still a bit of guesswork, which is the exact length of timeout to use. 
I have selected twice the time it takes to fill the read buffer which works 
well on my laptop - I don't have the experience to know if this is a good 
general solution.

Do you think this implementation is OK in principle, or should I look again at 
the polling solution?

Thanks
Steven
From a69a801b5adb2c82dee8addadaf36962f92b0688 Mon Sep 17 00:00:00 2001
From: Steven O'Brien <steven_obri...@yahoo.co.uk>
Date: Tue, 7 Jan 2014 12:46:23 +0000
Subject: [PATCH] input/AlsaInputPlugin: use TimeoutMonitor in place of
 SocketMonitor

---
 src/input/AlsaInputPlugin.cxx | 121 ++++++------------------------------------
 1 file changed, 17 insertions(+), 104 deletions(-)

diff --git a/src/input/AlsaInputPlugin.cxx b/src/input/AlsaInputPlugin.cxx
index 5aa0a69..999c74b 100644
--- a/src/input/AlsaInputPlugin.cxx
+++ b/src/input/AlsaInputPlugin.cxx
@@ -31,12 +31,10 @@
 #include "util/Domain.hxx"
 #include "util/Error.hxx"
 #include "util/StringUtil.hxx"
-#include "util/ReusableArray.hxx"
 #include "util/Cast.hxx"
 #include "Log.hxx"
-#include "event/MultiSocketMonitor.hxx"
+#include "event/TimeoutMonitor.hxx"
 #include "event/DeferredMonitor.hxx"
-#include "event/Call.hxx"
 #include "thread/Mutex.hxx"
 #include "thread/Cond.hxx"
 #include "IOThread.hxx"
@@ -64,11 +62,12 @@ static constexpr unsigned int default_rate = 44100; // cd quality
  */
 static constexpr size_t read_buffer_size = 4096;
 
-class AlsaInputStream final : MultiSocketMonitor, DeferredMonitor {
+class AlsaInputStream final : TimeoutMonitor, DeferredMonitor {
 	InputStream base;
 	snd_pcm_t *capture_handle;
 	size_t frame_size;
 	int frames_to_read;
+	int timeout_ms;
 	bool eof;
 
 	/**
@@ -77,13 +76,11 @@ class AlsaInputStream final : MultiSocketMonitor, DeferredMonitor {
 	 */
 	std::atomic_bool waiting;
 
-	ReusableArray<pollfd> pfd_buffer;
-
 public:
 	AlsaInputStream(EventLoop &loop,
 			const char *uri, Mutex &mutex, Cond &cond,
-			snd_pcm_t *_handle, int _frame_size)
-		:MultiSocketMonitor(loop),
+			snd_pcm_t *_handle, int _frame_size, int rate)
+		:TimeoutMonitor(loop),
 		 DeferredMonitor(loop),
 		 base(input_plugin_alsa, uri, mutex, cond),
 		 capture_handle(_handle),
@@ -101,7 +98,7 @@ public:
 		base.size = -1;
 		base.ready = true;
 		frames_to_read = read_buffer_size / frame_size;
-
+		timeout_ms = frames_to_read * 1000 * 2 / rate;
 		snd_pcm_start(capture_handle);
 
 		DeferredMonitor::Schedule();
@@ -133,9 +130,7 @@ public:
 		if (snd_pcm_avail(capture_handle) > frames_to_read)
 			return true;
 
-		if (!waiting.exchange(true))
-			SafeInvalidateSockets();
-
+		waiting.store(true, std::memory_order_relaxed);
 		return false;
 	}
 
@@ -152,16 +147,18 @@ private:
 
 	int Recover(int err);
 
-	void SafeInvalidateSockets() {
-		DeferredMonitor::Schedule();
+	virtual void OnTimeout() override {
+		if (waiting.exchange(false, std::memory_order_relaxed)) {
+			const ScopeLock protect(base.mutex);
+			/* wake up the thread that is waiting for more data */
+			base.cond.broadcast();
+		}
+		TimeoutMonitor::Schedule(timeout_ms);
 	}
 
 	virtual void RunDeferred() override {
-		InvalidateSockets();
+		TimeoutMonitor::Schedule(timeout_ms);
 	}
-
-	virtual int PrepareSockets() override;
-	virtual void DispatchSockets() override;
 };
 
 inline InputStream *
@@ -190,8 +187,8 @@ AlsaInputStream::Create(const char *uri, Mutex &mutex, Cond &cond,
 
 	int frame_size = snd_pcm_format_width(format) / 8 * channels;
 	AlsaInputStream *stream = new AlsaInputStream(io_thread_get(),
-						      uri, mutex, cond,
-						      handle, frame_size);
+						      uri, mutex, cond, handle,
+	                                              frame_size, rate);
 	return &stream->base;
 }
 
@@ -216,40 +213,6 @@ AlsaInputStream::Read(void *ptr, size_t size, Error &error)
 	return nbytes;
 }
 
-int
-AlsaInputStream::PrepareSockets()
-{
-	if (!waiting) {
-		ClearSocketList();
-		return -1;
-	}
-
-	int count = snd_pcm_poll_descriptors_count(capture_handle);
-	if (count < 0) {
-		ClearSocketList();
-		return -1;
-	}
-
-	struct pollfd *pfds = pfd_buffer.Get(count);
-
-	count = snd_pcm_poll_descriptors(capture_handle, pfds, count);
-	if (count < 0)
-		count = 0;
-
-	ReplaceSocketList(pfds, count);
-	return -1;
-}
-
-void
-AlsaInputStream::DispatchSockets()
-{
-	waiting = false;
-
-	const ScopeLock protect(base.mutex);
-	/* wake up the thread that is waiting for more data */
-	base.cond.broadcast();
-}
-
 inline int
 AlsaInputStream::Recover(int err)
 {
@@ -323,23 +286,6 @@ AlsaInputStream::OpenDevice(const char *device,
 		return nullptr;
 	}
 
-	/* period needs to be big enough so that poll() doesn't fire too often,
-	 * but small enough that buffer overruns don't occur if Read() is not
-	 * invoked often enough.
-	 * the calculation here is empirical; however all measurements were
-	 * done using 44100:16:2. When we extend this plugin to support
-	 * other audio formats then this may need to be revisited */
-	snd_pcm_uframes_t period = read_buffer_size * 2;
-	int direction = -1;
-	if ((err = snd_pcm_hw_params_set_period_size_near(capture_handle, hw_params,
-							  &period, &direction)) < 0) {
-		error.Format(alsa_input_domain, "Cannot set period size (%s)",
-			     snd_strerror(err));
-		snd_pcm_hw_params_free(hw_params);
-		snd_pcm_close(capture_handle);
-		return nullptr;
-	}
-
 	if ((err = snd_pcm_hw_params(capture_handle, hw_params)) < 0) {
 		error.Format(alsa_input_domain, "Cannot set parameters (%s)",
 			     snd_strerror(err));
@@ -350,39 +296,6 @@ AlsaInputStream::OpenDevice(const char *device,
 
 	snd_pcm_hw_params_free (hw_params);
 
-	snd_pcm_sw_params_t *sw_params;
-
-	snd_pcm_sw_params_malloc(&sw_params);
-	snd_pcm_sw_params_current(capture_handle, sw_params);
-
-	if ((err = snd_pcm_sw_params_set_start_threshold(capture_handle, sw_params,
-							 period)) < 0)  {
-		error.Format(alsa_input_domain,
-			     "unable to set start threshold (%s)", snd_strerror(err));
-		snd_pcm_sw_params_free(sw_params);
-		snd_pcm_close(capture_handle);
-		return nullptr;
-	}
-
-	if ((err = snd_pcm_sw_params_set_period_event(capture_handle, sw_params,
-						      1)) < 0) {
-		error.Format(alsa_input_domain,
-			     "unable to set period event (%s)", snd_strerror(err));
-		snd_pcm_sw_params_free(sw_params);
-		snd_pcm_close(capture_handle);
-		return nullptr;
-	}
-
-	if ((err = snd_pcm_sw_params(capture_handle, sw_params)) < 0) {
-		error.Format(alsa_input_domain,
-			     "unable to install sw params (%s)", snd_strerror(err));
-		snd_pcm_sw_params_free(sw_params);
-		snd_pcm_close(capture_handle);
-		return nullptr;
-	}
-
-	snd_pcm_sw_params_free(sw_params);
-
 	snd_pcm_prepare(capture_handle);
 
 	return capture_handle;
-- 
1.8.3.2

------------------------------------------------------------------------------
Rapidly troubleshoot problems before they affect your business. Most IT 
organizations don't have a clear picture of how application performance 
affects their revenue. With AppDynamics, you get 100% visibility into your 
Java,.NET, & PHP application. Start your 15-day FREE TRIAL of AppDynamics Pro!
http://pubads.g.doubleclick.net/gampad/clk?id=84349831&iu=/4140/ostg.clktrk
_______________________________________________
Musicpd-dev-team mailing list
Musicpd-dev-team@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/musicpd-dev-team

Reply via email to