tracy icon indicating copy to clipboard operation
tracy copied to clipboard

Feature request: Conditionally submit a Zone.

Open mcourteaux opened this issue 2 years ago • 7 comments

Looking at the code of tracy::ScopedZone, it doesn't seem possible to conditionally submit/commit a zone, as the constructor already commits something right away.

My situation: I'm having a spinlock where some threads are waiting for other threads. I'd like to catch outliers in case the spinlock takes unusually long. In that case I do want to pay the communication overhead of sending the Zone, otherwise, I'd like to just discard it.

Ideally, I could change the zone name from "spinlock" to "unusually_long_spinlock" or something, you see? From my 5.7 million spinlock Zones, about 4.9 million are very short (< 3 microseconds):

image

I'm timing the length of the spinlock myself using rdtsc for other statistics, so I could use that. To control it. However, a macro of some sorts that allows me to delete a zone would be cool.

mcourteaux avatar Feb 23 '23 15:02 mcourteaux

The stream must be temporally coherent, and what you are asking would violate that requirement. You may want to implement your own discardable variant of a zone that would postpone sending the begin event, but that has big caveats you need to understand.

wolfpld avatar Feb 23 '23 15:02 wolfpld

I am assuming "temporally coherent" means "monotonically increasing in time"? It would only violate this requirement if the Zone has child Zones, right? Otherwise, delaying submitting the beginning of a Zone, doesn't break the monotonicity of things. UNLESS the sampling data comes into the same stream and also has to be monotonically increasing relative to the other events (such as the Zones).

mcourteaux avatar Feb 23 '23 15:02 mcourteaux

Child zones are the main concern here. Sampling data is already disjoint because it is accumulated in kernel buffers and then processed in batches that cover past or future events.

wolfpld avatar Feb 23 '23 15:02 wolfpld

Okay, so it should work if I don't create child zones? I might give that a try soon. Will report back on the success of this. Thanks for the quick responses. :smile:

mcourteaux avatar Feb 23 '23 15:02 mcourteaux

Yes, it should be fairly simple to do. The API may change at some point in the future, but I don't have any immediate plans to do so.

wolfpld avatar Feb 23 '23 15:02 wolfpld

I quickly put this stuff together. Nothing fancy. If you have feedback, I could turn this into a PR, if you are interested:

#pragma once

#include <limits>
#include <stdint.h>
#include <string.h>

#include "../public/client/TracyProfiler.hpp"
#include "../public/common/TracyAlign.hpp"
#include "../public/common/TracyAlloc.hpp"
#include "../public/common/TracySystem.hpp"

#if TRACY_ENABLE

#define ConditionalZoneNamedN( varname, name, active, discard_if_faster_than_this ) static constexpr tracy::SourceLocationData TracyConcat(__tracy_source_location,TracyLine) { name, TracyFunction,  TracyFile, (uint32_t)TracyLine, 0 }; tracy::ScopedConditionalZone varname( &TracyConcat(__tracy_source_location,TracyLine), discard_if_faster_than_this, active )
#define ConditionalZoneScopedN( name, discard_if_faster_than_this ) ConditionalZoneNamedN( ___tracy_scoped_zone, name, true, discard_if_faster_than_this )


namespace tracy {

class ScopedConditionalZone {
 public:
  ScopedConditionalZone(const ScopedConditionalZone &) = delete;
  ScopedConditionalZone(ScopedConditionalZone &&) = delete;
  ScopedConditionalZone &operator=(const ScopedConditionalZone &) = delete;
  ScopedConditionalZone &operator=(ScopedConditionalZone &&) = delete;

  ScopedConditionalZone(const SourceLocationData *srcloc, int64_t discard_if_faster_than_this, bool is_active = true)
#ifdef TRACY_ON_DEMAND
      : m_active(is_active && GetProfiler().IsConnected()), m_discard_if_faster_than_this(discard_if_faster_than_this), m_srcloc((uint64_t)srcloc)
#else
      : m_active(is_active), m_discard_if_faster_than_this(discard_if_faster_than_this), m_srcloc((uint64_t)srcloc)
#endif
  {
    if (!m_active)
      return;
#ifdef TRACY_ON_DEMAND
    m_connectionId = GetProfiler().ConnectionId();
#endif

    m_zone_begin = Profiler::GetTime();
  }

  ~ScopedConditionalZone() {
    if (!m_active)
      return;
#ifdef TRACY_ON_DEMAND
    if (GetProfiler().ConnectionId() != m_connectionId)
      return;
#endif
    int64_t end_time = Profiler::GetTime();
    int64_t duration = end_time - m_zone_begin;
    if (duration < m_discard_if_faster_than_this) {
      return;
    }
    {
      TracyQueuePrepare(QueueType::ZoneBegin);
      MemWrite(&item->zoneBegin.time, m_zone_begin);
      MemWrite(&item->zoneBegin.srcloc, m_srcloc);
      TracyQueueCommit(zoneBeginThread);
    }

    {
      TracyQueuePrepare(QueueType::ZoneEnd);
      MemWrite(&item->zoneEnd.time, end_time);
      TracyQueueCommit(zoneEndThread);
    }
  }

  // Delete all the fancy features, to prevent storing too much info.
  tracy_force_inline void Text(const char *txt, size_t size) = delete;
  tracy_force_inline void Name(const char *txt, size_t size) = delete;
  tracy_force_inline void Value(uint64_t value) = delete;


  tracy_force_inline bool IsActive() const { return m_active; }

 private:
  const bool m_active;
  int64_t m_discard_if_faster_than_this;
  int64_t m_zone_begin;
  uint64_t m_srcloc;

#ifdef TRACY_ON_DEMAND
  uint64_t m_connectionId;
#endif
};
} // namespace tracy
#else



#endif

mcourteaux avatar Feb 23 '23 20:02 mcourteaux

I could turn this into a PR

I don't think this is something that should be provided by default.

wolfpld avatar Feb 23 '23 20:02 wolfpld