mirror of
https://github.com/gnustep/libs-performance.git
synced 2025-02-19 18:11:16 +00:00
Rewrite thread handling to avoid use of locks
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/performance/trunk@21906 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
9b80b6621c
commit
7a89dea8ea
2 changed files with 406 additions and 307 deletions
|
@ -31,34 +31,38 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The GSThroughput class is used maintain statistics about the number
|
* The GSThroughput class is used maintain statistics about the number
|
||||||
* of events or the duration of operations in your software.
|
* of events or the duration of operations in your software.<br />
|
||||||
|
* For performance reasons, the class avoids locking and you must ensure
|
||||||
|
* that an instance of the class is only ever used by a single thread
|
||||||
|
* (the one in which it was created).
|
||||||
*/
|
*/
|
||||||
@interface GSThroughput : NSObject
|
@interface GSThroughput : NSObject
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return all the current throughput measuring objects ...
|
* Return all the current throughput measuring objects in the current thread...
|
||||||
* useful if you want to do to all instances in your process.
|
|
||||||
*/
|
*/
|
||||||
+ (NSArray*) allInstances;
|
+ (NSArray*) allInstances;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a report on all GSThroughput instances ... calls
|
* Return a report on all GSThroughput instances in the current thread...
|
||||||
* the [GSThroughput-description] * method of the individual
|
* calls the [GSThroughput-description] method of the individual instances
|
||||||
* instances to get a report on each one.
|
* to get a report on each one.
|
||||||
*/
|
*/
|
||||||
+ (NSString*) description;
|
+ (NSString*) description;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instructs the minitoring system to use a timer at the specified interval
|
* Instructs the monitoring system to use a timer at the specified interval
|
||||||
* for keeping its idea of the current time up to date.
|
* for keeping its idea of the current time up to date. This timer is used
|
||||||
|
* by all instances associated with the current thread.
|
||||||
*/
|
*/
|
||||||
+ (void) setTick: (NSTimeInterval)interval;
|
+ (void) setTick: (NSTimeInterval)interval;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the monitoring system's notion of the current time.<br />
|
* Updates the monitoring system's notion of the current time for all
|
||||||
* This should be called at the start of each (or more often) if
|
* instances associated with the current thread.<br />
|
||||||
|
* This should be called at the start of each second (or more often) if
|
||||||
* you want accurate monitoring by the second.
|
* you want accurate monitoring by the second.
|
||||||
*/
|
*/
|
||||||
+ (void) tick;
|
+ (void) tick;
|
||||||
|
@ -84,22 +88,22 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ends duration recording for the current event started by a matching
|
* Ends duration recording for the current event started by a matching
|
||||||
* call to -startDuration.<br />
|
* call to the -startDuration: method.<br />
|
||||||
* You may use this method only if the receiver was initialised with
|
* You may use this method only if the receiver was initialised with
|
||||||
* duration logging turned on.
|
* duration logging turned on.
|
||||||
*/
|
*/
|
||||||
- (void) endDuration;
|
- (void) endDuration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialises the receiver for duration logging for fifteen minute
|
* Initialises the receiver for duration logging (in the current thread only)
|
||||||
* periods over the last twentyfour hours.
|
* for fifteen minute periods over the last twentyfour hours.
|
||||||
*/
|
*/
|
||||||
- (id) init;
|
- (id) init;
|
||||||
|
|
||||||
/** <init />
|
/** <init />
|
||||||
* Initialises the receiver to maintain stats over a particular time range,
|
* Initialises the receiver to maintain stats (for the current thread only)
|
||||||
* specifying whether duration statistics are to be maintained, or just
|
* over a particular time range, specifying whether duration statistics are
|
||||||
* event/transation counts.
|
* to be maintained, or just event/transaction counts.
|
||||||
*/
|
*/
|
||||||
- (id) initWithDurations: (BOOL)aFlag
|
- (id) initWithDurations: (BOOL)aFlag
|
||||||
forPeriods: (unsigned)numberOfPeriods
|
forPeriods: (unsigned)numberOfPeriods
|
||||||
|
@ -117,16 +121,16 @@
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts recording the duration of an event. This must be followed by
|
* Starts recording the duration of an event. This must be followed by
|
||||||
* a matching call to -endDuration.<br />
|
* a matching call to the -endDuration method.<br />
|
||||||
|
* The name argument is used to identify the location of the call for
|
||||||
|
* debugging/logging purposes, and you must ensure that the string
|
||||||
|
* continues to exist up to the point where -endDuration is called,
|
||||||
|
* as the receiver will not retain it.<br />
|
||||||
* You may use this method only if the receiver was initialised with
|
* You may use this method only if the receiver was initialised with
|
||||||
* duration logging turned on.
|
* duration logging turned on.
|
||||||
*/
|
*/
|
||||||
- (void) startDuration;
|
- (void) startDuration: (NSString*)name;
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal method called by +tick in order to update stats for this instance.
|
|
||||||
*/
|
|
||||||
- (void) update;
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
663
GSThroughput.m
663
GSThroughput.m
|
@ -33,37 +33,21 @@
|
||||||
#include <Foundation/NSException.h>
|
#include <Foundation/NSException.h>
|
||||||
#include <Foundation/NSNotification.h>
|
#include <Foundation/NSNotification.h>
|
||||||
#include <Foundation/NSHashTable.h>
|
#include <Foundation/NSHashTable.h>
|
||||||
#include <Foundation/NSMapTable.h>
|
|
||||||
#include <Foundation/NSLock.h>
|
|
||||||
#include <Foundation/NSAutoreleasePool.h>
|
#include <Foundation/NSAutoreleasePool.h>
|
||||||
#include <Foundation/NSValue.h>
|
|
||||||
#include <Foundation/NSDebug.h>
|
#include <Foundation/NSDebug.h>
|
||||||
#include <Foundation/NSSet.h>
|
|
||||||
#include <Foundation/NSTimer.h>
|
#include <Foundation/NSTimer.h>
|
||||||
|
#include <Foundation/NSThread.h>
|
||||||
|
|
||||||
#include "GSThroughput.h"
|
#include "GSThroughput.h"
|
||||||
|
|
||||||
#define MAXDURATION 24.0*60.0*60.0
|
#define MAXDURATION 24.0*60.0*60.0
|
||||||
|
|
||||||
|
@class GSThroughputThread;
|
||||||
|
|
||||||
static Class NSDateClass = 0;
|
static Class NSDateClass = 0;
|
||||||
static SEL tiSel = 0;
|
static SEL tiSel = 0;
|
||||||
static NSTimeInterval (*tiImp)(Class,SEL) = 0;
|
static NSTimeInterval (*tiImp)(Class,SEL) = 0;
|
||||||
|
|
||||||
static NSTimer *theTimer = nil;
|
|
||||||
static NSTimeInterval baseTime = 0;
|
|
||||||
static NSTimeInterval lastTime = 0;
|
|
||||||
|
|
||||||
inline unsigned GSThroughputTimeTick()
|
|
||||||
{
|
|
||||||
return (lastTime - baseTime) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@implementation GSThroughput
|
|
||||||
|
|
||||||
static NSHashTable *GSThroughputInstances = 0;
|
|
||||||
static NSLock *GSThroughputLock = nil;
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned cnt; // Number of events.
|
unsigned cnt; // Number of events.
|
||||||
unsigned tick; // Start time
|
unsigned tick; // Start time
|
||||||
|
@ -89,7 +73,9 @@ typedef struct {
|
||||||
unsigned period;
|
unsigned period;
|
||||||
unsigned last; // last tick used
|
unsigned last; // last tick used
|
||||||
NSTimeInterval started; // When duration logging started.
|
NSTimeInterval started; // When duration logging started.
|
||||||
NSString *name;
|
NSString *event; // Name of current event
|
||||||
|
NSString *name; // Name of this instance
|
||||||
|
GSThroughputThread *thread; // Thread info
|
||||||
} Item;
|
} Item;
|
||||||
#define my ((Item*)&self[1])
|
#define my ((Item*)&self[1])
|
||||||
|
|
||||||
|
@ -100,110 +86,315 @@ typedef struct {
|
||||||
#define dminutes ((DInfo*)my->minutes)
|
#define dminutes ((DInfo*)my->minutes)
|
||||||
#define dperiods ((DInfo*)my->periods)
|
#define dperiods ((DInfo*)my->periods)
|
||||||
|
|
||||||
+ (NSArray*) allInstances
|
|
||||||
{
|
|
||||||
NSArray *a;
|
|
||||||
|
|
||||||
[GSThroughputLock lock];
|
@interface GSThroughputThread : NSObject
|
||||||
a = NSAllHashTableObjects(GSThroughputInstances);
|
{
|
||||||
[GSThroughputLock unlock];
|
@public
|
||||||
return a;
|
NSTimer *theTimer;
|
||||||
|
NSTimeInterval baseTime;
|
||||||
|
NSTimeInterval lastTime;
|
||||||
|
NSHashTable *instances;
|
||||||
}
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
+ (id) alloc
|
@interface GSThroughput (Private)
|
||||||
|
+ (GSThroughputThread*) _threadInfo;
|
||||||
|
+ (void) _tick: (NSTimer*)aTimer;
|
||||||
|
+ (void) _tickForThread: (GSThroughputThread*)t;
|
||||||
|
- (void) _detach;
|
||||||
|
- (void) _update;
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@implementation GSThroughputThread
|
||||||
|
- (void) dealloc
|
||||||
{
|
{
|
||||||
return [self allocWithZone: NSDefaultMallocZone()];
|
if (instances != 0)
|
||||||
}
|
|
||||||
|
|
||||||
+ (id) allocWithZone: (NSZone*)z
|
|
||||||
{
|
|
||||||
GSThroughput *c;
|
|
||||||
|
|
||||||
c = (GSThroughput*)NSAllocateObject(self, sizeof(Item), z);
|
|
||||||
[GSThroughputLock lock];
|
|
||||||
NSHashInsert(GSThroughputInstances, (void*)c);
|
|
||||||
[GSThroughputLock unlock];
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ (NSString*) description
|
|
||||||
{
|
|
||||||
NSMutableString *ms;
|
|
||||||
NSHashEnumerator e;
|
|
||||||
GSThroughput *c;
|
|
||||||
|
|
||||||
ms = [NSMutableString stringWithString: [super description]];
|
|
||||||
[GSThroughputLock lock];
|
|
||||||
e = NSEnumerateHashTable(GSThroughputInstances);
|
|
||||||
while ((c = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil)
|
|
||||||
{
|
{
|
||||||
[ms appendFormat: @"\n%@", [c description]];
|
NSHashEnumerator e;
|
||||||
|
GSThroughput *i;
|
||||||
|
|
||||||
|
e = NSEnumerateHashTable(instances);
|
||||||
|
while ((i = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil)
|
||||||
|
{
|
||||||
|
[i _detach];
|
||||||
|
}
|
||||||
|
NSEndHashTableEnumeration(&e);
|
||||||
|
NSFreeHashTable(instances);
|
||||||
|
instances = 0;
|
||||||
}
|
}
|
||||||
NSEndHashTableEnumeration(&e);
|
[super dealloc];
|
||||||
[GSThroughputLock unlock];
|
|
||||||
return ms;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void) initialize
|
- (id) init
|
||||||
{
|
{
|
||||||
if (GSThroughputInstances == 0)
|
baseTime = lastTime = (*tiImp)(NSDateClass, tiSel);
|
||||||
{
|
instances = NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0);
|
||||||
NSDateClass = [NSDate class];
|
return self;
|
||||||
tiSel = @selector(timeIntervalSinceReferenceDate);
|
|
||||||
tiImp
|
|
||||||
= (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel];
|
|
||||||
baseTime = lastTime = (*tiImp)(NSDateClass, tiSel);
|
|
||||||
GSThroughputLock = [NSLock new];
|
|
||||||
GSThroughputInstances
|
|
||||||
= NSCreateHashTable(NSNonRetainedObjectHashCallBacks, 0);
|
|
||||||
[self setTick: 1.0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void) setTick: (NSTimeInterval)interval
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@implementation GSThroughput (Private)
|
||||||
|
|
||||||
|
+ (GSThroughputThread*) _threadInfo
|
||||||
{
|
{
|
||||||
[GSThroughputLock lock];
|
GSThroughputThread *t;
|
||||||
if (theTimer != nil)
|
|
||||||
|
t = [[[NSThread currentThread] threadDictionary]
|
||||||
|
objectForKey: @"GSThroughput"];
|
||||||
|
if (t == nil)
|
||||||
{
|
{
|
||||||
[theTimer invalidate];
|
t = [GSThroughputThread new];
|
||||||
theTimer = nil;
|
[[[NSThread currentThread] threadDictionary] setObject: t
|
||||||
|
forKey: @"GSThroughput"];
|
||||||
|
RELEASE(t);
|
||||||
}
|
}
|
||||||
if (interval > 0.0)
|
return t;
|
||||||
{
|
|
||||||
theTimer = [NSTimer scheduledTimerWithTimeInterval: interval
|
|
||||||
target: self
|
|
||||||
selector: @selector(tick)
|
|
||||||
userInfo: 0
|
|
||||||
repeats: YES];
|
|
||||||
}
|
|
||||||
[GSThroughputLock unlock];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
+ (void) tick
|
+ (void) _tick: (NSTimer*)aTimer
|
||||||
|
{
|
||||||
|
[self _tickForThread: [aTimer userInfo]];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void) _tickForThread: (GSThroughputThread*)t
|
||||||
{
|
{
|
||||||
NSTimeInterval now;
|
NSTimeInterval now;
|
||||||
NSHashEnumerator e;
|
NSHashEnumerator e;
|
||||||
GSThroughput *i;
|
GSThroughput *i;
|
||||||
|
|
||||||
[GSThroughputLock lock];
|
|
||||||
/*
|
/*
|
||||||
* If the clock has been reset so that time has gone backwards,
|
* If the clock has been reset so that time has gone backwards,
|
||||||
* we adjust the baseTime so that lastTime >= baseTime is true.
|
* we adjust the baseTime so that lastTime >= baseTime is true.
|
||||||
*/
|
*/
|
||||||
now = (*tiImp)(NSDateClass, tiSel);
|
now = (*tiImp)(NSDateClass, tiSel);
|
||||||
if (now < lastTime)
|
if (now < t->lastTime)
|
||||||
{
|
{
|
||||||
baseTime -= (lastTime - now);
|
t->baseTime -= (t->lastTime - now);
|
||||||
}
|
}
|
||||||
lastTime = now;
|
t->lastTime = now;
|
||||||
e = NSEnumerateHashTable(GSThroughputInstances);
|
e = NSEnumerateHashTable(t->instances);
|
||||||
while ((i = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil)
|
while ((i = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil)
|
||||||
{
|
{
|
||||||
[i update];
|
[i _update];
|
||||||
}
|
}
|
||||||
NSEndHashTableEnumeration(&e);
|
NSEndHashTableEnumeration(&e);
|
||||||
|
}
|
||||||
|
|
||||||
[GSThroughputLock unlock];
|
- (void) _detach
|
||||||
|
{
|
||||||
|
my->thread = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void) _update
|
||||||
|
{
|
||||||
|
if (my->thread != nil)
|
||||||
|
{
|
||||||
|
unsigned tick = (my->thread->lastTime - my->thread->baseTime) + 1;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
if (my->supportDurations == YES)
|
||||||
|
{
|
||||||
|
while (my->last < tick)
|
||||||
|
{
|
||||||
|
DInfo *info;
|
||||||
|
|
||||||
|
if (my->second++ == 59)
|
||||||
|
{
|
||||||
|
info = &dminutes[my->minute];
|
||||||
|
for (i = 0; i < 60; i++)
|
||||||
|
{
|
||||||
|
DInfo *from = &dseconds[i];
|
||||||
|
|
||||||
|
info->cnt += from->cnt;
|
||||||
|
if (from->min < info->min)
|
||||||
|
{
|
||||||
|
info->min = from->min;
|
||||||
|
}
|
||||||
|
if (from->max > info->max)
|
||||||
|
{
|
||||||
|
info->max = from->max;
|
||||||
|
}
|
||||||
|
info->sum += from->sum;
|
||||||
|
}
|
||||||
|
if (my->minute++ == my->minutesPerPeriod)
|
||||||
|
{
|
||||||
|
info = &dperiods[my->period];
|
||||||
|
for (i = 0; i < my->minutesPerPeriod; i++)
|
||||||
|
{
|
||||||
|
DInfo *from = &dminutes[i];
|
||||||
|
|
||||||
|
info->cnt += from->cnt;
|
||||||
|
if (from->min > 0.0 && from->min < info->min)
|
||||||
|
{
|
||||||
|
info->min = from->min;
|
||||||
|
}
|
||||||
|
if (from->max > info->max)
|
||||||
|
{
|
||||||
|
info->max = from->max;
|
||||||
|
}
|
||||||
|
info->sum += from->sum;
|
||||||
|
}
|
||||||
|
if (my->period++ == my->numberOfPeriods)
|
||||||
|
{
|
||||||
|
my->period = 0;
|
||||||
|
}
|
||||||
|
info = &dperiods[my->period];
|
||||||
|
info->cnt = 0;
|
||||||
|
info->max = 0.0;
|
||||||
|
info->min = MAXDURATION;
|
||||||
|
info->sum = 0.0;
|
||||||
|
info->tick = tick;
|
||||||
|
my->minute = 0;
|
||||||
|
}
|
||||||
|
info = &dminutes[my->minute];
|
||||||
|
info->cnt = 0;
|
||||||
|
info->max = 0.0;
|
||||||
|
info->min = MAXDURATION;
|
||||||
|
info->sum = 0.0;
|
||||||
|
info->tick = tick;
|
||||||
|
my->second = 0;
|
||||||
|
}
|
||||||
|
info = &dseconds[my->second];
|
||||||
|
info->cnt = 0;
|
||||||
|
info->max = 0.0;
|
||||||
|
info->min = MAXDURATION;
|
||||||
|
info->sum = 0.0;
|
||||||
|
info->tick = tick;
|
||||||
|
|
||||||
|
my->last++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (my->last < tick)
|
||||||
|
{
|
||||||
|
CInfo *info;
|
||||||
|
|
||||||
|
if (my->second++ == 59)
|
||||||
|
{
|
||||||
|
info = &cminutes[my->minute];
|
||||||
|
for (i = 0; i < 60; i++)
|
||||||
|
{
|
||||||
|
info->cnt += cseconds[i].cnt;
|
||||||
|
}
|
||||||
|
if (my->minute++ == my->minutesPerPeriod)
|
||||||
|
{
|
||||||
|
info = &cperiods[my->period];
|
||||||
|
for (i = 0; i < my->minutesPerPeriod; i++)
|
||||||
|
{
|
||||||
|
info->cnt += cminutes[i].cnt;
|
||||||
|
}
|
||||||
|
if (my->period++ == my->numberOfPeriods)
|
||||||
|
{
|
||||||
|
my->period = 0;
|
||||||
|
}
|
||||||
|
info = &cperiods[my->period];
|
||||||
|
info->cnt = 0;
|
||||||
|
info->tick = tick;
|
||||||
|
my->minute = 0;
|
||||||
|
}
|
||||||
|
info = &cminutes[my->minute];
|
||||||
|
info->cnt = 0;
|
||||||
|
info->tick = tick;
|
||||||
|
my->second = 0;
|
||||||
|
}
|
||||||
|
info = &cseconds[my->second];
|
||||||
|
info->cnt = 0;
|
||||||
|
info->tick = tick;
|
||||||
|
|
||||||
|
my->last++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@implementation GSThroughput
|
||||||
|
|
||||||
|
+ (NSArray*) allInstances
|
||||||
|
{
|
||||||
|
GSThroughputThread *t;
|
||||||
|
NSArray *a;
|
||||||
|
|
||||||
|
t = [[[NSThread currentThread] threadDictionary]
|
||||||
|
objectForKey: @"GSThroughput"];
|
||||||
|
if (t == nil)
|
||||||
|
{
|
||||||
|
a = nil;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
a = NSAllHashTableObjects(t->instances);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString*) description
|
||||||
|
{
|
||||||
|
GSThroughputThread *t;
|
||||||
|
NSMutableString *ms;
|
||||||
|
|
||||||
|
ms = [NSMutableString stringWithString: [super description]];
|
||||||
|
t = [[[NSThread currentThread] threadDictionary]
|
||||||
|
objectForKey: @"GSThroughput"];
|
||||||
|
if (t != nil)
|
||||||
|
{
|
||||||
|
NSHashEnumerator e;
|
||||||
|
GSThroughput *c;
|
||||||
|
|
||||||
|
e = NSEnumerateHashTable(t->instances);
|
||||||
|
while ((c = (GSThroughput*)NSNextHashEnumeratorItem(&e)) != nil)
|
||||||
|
{
|
||||||
|
[ms appendFormat: @"\n%@", [c description]];
|
||||||
|
}
|
||||||
|
NSEndHashTableEnumeration(&e);
|
||||||
|
}
|
||||||
|
return ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
+ (void) initialize
|
||||||
|
{
|
||||||
|
if (NSDateClass == 0)
|
||||||
|
{
|
||||||
|
NSDateClass = [NSDate class];
|
||||||
|
tiSel = @selector(timeIntervalSinceReferenceDate);
|
||||||
|
tiImp
|
||||||
|
= (NSTimeInterval (*)(Class,SEL))[NSDateClass methodForSelector: tiSel];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void) setTick: (NSTimeInterval)interval
|
||||||
|
{
|
||||||
|
GSThroughputThread *t = [self _threadInfo];
|
||||||
|
|
||||||
|
if (t->theTimer != nil)
|
||||||
|
{
|
||||||
|
[t->theTimer invalidate];
|
||||||
|
t->theTimer = nil;
|
||||||
|
}
|
||||||
|
if (interval > 0.0)
|
||||||
|
{
|
||||||
|
t->theTimer = [NSTimer scheduledTimerWithTimeInterval: interval
|
||||||
|
target: self
|
||||||
|
selector: @selector(_tick:)
|
||||||
|
userInfo: t
|
||||||
|
repeats: YES];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void) tick
|
||||||
|
{
|
||||||
|
[self _tickForThread: [self _threadInfo]];
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) add: (unsigned)count
|
- (void) add: (unsigned)count
|
||||||
|
@ -240,15 +431,17 @@ typedef struct {
|
||||||
|
|
||||||
- (void) dealloc
|
- (void) dealloc
|
||||||
{
|
{
|
||||||
[GSThroughputLock lock];
|
|
||||||
if (my->seconds != 0)
|
if (my->seconds != 0)
|
||||||
{
|
{
|
||||||
NSZoneFree(NSDefaultMallocZone(), my->seconds);
|
NSZoneFree(NSDefaultMallocZone(), my->seconds);
|
||||||
}
|
}
|
||||||
RELEASE(my->name);
|
RELEASE(my->name);
|
||||||
NSHashRemove(GSThroughputInstances, (void*)self);
|
if (my->thread != nil)
|
||||||
|
{
|
||||||
|
NSHashRemove(my->thread->instances, (void*)self);
|
||||||
|
my->thread = nil;
|
||||||
|
}
|
||||||
NSDeallocateObject(self);
|
NSDeallocateObject(self);
|
||||||
[GSThroughputLock unlock];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSString*) description
|
- (NSString*) description
|
||||||
|
@ -264,88 +457,93 @@ typedef struct {
|
||||||
}
|
}
|
||||||
m = [n mutableCopy];
|
m = [n mutableCopy];
|
||||||
|
|
||||||
if (my->supportDurations == YES)
|
if (my->thread != nil)
|
||||||
{
|
{
|
||||||
if (my->second > 0)
|
NSTimeInterval baseTime = my->thread->baseTime;
|
||||||
{
|
|
||||||
[m appendString: @"\nCurrent minute:\n"];
|
|
||||||
for (i = 0; i < my->second; i++)
|
|
||||||
{
|
|
||||||
DInfo *info = &dseconds[i];
|
|
||||||
NSTimeInterval ti = info->tick + baseTime;
|
|
||||||
|
|
||||||
[m appendFormat: @"%u, %g, %g, %g, %@\n",
|
if (my->supportDurations == YES)
|
||||||
info->cnt, info->max, info->min, info->sum,
|
{
|
||||||
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
|
if (my->second > 0)
|
||||||
|
{
|
||||||
|
[m appendString: @"\nCurrent minute:\n"];
|
||||||
|
for (i = 0; i < my->second; i++)
|
||||||
|
{
|
||||||
|
DInfo *info = &dseconds[i];
|
||||||
|
NSTimeInterval ti = info->tick + baseTime;
|
||||||
|
|
||||||
|
[m appendFormat: @"%u, %g, %g, %g, %@\n",
|
||||||
|
info->cnt, info->max, info->min, info->sum,
|
||||||
|
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (my->minute > 0)
|
||||||
|
{
|
||||||
|
[m appendString: @"\nCurrent period:\n"];
|
||||||
|
for (i = 0; i < my->minute; i++)
|
||||||
|
{
|
||||||
|
DInfo *info = &dminutes[i];
|
||||||
|
NSTimeInterval ti = info->tick + baseTime;
|
||||||
|
|
||||||
|
[m appendFormat: @"%u, %g, %g, %g, %@\n",
|
||||||
|
info->cnt, info->max, info->min, info->sum,
|
||||||
|
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (my->period > 0)
|
||||||
|
{
|
||||||
|
[m appendString: @"\nPrevious periods:\n"];
|
||||||
|
for (i = 0; i < my->period; i++)
|
||||||
|
{
|
||||||
|
DInfo *info = &dperiods[i];
|
||||||
|
NSTimeInterval ti = info->tick + baseTime;
|
||||||
|
|
||||||
|
[m appendFormat: @"%u, %g, %g, %g, %@\n",
|
||||||
|
info->cnt, info->max, info->min, info->sum,
|
||||||
|
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
if (my->minute > 0)
|
|
||||||
{
|
{
|
||||||
[m appendString: @"\nCurrent period:\n"];
|
if (my->second > 0)
|
||||||
for (i = 0; i < my->minute; i++)
|
|
||||||
{
|
{
|
||||||
DInfo *info = &dminutes[i];
|
[m appendString: @"\nCurrent minute:\n"];
|
||||||
NSTimeInterval ti = info->tick + baseTime;
|
for (i = 0; i < my->second; i++)
|
||||||
|
{
|
||||||
|
CInfo *info = &cseconds[i];
|
||||||
|
NSTimeInterval ti = info->tick + baseTime;
|
||||||
|
|
||||||
[m appendFormat: @"%u, %g, %g, %g, %@\n",
|
[m appendFormat: @"%u, %@\n", info->cnt,
|
||||||
info->cnt, info->max, info->min, info->sum,
|
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
|
||||||
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (my->period > 0)
|
if (my->minute > 0)
|
||||||
{
|
|
||||||
[m appendString: @"\nPrevious periods:\n"];
|
|
||||||
for (i = 0; i < my->period; i++)
|
|
||||||
{
|
{
|
||||||
DInfo *info = &dperiods[i];
|
[m appendString: @"\nCurrent period:\n"];
|
||||||
NSTimeInterval ti = info->tick + baseTime;
|
for (i = 0; i < my->minute; i++)
|
||||||
|
{
|
||||||
|
CInfo *info = &cminutes[i];
|
||||||
|
NSTimeInterval ti = info->tick + baseTime;
|
||||||
|
|
||||||
[m appendFormat: @"%u, %g, %g, %g, %@\n",
|
[m appendFormat: @"%u, %@\n", info->cnt,
|
||||||
info->cnt, info->max, info->min, info->sum,
|
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
|
||||||
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
if (my->period > 0)
|
||||||
else
|
|
||||||
{
|
|
||||||
if (my->second > 0)
|
|
||||||
{
|
|
||||||
[m appendString: @"\nCurrent minute:\n"];
|
|
||||||
for (i = 0; i < my->second; i++)
|
|
||||||
{
|
{
|
||||||
CInfo *info = &cseconds[i];
|
[m appendString: @"\nPrevious periods:\n"];
|
||||||
NSTimeInterval ti = info->tick + baseTime;
|
for (i = 0; i < my->period; i++)
|
||||||
|
{
|
||||||
|
CInfo *info = &cperiods[i];
|
||||||
|
NSTimeInterval ti = info->tick + baseTime;
|
||||||
|
|
||||||
[m appendFormat: @"%u, %@\n", info->cnt,
|
[m appendFormat: @"%u, %@\n", info->cnt,
|
||||||
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
|
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (my->minute > 0)
|
|
||||||
{
|
|
||||||
[m appendString: @"\nCurrent period:\n"];
|
|
||||||
for (i = 0; i < my->minute; i++)
|
|
||||||
{
|
|
||||||
CInfo *info = &cminutes[i];
|
|
||||||
NSTimeInterval ti = info->tick + baseTime;
|
|
||||||
|
|
||||||
[m appendFormat: @"%u, %@\n", info->cnt,
|
|
||||||
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (my->period > 0)
|
|
||||||
{
|
|
||||||
[m appendString: @"\nPrevious periods:\n"];
|
|
||||||
for (i = 0; i < my->period; i++)
|
|
||||||
{
|
|
||||||
CInfo *info = &cperiods[i];
|
|
||||||
NSTimeInterval ti = info->tick + baseTime;
|
|
||||||
|
|
||||||
[m appendFormat: @"%u, %@\n", info->cnt,
|
|
||||||
[NSDate dateWithTimeIntervalSinceReferenceDate: ti]];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -358,6 +556,7 @@ typedef struct {
|
||||||
{
|
{
|
||||||
NSAssert(my->started > 0.0, NSInternalInconsistencyException);
|
NSAssert(my->started > 0.0, NSInternalInconsistencyException);
|
||||||
[self addDuration: (*tiImp)(NSDateClass, tiSel) - my->started];
|
[self addDuration: (*tiImp)(NSDateClass, tiSel) - my->started];
|
||||||
|
my->event = nil;
|
||||||
my->started = 0.0;
|
my->started = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,11 +579,19 @@ typedef struct {
|
||||||
DESTROY(self);
|
DESTROY(self);
|
||||||
return nil;
|
return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add this instance to the current thread.
|
||||||
|
*/
|
||||||
|
my->thread = [[self class] _threadInfo];
|
||||||
|
NSHashInsert(my->thread->instances, (void*)self);
|
||||||
|
|
||||||
my->supportDurations = aFlag;
|
my->supportDurations = aFlag;
|
||||||
my->numberOfPeriods = numberOfPeriods;
|
my->numberOfPeriods = numberOfPeriods;
|
||||||
my->minutesPerPeriod = minutesPerPeriod;
|
my->minutesPerPeriod = minutesPerPeriod;
|
||||||
my->last = GSThroughputTimeTick() - 1;
|
my->last = (my->thread->lastTime - my->thread->baseTime) + 1;
|
||||||
c = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate: lastTime];
|
c = [[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:
|
||||||
|
my->thread->lastTime];
|
||||||
my->second = [c secondOfMinute];
|
my->second = [c secondOfMinute];
|
||||||
i = [c hourOfDay] * 60 + [c minuteOfHour];
|
i = [c hourOfDay] * 60 + [c minuteOfHour];
|
||||||
my->minute = i % minutesPerPeriod;
|
my->minute = i % minutesPerPeriod;
|
||||||
|
@ -444,131 +651,19 @@ typedef struct {
|
||||||
ASSIGN(my->name, name);
|
ASSIGN(my->name, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) startDuration
|
- (void) startDuration: (NSString*)name
|
||||||
{
|
{
|
||||||
NSAssert(my->supportDurations == YES && my->started == 0.0,
|
NSAssert(my->supportDurations == YES && my->started == 0.0,
|
||||||
NSInternalInconsistencyException);
|
NSInternalInconsistencyException);
|
||||||
|
if (my->event != nil)
|
||||||
|
{
|
||||||
|
[NSException raise: NSInternalInconsistencyException
|
||||||
|
format: @"-startDuration: for '%@' nested inside '%@'",
|
||||||
|
my->event, name];
|
||||||
|
}
|
||||||
my->started = (*tiImp)(NSDateClass, tiSel);
|
my->started = (*tiImp)(NSDateClass, tiSel);
|
||||||
|
my->event = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void) update
|
|
||||||
{
|
|
||||||
unsigned tick = GSThroughputTimeTick();
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
if (my->supportDurations == YES)
|
|
||||||
{
|
|
||||||
while (my->last < tick)
|
|
||||||
{
|
|
||||||
DInfo *info;
|
|
||||||
|
|
||||||
if (my->second++ == 59)
|
|
||||||
{
|
|
||||||
info = &dminutes[my->minute];
|
|
||||||
for (i = 0; i < 60; i++)
|
|
||||||
{
|
|
||||||
DInfo *from = &dseconds[i];
|
|
||||||
|
|
||||||
info->cnt += from->cnt;
|
|
||||||
if (from->min < info->min)
|
|
||||||
{
|
|
||||||
info->min = from->min;
|
|
||||||
}
|
|
||||||
if (from->max > info->max)
|
|
||||||
{
|
|
||||||
info->max = from->max;
|
|
||||||
}
|
|
||||||
info->sum += from->sum;
|
|
||||||
}
|
|
||||||
if (my->minute++ == my->minutesPerPeriod)
|
|
||||||
{
|
|
||||||
info = &dperiods[my->period];
|
|
||||||
for (i = 0; i < my->minutesPerPeriod; i++)
|
|
||||||
{
|
|
||||||
DInfo *from = &dminutes[i];
|
|
||||||
|
|
||||||
info->cnt += from->cnt;
|
|
||||||
if (from->min > 0.0 && from->min < info->min)
|
|
||||||
{
|
|
||||||
info->min = from->min;
|
|
||||||
}
|
|
||||||
if (from->max > info->max)
|
|
||||||
{
|
|
||||||
info->max = from->max;
|
|
||||||
}
|
|
||||||
info->sum += from->sum;
|
|
||||||
}
|
|
||||||
if (my->period++ == my->numberOfPeriods)
|
|
||||||
{
|
|
||||||
my->period = 0;
|
|
||||||
}
|
|
||||||
info = &dperiods[my->period];
|
|
||||||
info->cnt = 0;
|
|
||||||
info->max = 0.0;
|
|
||||||
info->min = MAXDURATION;
|
|
||||||
info->sum = 0.0;
|
|
||||||
info->tick = tick;
|
|
||||||
my->minute = 0;
|
|
||||||
}
|
|
||||||
info = &dminutes[my->minute];
|
|
||||||
info->cnt = 0;
|
|
||||||
info->max = 0.0;
|
|
||||||
info->min = MAXDURATION;
|
|
||||||
info->sum = 0.0;
|
|
||||||
info->tick = tick;
|
|
||||||
my->second = 0;
|
|
||||||
}
|
|
||||||
info = &dseconds[my->second];
|
|
||||||
info->cnt = 0;
|
|
||||||
info->max = 0.0;
|
|
||||||
info->min = MAXDURATION;
|
|
||||||
info->sum = 0.0;
|
|
||||||
info->tick = tick;
|
|
||||||
|
|
||||||
my->last++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (my->last < tick)
|
|
||||||
{
|
|
||||||
CInfo *info;
|
|
||||||
|
|
||||||
if (my->second++ == 59)
|
|
||||||
{
|
|
||||||
info = &cminutes[my->minute];
|
|
||||||
for (i = 0; i < 60; i++)
|
|
||||||
{
|
|
||||||
info->cnt += cseconds[i].cnt;
|
|
||||||
}
|
|
||||||
if (my->minute++ == my->minutesPerPeriod)
|
|
||||||
{
|
|
||||||
info = &cperiods[my->period];
|
|
||||||
for (i = 0; i < my->minutesPerPeriod; i++)
|
|
||||||
{
|
|
||||||
info->cnt += cminutes[i].cnt;
|
|
||||||
}
|
|
||||||
if (my->period++ == my->numberOfPeriods)
|
|
||||||
{
|
|
||||||
my->period = 0;
|
|
||||||
}
|
|
||||||
info = &cperiods[my->period];
|
|
||||||
info->cnt = 0;
|
|
||||||
info->tick = tick;
|
|
||||||
my->minute = 0;
|
|
||||||
}
|
|
||||||
info = &cminutes[my->minute];
|
|
||||||
info->cnt = 0;
|
|
||||||
info->tick = tick;
|
|
||||||
my->second = 0;
|
|
||||||
}
|
|
||||||
info = &cseconds[my->second];
|
|
||||||
info->cnt = 0;
|
|
||||||
info->tick = tick;
|
|
||||||
|
|
||||||
my->last++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue