Add support for multi-threaded playback
Imre Deak
imre.deak at intel.com
Fri Oct 26 11:53:01 PDT 2012
Hi,
On Fri, 2012-10-26 at 18:49 +0100, José Fonseca wrote:
> Imre,
>
> Thanks for this. I took your mt-trace patches and did some follow on changes:
>
> - port it to macosx and windows (implement and use C++11-like
> threading primitives)
>
> - make threads synchronous i.e., respect the ordering of the calls in
> the trace, otherwise there would be random results and race
> conditions, as the locking is not captured in the trace.
I thought the workqueue approach already guaranteed this:
if (prev_thread_id != call->thread_id) {
if (thread_wq)
thread_wq->flush();
thread_wq = get_work_queue(call->thread_id);
prev_thread_id = call->thread_id;
}
thread_wq->queue_work(render_work);
So if the new call's thread ID is different than the previous one we
wait until all calls in the previous thread finish. Could you explain
what extra synchronization we need here?
> - make it faster -- the parsing is done in the thread that is
> executing, so there is less thread switching.
I'd have to think more how this improves things. Afaics on multi-core at
least there shouldn't be much task switching, except for the above
synchronization points.
> The code is in https://github.com/apitrace/apitrace/tree/mt-trace . A
> very good sample is android emulator,
> http://people.freedesktop.org/~jrfonseca/traces/android-emulator.trace
> .
>
> Please give it a try, and let me know it if it still works for you.
> In particular let me know if __thread works alright in Android or if
> it causes problems.
Ok, I can give it a try next week.
--Imre
>
> I hope to merge it into master after a bit more testing and code clean up.
>
> Jose
>
>
> On Thu, Sep 20, 2012 at 10:33 AM, Imre Deak <imre.deak at intel.com> wrote:
> > Signed-off-by: Imre Deak <imre.deak at intel.com>
> > ---
> > retrace/retrace_main.cpp | 166 +++++++++++++++++++++++++++++++++++-----------
> > 1 file changed, 126 insertions(+), 40 deletions(-)
> >
> > diff --git a/retrace/retrace_main.cpp b/retrace/retrace_main.cpp
> > index 9386a61..df69d4a 100644
> > --- a/retrace/retrace_main.cpp
> > +++ b/retrace/retrace_main.cpp
> > @@ -27,8 +27,10 @@
> > #include <string.h>
> > #include <iostream>
> >
> > +#include "glws.hpp"
> > #include "os_binary.hpp"
> > #include "os_time.hpp"
> > +#include "os_workqueue.hpp"
> > #include "image.hpp"
> > #include "trace_callset.hpp"
> > #include "trace_dump.hpp"
> > @@ -36,6 +38,7 @@
> >
> >
> > static bool waitOnFinish = false;
> > +static bool use_threads;
> >
> > static const char *comparePrefix = NULL;
> > static const char *snapshotPrefix = NULL;
> > @@ -44,6 +47,8 @@ static trace::CallSet compareFrequency;
> >
> > static unsigned dumpStateCallNo = ~0;
> >
> > +retrace::Retracer retracer;
> > +
> >
> > namespace retrace {
> >
> > @@ -51,6 +56,7 @@ namespace retrace {
> > trace::Parser parser;
> > trace::Profiler profiler;
> >
> > +static std::map<unsigned long, os::WorkQueue *> thread_wq_map;
> >
> > int verbosity = 0;
> > bool debug = true;
> > @@ -66,7 +72,22 @@ bool profilingPixelsDrawn = false;
> >
> > unsigned frameNo = 0;
> > unsigned callNo = 0;
> > +static bool state_dumped;
> >
> > +class RenderWork : public os::WorkQueueWork
> > +{
> > + trace::Call *call;
> > +public:
> > + void run(void);
> > + RenderWork(trace::Call *_call) { call = _call; }
> > + ~RenderWork(void) { delete call; }
> > +};
> > +
> > +class FlushGLWork : public os::WorkQueueWork
> > +{
> > +public:
> > + void run(void) { flushRendering(); }
> > +};
> >
> > void
> > frameComplete(trace::Call &call) {
> > @@ -119,57 +140,119 @@ takeSnapshot(unsigned call_no) {
> > return;
> > }
> >
> > +void RenderWork::run(void)
> > +{
> > + bool swapRenderTarget = call->flags &
> > + trace::CALL_FLAG_SWAP_RENDERTARGET;
> > + bool doSnapshot = snapshotFrequency.contains(*call) ||
> > + compareFrequency.contains(*call);
> >
> > -static void
> > -mainLoop() {
> > - retrace::Retracer retracer;
> > + if (state_dumped)
> > + return;
> >
> > - addCallbacks(retracer);
> > + // For calls which cause rendertargets to be swaped, we take the
> > + // snapshot _before_ swapping the rendertargets.
> > + if (doSnapshot && swapRenderTarget) {
> > + if (call->flags & trace::CALL_FLAG_END_FRAME) {
> > + // For swapbuffers/presents we still use this
> > + // call number, spite not have been executed yet.
> > + takeSnapshot(call->no);
> > + } else {
> > + // Whereas for ordinate fbo/rendertarget changes we
> > + // use the previous call's number.
> > + takeSnapshot(call->no - 1);
> > + }
> > + }
> >
> > - long long startTime = 0;
> > - frameNo = 0;
> > + callNo = call->no;
> > + retracer.retrace(*call);
> >
> > - startTime = os::getTime();
> > - trace::Call *call;
> > + if (doSnapshot && !swapRenderTarget)
> > + takeSnapshot(call->no);
> >
> > - while ((call = retrace::parser.parse_call())) {
> > - bool swapRenderTarget = call->flags & trace::CALL_FLAG_SWAP_RENDERTARGET;
> > - bool doSnapshot =
> > - snapshotFrequency.contains(*call) ||
> > - compareFrequency.contains(*call)
> > - ;
> > -
> > - // For calls which cause rendertargets to be swaped, we take the
> > - // snapshot _before_ swapping the rendertargets.
> > - if (doSnapshot && swapRenderTarget) {
> > - if (call->flags & trace::CALL_FLAG_END_FRAME) {
> > - // For swapbuffers/presents we still use this call number,
> > - // spite not have been executed yet.
> > - takeSnapshot(call->no);
> > - } else {
> > - // Whereas for ordinate fbo/rendertarget changes we use the
> > - // previous call's number.
> > - takeSnapshot(call->no - 1);
> > - }
> > - }
> > + if (call->no >= dumpStateCallNo && dumpState(std::cout))
> > + state_dumped = true;
> > +}
> >
> > - callNo = call->no;
> > - retracer.retrace(*call);
> > +static os::WorkQueue *get_work_queue(unsigned long thread_id)
> > +{
> > + os::WorkQueue *thread;
> > + std::map<unsigned long, os::WorkQueue *>::iterator it;
> >
> > - if (doSnapshot && !swapRenderTarget) {
> > - takeSnapshot(call->no);
> > - }
> > + it = thread_wq_map.find(thread_id);
> > + if (it == thread_wq_map.end()) {
> > + thread = new os::WorkQueue();
> > + thread_wq_map[thread_id] = thread;
> > + } else {
> > + thread = it->second;
> > + }
> > +
> > + return thread;
> > +}
> >
> > - if (call->no >= dumpStateCallNo &&
> > - dumpState(std::cout)) {
> > - exit(0);
> > +static void exit_work_queues(void)
> > +{
> > + std::map<unsigned long, os::WorkQueue *>::iterator it;
> > +
> > + it = thread_wq_map.begin();
> > + while (it != thread_wq_map.end()) {
> > + os::WorkQueue *thread_wq = it->second;
> > +
> > + thread_wq->queue_work(new FlushGLWork);
> > + thread_wq->flush();
> > + thread_wq->destroy();
> > + thread_wq_map.erase(it++);
> > + }
> > +}
> > +
> > +static void do_all_calls(void)
> > +{
> > + trace::Call *call;
> > + int prev_thread_id = -1;
> > + os::WorkQueue *thread_wq = NULL;
> > +
> > + while ((call = parser.parse_call())) {
> > + RenderWork *render_work = new RenderWork(call);
> > +
> > + if (use_threads) {
> > + if (prev_thread_id != call->thread_id) {
> > + if (thread_wq)
> > + thread_wq->flush();
> > + thread_wq = get_work_queue(call->thread_id);
> > + prev_thread_id = call->thread_id;
> > + }
> > +
> > + thread_wq->queue_work(render_work);
> > + } else {
> > + render_work->run();
> > + delete render_work;
> > }
> >
> > - delete call;
> > + if (state_dumped)
> > + break;
> > }
> >
> > - // Reached the end of trace
> > - flushRendering();
> > + exit_work_queues();
> > +}
> > +
> > +
> > +static void
> > +mainLoop() {
> > + addCallbacks(retracer);
> > +
> > + long long startTime = 0;
> > + frameNo = 0;
> > +
> > + startTime = os::getTime();
> > +
> > + do_all_calls();
> > +
> > + if (!use_threads)
> > + /*
> > + * Reached the end of trace; if using threads we do the flush
> > + * when exiting the threads.
> > + */
> > + flushRendering();
> >
> > long long endTime = os::getTime();
> > float timeInterval = (endTime - startTime) * (1.0 / os::timeFrequency);
> > @@ -211,7 +294,8 @@ usage(const char *argv0) {
> > " -S CALLSET calls to snapshot (default is every frame)\n"
> > " -v increase output verbosity\n"
> > " -D CALLNO dump state at specific call no\n"
> > - " -w waitOnFinish on final frame\n";
> > + " -w waitOnFinish on final frame\n"
> > + " -t enable threading\n";
> > }
> >
> >
> > @@ -289,6 +373,8 @@ int main(int argc, char **argv)
> > } else if (!strcmp(arg, "-ppd")) {
> > retrace::profilingPixelsDrawn = true;
> > }
> > + } else if (!strcmp(arg, "-t")) {
> > + use_threads = true;
> > } else {
> > std::cerr << "error: unknown option " << arg << "\n";
> > usage(argv[0]);
> > --
> > 1.7.9.5
> >
More information about the apitrace
mailing list