monotone-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Monotone-devel] oprofile data for mtn 0.37.


From: Eric Anderson
Subject: Re: [Monotone-devel] oprofile data for mtn 0.37.
Date: Wed, 12 Mar 2008 17:26:17 -0700

Jack Lloyd writes:
 > Woo, good times. Yes, this is used for locking, specifically to perform
 > the mapping that occurs for code like
 > 
 > Named_Mutex_Holder lock("allocator");
 > 
 > Which gets and locks the mutex returned from
 >   global_state().get_named_mutex("allocator")
 > 
 > 1) Change this to use an enum instead of a string for indexing. Which
 > leaves the call count the same but should reduce the lookup costs
 > significantly.
 > 
 > 2) Drop the concept entirely, use plain mutex objects where needed.
 > 
 > 3) Drop global shared state wholesale, and require applications to
 > maintain any per-thread or per-process state (the RNG, allocators,
 > etc). [I've been leaning towards this of late for various other
 > reasons anyway]
 > 
 > 4) The clever and elegant solution that you just thought of while
 > reading this.

So while (3) may be a good long term solution, there is a trick you
can use with macros that will let you write strings for the name, yet
keep the cost as just an integer lookup; we used it for debugging;
basically the trick is that you allocate a static object which holds
the string to <whatever> mapping, in our case, string to integer, in
your case string to global mutex, so you only pay the translation cost
once.
        -Eric


// Using the below (and some obvious bits that were excluded) you can
// write something like:

SimLogDebug("mtn::netsync", boost::format("sending message %s") % msg);

// you get to write your debugging as if it was strings, but the cost of 
// determining if you would print the message is just a double memory
// dereference (pointer + array)
// 
// In your case, you'd map to a shared mutex pointer and the costs are 
// even lower.

#define SimLogDebug(what, msg) \
  do { \
    static SimLog::AWhat awhat(std::string(what)); \
    if (SimLog::wouldDebug(awhat)) { \
      SimLog::ForceDebug(msg); \
    } \
  } while(0)

class SimLog {
public:
    // There has to be a better name; you want to make these static in a file,
    // the whole point is efficiency; otherwise the calls to wouldDebug
    // just take too long.
    class AWhat {
    public:
        AWhat(const std::string &what);
        AWhat(const AWhat &what) : id(what.id) { }
        
        unsigned id;
    };

    struct instance_data {
        std::vector<unsigned> debug_whats;
        HashMap<std::string, unsigned> debug_levels;
        HashMap<std::string, unsigned> string2id;
        unsigned max_id;
        instance_data() : max_id(0) { debug_whats.reserve(1000); }
    };

    static bool wouldDebug(const AWhat &what, unsigned level = 1) {
        return instance->debug_whats[what.id] >= level;
    }
};

unsigned 
SimLog::initId(const string &what)
{
    if (instance == NULL) {
        instance = new SimLog::instance_data();
    }
    if (instance->string2id.exists(what)) {
        return instance->string2id[what];
    }
    ++instance->max_id;
    INVARIANT(instance->max_id < instance->debug_whats.capacity(),
              "thread safety would be sacrificed by letting the vector resize");
    instance->debug_whats.resize(instance->max_id+1);
    INVARIANT(instance->string2id[what] == 0, "bad");
    instance->string2id[what] = instance->max_id;
    INVARIANT(instance->debug_whats.size() > instance->max_id, "bad");
    return instance->max_id;
}


SimLog::AWhat::AWhat(const std::string &what)
    : id(initId(what))
{
}





reply via email to

[Prev in Thread] Current Thread [Next in Thread]