diff -r c1f20ce4abcf -r 3e88ff8f41d5 kerneltest/e32test/misc/t_loadsim.cpp --- a/kerneltest/e32test/misc/t_loadsim.cpp Tue Aug 31 16:34:26 2010 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2351 +0,0 @@ -// Copyright (c) 2009-2009 Nokia Corporation and/or its subsidiary(-ies). -// All rights reserved. -// This component and the accompanying materials are made available -// under the terms of the License "Eclipse Public License v1.0" -// which accompanies this distribution, and is available -// at the URL "http://www.eclipse.org/legal/epl-v10.html". -// -// Initial Contributors: -// Nokia Corporation - initial contribution. -// -// Contributors: -// -// Description: -// e32test\misc\t_loadsim.cpp -// -// - - -//------------------------------------------------------------------------------------------- -//! @SYMTestCaseID KBASE-t_loadsim-2705 -//! @SYMTestCaseDesc verifying the behaviour of the load balancer -//! @SYMPREQ 417-52765/417-58889 -//! @SYMTestPriority Critical -//! @SYMTestActions -//! 1. This test runs a variety of loads on an SMP system. Loads types are: -//! 1.1 Cpu intensive loads -//! 1.2 memory intensive loads (high locality) -//! 1.3 memory intensive loads (low locality) -//! 1.4 memory intensive loads with atomic operations -//! 1.5 cpu intensive loads with some serialization -//! 2. For each test, the load is first run on a single cpu locked thread as a baseline -//! benchmark. Then the tests are run in the following configurations: -//! 2.1 For n = 1 to 2*Number of cpus do a run with i threads. -//! 2.2 For h = 1 to NumCpus ; For n = h to 2*NumCpus; run with h high priorty threads and -//! n standard priority threads, with high priority threads cpu locked. -//! 2.3 For h = 1 to NumCpus ; For n = h to 2*NumCpus; run with h high priorty threads and -//! n standard priority threads. -//! @SYMTestExpectedResults -//! test passed. TTest is manual: -//! 1. For each test we expect to see that the amount of CPU time obtained by each CPU is -//! balanced. That is, all standard priority threads get roughly same amount of CPU time -//! and all high priority threads get roughly same amount of CPU time and a higher value -//! than lower priority threads. -//! 2. We also expect the relative efficiency reported by the test between the benchmark -//! and each test run to be >=95% on average. Values well below this are acceptable in -//! test runs involving atomic operations (1.4) - -//------------------------------------------------------------------------------------------- - -#define __E32TEST_EXTENSION__ -#include -#include -#include -#include -#include -#include - -//#define TRACE(x) x -#define TRACE(x) - -void Panic(TInt aLine) - { - User::Panic(_L("T_LOADSIM"),aLine); - } - -#define assert(x) ((void)((x)||(Panic(__LINE__),0))) - -RTest test(_L("T_LOADSIM")); - -const TInt KErrCouldNotStart = -99; - -volatile TInt STFU = 1; - -/****************************************************************************** - * Random Number Generation - ******************************************************************************/ -void LFSR(TUint64& a) - { - TInt i; - for (i=64; i>0; --i) - { - TUint64 x = a<<1; - TUint64 y = x<<1; - x^=y; - a = (y>>1) | (x>>63); - } - } - -// Returns 256*log2(a/2^64) -TInt Log2(TUint64 a) - { - const TUint64 KBit63 = UI64LIT(0x8000000000000000); - TInt n = __e32_find_ms1_64(a); - a <<= (63-n); - n -= 64; - TInt i; - for (i=0; i<8; ++i) - { - a >>= 32; - a *= a; - n <<= 1; - if (a & KBit63) - { - ++n; - } - else - { - a <<= 1; - } - } - return n; - } - -TUint32 ExpRV(TUint64 aU, TUint32 aMean, TUint32 aTick) - { - TInt n = -Log2(aU); - TUint64 x = TUint64(n) * TUint64(aMean); - x *= TUint64(22713); // 2^15 * ln2 - TUint64 p(aTick); - p <<= 22; - x += p; - p += p; - x /= p; - return I64LOW(x); - } - - - -/****************************************************************************** - * Generic High-Resolution Timing - ******************************************************************************/ -class TTimestamp - { -public: - typedef void (*TSampleFunc)(TAny*); -public: - void Sample(); - void Sample(TSampleFunc aFunc, TAny* aPtr); - TInt64 operator-(const TTimestamp&) const; - static void Init(); -private: - TUint32 iF; // User::FastCounter() value - TUint32 iN; // User::NTickCount() value -private: - static TUint32 FF; // User::FastCounter() frequency - static TUint32 NP; // User::NTickCount() period - static TBool FU; // User::FastCounter() counts up - static TUint32 FWrapM; // Number of nanokernel ticks for FastCounter() to wrap / 2 * 2^FWrapS - static TInt FWrapS; // Shift so that 2^31<=FWrapM<2^32 - }; - -TUint32 TTimestamp::FF; -TUint32 TTimestamp::NP; -TBool TTimestamp::FU; -TUint32 TTimestamp::FWrapM; -TInt TTimestamp::FWrapS; - - -void TTimestamp::Sample() - { - TUint32 n = User::NTickCount(); - do { - iN = n; - iF = User::FastCounter(); - n = User::NTickCount(); - } while (n!=iN); - } - -void TTimestamp::Sample(TSampleFunc aFunc, TAny* aPtr) - { - TUint32 n = User::NTickCount(); - do { - iN = n; - (*aFunc)(aPtr); - iF = User::FastCounter(); - n = User::NTickCount(); - } while (n!=iN); - } - -// return (x*a)/b -TUint64 scale(TUint64 x, TUint32 a, TUint32 b) - { - TUint64 mask = KMaxTUint32; - TUint64 x0 = x & mask; - TUint64 x1 = x >> 32; - x0 *= TUint64(a); - x1 *= TUint64(a); - x1 += (x0 >> 32); - x0 &= mask; - TUint64 q1 = x1 / TUint64(b); - TUint64 q0 = x1 - q1*TUint64(b); - q0 <<= 32; - q0 |= x0; - q0 /= TUint64(b); - return (q1<<32)|q0; - } - - -// Return difference between a and this in microseconds -TInt64 TTimestamp::operator-(const TTimestamp& a) const - { - TInt sign = 1; - TTimestamp start; - TTimestamp end; - if (iN-a.iN >= 0x80000000u) - { - sign = -1; - start = *this; - end = a; - } - else - { - start = a; - end = *this; - } - TUint32 fd32 = end.iF - start.iF; - if (!FU) - fd32 = ~fd32 + 1u; - TUint64 nd = TUint64(end.iN) - TUint64(start.iN); - nd <<= 31; // 2^31 * difference in NTickCount - TUint64 x = TUint64(fd32) * TUint64(FWrapM); - x >>= FWrapS; // ftick difference * (FWrapM/2^FWrapS) = 2^31 * ntick difference - nd -= x; // Should now be a multiple of 2^31N where N=2^32*ftickp/ntickp - // i.e. should be a multiple of 2^63*ftickp/ntickp - - // FWrapM = 2^(31+FWrapS)*ftickp/ntickp - // FWrapM << (32-FWrapS) = 2^63*ftickp/ntickp - TUint64 m = TUint64(FWrapM) << (32-FWrapS); - - nd += (m>>1); - nd /= m; - - nd = (nd<<32) + TUint64(fd32); // final result in fast counter ticks - TInt64 r = scale(nd, 1000000, FF); // convert to microseconds - if (sign<0) - r = -r; - return r; - } - -void TTimestamp::Init() - { - TInt r; - r = HAL::Get(HAL::ENanoTickPeriod, (TInt&)NP); - assert(r==KErrNone); - r = HAL::Get(HAL::EFastCounterFrequency, (TInt&)FF); - assert(r==KErrNone); - r = HAL::Get(HAL::EFastCounterCountsUp, (TInt&)FU); - assert(r==KErrNone); - TReal fpn = TReal(FF) * TReal(NP) / 1000000.0; // fast counter ticks per NTick - TReal fwrap = 2147483648.0 / fpn; // NTicks between fast counter wraparounds / 2 - TInt exp = 0; - while (fwrap < 2147483648.0) - { - fwrap *= 2.0; - ++exp; - } - fwrap += 0.5; - if (fwrap >= 4294967296.0) - { - fwrap *= 0.5; - --exp; - } - FWrapM = (TUint32)fwrap; - FWrapS = exp; // NTicks for 2^31 fast ticks = FWrapM/2^FWrapS - - test.Printf(_L("FastCounter frequency %uHz\n"), FF); - if (FU) - test.Printf(_L("FastCounter counts UP\n")); - else - test.Printf(_L("FastCounter counts DOWN\n")); - test.Printf(_L("Nanokernel tick period %uus\n"), NP); - test.Printf(_L("FWrapM %08x\n"), FWrapM); - test.Printf(_L("FWrapS %d\n"), FWrapS); - } - -/****************************************************************************** - * CPU Usage Measurement - ******************************************************************************/ -class TThreadCpuUsageSample - { -public: - void Sample(RThread aThread); - TInt64 ElapsedTimeDelta(const TThreadCpuUsageSample& aStart) const; - TInt64 CpuTimeDelta(const TThreadCpuUsageSample& aStart) const; -private: - static void SampleThreadCpuTime(TAny* aPtr); -private: - TTimestamp iElapsedTime; - TInt64 iCpuTime; - RThread iThread; - }; - -void TThreadCpuUsageSample::Sample(RThread aThread) - { - iThread = aThread; - iElapsedTime.Sample(&SampleThreadCpuTime, this); - } - -void TThreadCpuUsageSample::SampleThreadCpuTime(TAny* aPtr) - { - TThreadCpuUsageSample& me = *(TThreadCpuUsageSample*)aPtr; - TTimeIntervalMicroSeconds& rt = *(TTimeIntervalMicroSeconds*)&me.iCpuTime; - assert(me.iThread.GetCpuTime(rt) == KErrNone); - } - -TInt64 TThreadCpuUsageSample::ElapsedTimeDelta(const TThreadCpuUsageSample& aStart) const - { - return iElapsedTime - aStart.iElapsedTime; - } - -TInt64 TThreadCpuUsageSample::CpuTimeDelta(const TThreadCpuUsageSample& aStart) const - { - return iCpuTime - aStart.iCpuTime; - } - -class TCpuUsage - { -public: - enum {EMaxCpus=8}; -public: - void Sample(); - TInt64 ElapsedTimeDelta(const TCpuUsage& aStart) const; - TInt64 CpuTimeDelta(const TCpuUsage& aStart, TInt aCpu) const; - static TInt N() { return NumberOfCpus; } -public: - static void Init(); -private: - static void SampleIdleTimes(TAny* aPtr); -private: - TTimestamp iElapsedTime; - TInt64 iIdleTime[EMaxCpus]; -private: - static TInt NumberOfCpus; - static RThread IdleThread[EMaxCpus]; - }; - -TInt TCpuUsage::NumberOfCpus = -1; -RThread TCpuUsage::IdleThread[TCpuUsage::EMaxCpus]; - -void TCpuUsage::Init() - { - TTimestamp::Init(); - - NumberOfCpus = UserSvr::HalFunction(EHalGroupKernel, EKernelHalNumLogicalCpus, 0, 0); - test.Printf(_L("NumberOfCpus = %d\n"), NumberOfCpus); - assert(NumberOfCpus > 0); - assert(NumberOfCpus <= EMaxCpus); - - TTimeIntervalMicroSeconds ms; - TInt r; - r = RThread().GetCpuTime(ms); - if (r != KErrNone) - { - test.Printf(_L("RThread::GetCpuTime() returned %d\n"), r); - test.Printf(_L("This test requires a working RThread::GetCpuTime() to run\n")); - test(0); - } - - TFullName kname; - _LIT(KLitKernelName, "ekern.exe*"); - _LIT(KLitNull, "::Null"); - TFindProcess fp(KLitKernelName); - test_KErrNone(fp.Next(kname)); - test.Printf(_L("Found kernel process: %S\n"), &kname); - kname.Append(KLitNull); - TInt i; - for (i=0; i0) - tname.AppendNum(i); - TFindThread ft(tname); - test_KErrNone(ft.Next(tname2)); - TInt r = IdleThread[i].Open(ft); - test_KErrNone(r); - IdleThread[i].FullName(tname2); - test.Printf(_L("Found and opened %S\n"), &tname2); - } - } - -void TCpuUsage::Sample() - { - iElapsedTime.Sample(&SampleIdleTimes, this); - } - -void TCpuUsage::SampleIdleTimes(TAny* aPtr) - { - TCpuUsage& me = *(TCpuUsage*)aPtr; - assert(NumberOfCpus > 0); - TInt i; - for (i=0; i= NumberOfCpus) - return 0; - TInt64 idle_time = iIdleTime[aCpu] - aStart.iIdleTime[aCpu]; - TInt64 elapsed_time = iElapsedTime - aStart.iElapsedTime; - return elapsed_time - idle_time; - } - - - -/****************************************************************************** - * Generic CPU Consumer - ******************************************************************************/ -enum TCpuEaterType - { - EEaterStd =0, // do CPU-intensive work with few memory references - EEaterMemoryLocalS =1, // do loads of memory references with reasonable locality, shared - EEaterMemoryNonLocalS =2, // do loads of memory references with poor locality, shared - EEaterMemoryLocalU =3, // do loads of memory references with reasonable locality, unshared - EEaterMemoryNonLocalU =4, // do loads of memory references with poor locality, unshared - EEaterMemoryAtomic =5, // do loads of atomic memory references - EEaterMemoryAtomic2 =6, // do loads of atomic memory references - EEaterAmdahl =7, // do CPU-intensive work interspersed with serialized sections - }; - -class CDefaultCpuEater; - -class REaterArray; -class MCpuEater - { -public: - MCpuEater(); - virtual ~MCpuEater(); - virtual void Eat(TInt aTime, TUint32* aWorkDone)=0; - virtual void Calibrate(); - inline TBool IsCalibrated() { return iCalibration!=0; } -protected: - TUint32 WorkValue(TInt aTime); - TUint32 iCalibration; // work value for 2^16 microseconds - TUint16 iInstance; - TUint16 iType; - - friend class REaterArray; - }; - -MCpuEater::MCpuEater() - { - iCalibration = 0; // uncalibrated - iInstance = KMaxTUint16; // dummy value - iType = KMaxTUint16; // dummy value - } - -MCpuEater::~MCpuEater() - { - } - -// Calibration is for 2^KLog2CalibrateTime microseconds -const TInt KLog2CalibrateTime = 13; - -TUint32 MCpuEater::WorkValue(TInt aTime) - { - if (iCalibration == 0) - return aTime; - TUint64 x = TUint64(aTime) * TUint64(iCalibration); - x >>= (KLog2CalibrateTime + 2); // Factor of 4 margin for slowdowns - TUint32 r = I64LOW(x); - if (I64HIGH(x)) - r = KMaxTUint32; - if (r == 0) - return 1; - if (r > iCalibration) - return iCalibration; - return r; - } - -void MCpuEater::Calibrate() - { - iCalibration = 0; - TUint32 work = 1; - TUint64 used = 1; - TUint64 threshold = 1; - threshold <<= KLog2CalibrateTime; - while (work) - { - TThreadCpuUsageSample initial; - TThreadCpuUsageSample final; - initial.Sample(RThread()); - Eat(work, 0); - final.Sample(RThread()); - used = final.CpuTimeDelta(initial); - if (used >= threshold) - break; - work <<= 1; - } - assert(work > 0); - TUint64 c(work); - c <<= KLog2CalibrateTime; - c /= used; - if (I64HIGH(c)) - iCalibration = KMaxTUint32; - else if (I64LOW(c)) - iCalibration = I64LOW(c); - else - iCalibration = 1; - test.Printf(_L("MCpuEater::Calibrate() %u\n"), iCalibration); - } - - -class REaterArray : public RPointerArray - { -public: - REaterArray(); - void Close(); - MCpuEater* Find(TInt aType, TInt aInstance); - MCpuEater* FindOrCreateL(TInt aType, TInt aInstance); -private: - MCpuEater* CreateLC(TInt aType); -private: - class MDummy : public MCpuEater - { - public: - MDummy(TInt aType, TInt aInstance) - { iType=TUint16(aType); iInstance=TUint16(aInstance); } - virtual ~MDummy() - {} - virtual void Eat(TInt, TUint32*) - {} - }; -private: - static TBool Identity(const MCpuEater& aL, const MCpuEater& aR); - static TInt Ordering(const MCpuEater& aL, const MCpuEater& aR); - }; - -REaterArray::REaterArray() - : RPointerArray(8, 2*256) - { - } - -void REaterArray::Close() - { - ResetAndDestroy(); - } - -TBool REaterArray::Identity(const MCpuEater& aL, const MCpuEater& aR) - { - return (aL.iType==aR.iType && aL.iInstance==aR.iInstance); - } - -TInt REaterArray::Ordering(const MCpuEater& aL, const MCpuEater& aR) - { - if (aL.iType > aR.iType) - return 1; - if (aL.iType < aR.iType) - return -1; - if (aL.iInstance > aR.iInstance) - return 1; - if (aL.iInstance < aR.iInstance) - return -1; - return 0; - } - -MCpuEater* REaterArray::Find(TInt aType, TInt aInstance) - { - MDummy search(aType, aInstance); - TInt ix = FindInOrder(&search, &Ordering); - if (ix < 0) - return 0; - return (*this)[ix]; - } - -MCpuEater* REaterArray::FindOrCreateL(TInt aType, TInt aInstance) - { - MCpuEater* p = Find(aType, aInstance); - if (p) - return p; - p = CreateLC(aType); - p->iType = TUint16(aType); - p->iInstance = TUint16(aInstance); - InsertInOrderL(p, &Ordering); - CleanupStack::Pop(); - return p; - } - -/****************************************************************************** - * Generic zero-drift timed events - ******************************************************************************/ -class CLoadSim; -class MEvent - { -public: - MEvent(CLoadSim*, TInt); - virtual void Start()=0; - virtual ~MEvent(); - inline TBool Queued() const - { return iQueued; } -protected: - void QueueAt(TUint32 aTime); - void QueueAfter(TUint32 aInterval); - void Dequeue(); - virtual TInt Event(); - inline TUint64 Random(); -protected: - TUint8 iId; - TUint8 iQueued; - TUint8 iE1; - TUint8 iE2; - MEvent* iChain; - CLoadSim* iT; - TUint32 iNextEventTime; - friend class CLoadSim; - }; - -class CLoadSim : public CActive - { -public: - static CLoadSim* NewL(); - ~CLoadSim(); - inline TInt TimerPeriod() const - { return iTimerPeriod; } - TUint64 Random(); -private: - CLoadSim(); - virtual void RunL(); - virtual void DoCancel(); - void StartTimer(); -private: - RTimer iTimer; - TUint64 iSeed; - MEvent* iNextEvent; - TUint32 iIterations; - TUint32 iLastTrigger; // Last trigger time in ticks - TInt iCarry; - TInt iTimerPeriod; // Timer tick period in microseconds - TInt iMaxDelta; - TUint8 iInRunL; - TUint8 iTimerRunning; - TUint8 iTimerInit; - TUint8 iOffsetInit; - TUint32 iOffset; -private: - friend class MEvent; - }; - -inline TUint64 MEvent::Random() - { return iT->Random(); } - -CLoadSim::CLoadSim() - : CActive(EPriorityStandard) - { - iSeed = 0xadf85458; - assert(HAL::Get(HAL::ENanoTickPeriod, iTimerPeriod)==KErrNone); - iMaxDelta = KMaxTInt / (2*iTimerPeriod); - } - -CLoadSim::~CLoadSim() - { - Cancel(); - iTimer.Close(); - } - -CLoadSim* CLoadSim::NewL() - { - CLoadSim* p = new (ELeave) CLoadSim(); - CleanupStack::PushL(p); - User::LeaveIfError(p->iTimer.CreateLocal()); - CleanupStack::Pop(); - return p; - } - -void CLoadSim::DoCancel() - { - iTimer.Cancel(); - iTimerRunning = 0; - } - -void CLoadSim::RunL() - { - TRACE(RDebug::Printf("!%d\n", iStatus.Int())); - iTimerRunning = 0; - iInRunL = 1; - TUint32 now = iLastTrigger; - if (iStatus == KErrNone) - { - now += iCarry; - iLastTrigger = now; - } - else if (iStatus == KErrArgument) - { - now += iCarry; - } - else if (iStatus == KErrCancel) - { - iLastTrigger += iCarry; // trigger time was still updated - } - iCarry = 0; - MEvent* e = 0; - FOREVER - { - ++iIterations; - e = iNextEvent; - if (!e || e->iNextEventTime>now) - break; - iNextEvent = e->iChain; - e->iChain = 0; - e->iQueued = 0; - e->Event(); - } - if (e) - { - TInt delta = TInt(e->iNextEventTime - iLastTrigger); - if (delta > iMaxDelta) - delta = iMaxDelta; - if (delta < -iMaxDelta) - delta = -iMaxDelta; - iCarry = delta; - TInt us = delta * iTimerPeriod; - TRACE(RDebug::Printf("T+%d\n", us)); - iTimer.AgainHighRes(iStatus, us); - SetActive(); - iTimerRunning = 1; - } - iInRunL = 0; - } - -void CLoadSim::StartTimer() - { - if (iInRunL) - return; - if (iTimerRunning) - { - TRACE(RDebug::Printf("TC\n")); - iTimer.Cancel(); // will cause RunL with KErrCancel which will restart timer - return; - } - TInt delta = TInt(iNextEvent->iNextEventTime - iLastTrigger); - if (delta > iMaxDelta) - delta = iMaxDelta; - if (delta < -iMaxDelta) - delta = -iMaxDelta; - iCarry = delta; - TInt us = delta * iTimerPeriod; - if (iTimerInit) - { - TRACE(RDebug::Printf("sT+%d\n", us)); - iTimer.AgainHighRes(iStatus, us); - } - else - { - if (!iOffsetInit) - iOffsetInit=1, iOffset=User::NTickCount(); - TRACE(RDebug::Printf("sT++%d\n", us)); - iTimer.HighRes(iStatus, us); - iTimerInit = 1; - } - SetActive(); - iTimerRunning = 1; - } - -TUint64 CLoadSim::Random() - { - LFSR(iSeed); - TUint32 h = I64HIGH(iSeed); - TUint32 l = I64LOW(iSeed); - h *= 0x9e3779b9u; - l *= 0x9e3779b9u; - return MAKE_TUINT64(l,h); - } - -MEvent::MEvent(CLoadSim* aT, TInt aId) - { - iId = (TUint8)aId; - iQueued = 0; - iE1 = 0; - iE2 = 0; - iChain = 0; - iT = aT; - iNextEventTime = 0; - } - -MEvent::~MEvent() - { - if (iT) - Dequeue(); - } - -void MEvent::QueueAt(TUint32 aTime) - { - TRACE(RDebug::Printf("Q%d@%u\n", iId, aTime)); - if (iQueued) - Dequeue(); - MEvent** p = &iT->iNextEvent; - MEvent* e = iT->iNextEvent; - for (; e && e->iNextEventTime <= aTime; p=&e->iChain, e=e->iChain) - {} - iChain = e; - *p = this; - iNextEventTime = aTime; - iQueued = 1; - if (iT->iNextEvent==this && !iT->iInRunL) - iT->StartTimer(); - } - -void MEvent::QueueAfter(TUint32 aInterval) - { - TRACE(RDebug::Printf("Q%d+%u\n", iId, aInterval)); - TUint32 now = User::NTickCount(); - if (!iT->iTimerInit) - iT->iOffset=now, iT->iOffsetInit=1; - QueueAt(now-iT->iOffset+aInterval); - } - -void MEvent::Dequeue() - { - TRACE(RDebug::Printf("DQ%d\n", iId)); - if (!iQueued) - return; - MEvent* e = iT->iNextEvent; - for (; e && e->iChain!=this; e=e->iChain) - {} - if (e) - { - e->iChain = iChain; - } - iChain = 0; - iQueued = 0; - } - -TInt MEvent::Event() - { - TRACE(RDebug::Printf("*%d\n", iId)); - return iId; - } - - - -/****************************************************************************** - * Poisson process simulation - ******************************************************************************/ -class MDiscretePoisson : public MEvent - { -public: - MDiscretePoisson(CLoadSim* aT, TInt aId, TUint32 aMicroseconds); - ~MDiscretePoisson(); - virtual void Start(); - virtual TInt Event(); - virtual void PoissonEvent(); -public: - TUint32 iUs; - TBool iContinue; - }; - -MDiscretePoisson::MDiscretePoisson(CLoadSim* aT, TInt aId, TUint32 aMicroseconds) - : MEvent(aT, aId) - { - iUs = aMicroseconds; - iContinue = EFalse; - } - -MDiscretePoisson::~MDiscretePoisson() - { - } - -void MDiscretePoisson::Start() - { - iContinue = ETrue; - TUint32 gap = ExpRV(Random(), iUs, iT->TimerPeriod()); - TRACE(RDebug::Printf("GG%u\n", gap)); - QueueAt(iNextEventTime + gap); - } - -TInt MDiscretePoisson::Event() - { - PoissonEvent(); - if (iContinue) - Start(); - return MEvent::Event(); - } - -void MDiscretePoisson::PoissonEvent() - { - } - - - -/****************************************************************************** - * Consume a specified amount of CPU time in either a continuous - * or 'staccato' fashion (i.e. in irregular intervals punctuated by gaps) - ******************************************************************************/ -class CStaccatoCpuEater : public CActive, public MEvent - { -public: - CStaccatoCpuEater(CLoadSim* aT, MCpuEater* aE, TUint32 aGranularity, TUint32 aMeanGap); - ~CStaccatoCpuEater(); - void EatMore(TInt64 aMicroseconds); - TUint32 WorkDone() const { return iWorkDone; } - TUint32 Invocations() const { return iInvocations; } -private: - virtual void RunL(); - virtual void DoCancel(); - virtual void Start(); - virtual TInt Event(); - void StartEating(); -private: - MCpuEater* iE; - TUint32 iWorkDone; - TUint32 iInvocations; - TUint32 iGranularity; - TUint32 iMeanGap; - TBool iEating; - TInt64 iRemainingCpuTime; - TTimeIntervalMicroSeconds iInitialCpuTime; - TTimeIntervalMicroSeconds iFinalCpuTime; - TInt64 iTotalCpuTime; - }; - -CStaccatoCpuEater::CStaccatoCpuEater(CLoadSim* aT, MCpuEater* aE, TUint32 aGranularity, TUint32 aMeanGap) - : CActive(EPriorityIdle), - MEvent(aT, 0x53) - { - iE = aE; - iWorkDone = 0; - iInvocations = 0; - iGranularity = aGranularity; - iMeanGap = aMeanGap; - iEating = EFalse; - iRemainingCpuTime = 0; - } - -CStaccatoCpuEater::~CStaccatoCpuEater() - { - Cancel(); - } - -void CStaccatoCpuEater::EatMore(TInt64 aMicroseconds) - { - TRACE(RDebug::Printf("E+%08x %08x\n", I64HIGH(aMicroseconds), I64LOW(aMicroseconds))); - iRemainingCpuTime += aMicroseconds; - if (!Queued() && !iEating && iRemainingCpuTime>0) - StartEating(); - } - -void CStaccatoCpuEater::RunL() - { - TInt time = KMaxTInt; - if (iRemainingCpuTime < TInt64(KMaxTInt)) - time = I64LOW(iRemainingCpuTime); - ++iInvocations; - iE->Eat(time, &iWorkDone); - TTimeIntervalMicroSeconds ms; - TInt r = RThread().GetCpuTime(ms); - assert(r==KErrNone); - if (ms < iFinalCpuTime) - { - SetActive(); - TRequestStatus* pS = &iStatus; - User::RequestComplete(pS, 0); - return; - } - iEating = EFalse; - TInt64 delta = ms.Int64() - iInitialCpuTime.Int64(); - iRemainingCpuTime -= delta; - iTotalCpuTime += delta; - TRACE(RDebug::Printf("D=%8u T=%10u\n",I64LOW(delta),I64LOW(iTotalCpuTime))); - if (iRemainingCpuTime > 0) - { - TUint32 gap = ExpRV(Random(), iMeanGap, iT->TimerPeriod()); - TRACE(RDebug::Printf("G%u\n", gap)); - QueueAfter(gap); - } - } - -void CStaccatoCpuEater::DoCancel() - { - MEvent::Dequeue(); - iEating = EFalse; - } - -void CStaccatoCpuEater::Start() - { - } - -TInt CStaccatoCpuEater::Event() - { - if (!iEating && iRemainingCpuTime>0) - { - StartEating(); - } - return MEvent::Event(); - } - -void CStaccatoCpuEater::StartEating() - { - iEating = ETrue; - TInt r = RThread().GetCpuTime(iInitialCpuTime); - assert(r==KErrNone); - if (iGranularity) - { - TInt howmuch = ExpRV(iT->Random(), iGranularity, 1); - TRACE(RDebug::Printf("SE+%08x\n", howmuch)); - iFinalCpuTime = iInitialCpuTime.Int64() + TInt64(howmuch); - } - else - iFinalCpuTime = iInitialCpuTime.Int64() + iRemainingCpuTime; // continuous CPU use - SetActive(); - TRequestStatus* pS = &iStatus; - User::RequestComplete(pS, 0); - } - - - -/****************************************************************************** - * Consume CPU time in a bursty fashion - ******************************************************************************/ -class CBurstyCpuEater : public CStaccatoCpuEater, public MDiscretePoisson - { -public: - struct SParams - { - MCpuEater* iE; - TUint32 iGranularity; - TUint32 iMeanIntraBurstGap; - TUint32 iMeanBurstLength; - TUint32 iMeanInterBurstGap; - }; -public: - CBurstyCpuEater(CLoadSim* aT, const SParams& aParams); - ~CBurstyCpuEater(); - virtual void Start(); - virtual void PoissonEvent(); -public: - TUint32 iMeanBurstLength; - TUint32 iMeanInterBurstGap; - }; - -CBurstyCpuEater::CBurstyCpuEater(CLoadSim* aT, const SParams& aParams) - : CStaccatoCpuEater(aT, aParams.iE, aParams.iGranularity, aParams.iMeanIntraBurstGap), - MDiscretePoisson(aT, 0x42, aParams.iMeanInterBurstGap) - { - iMeanBurstLength = aParams.iMeanBurstLength; - iMeanInterBurstGap = aParams.iMeanInterBurstGap; - } - -CBurstyCpuEater::~CBurstyCpuEater() - { - } - -void CBurstyCpuEater::Start() - { - if (iMeanInterBurstGap > 0) - { - PoissonEvent(); - MDiscretePoisson::Start(); - } - else - { - EatMore(iMeanBurstLength); // one single burst - } - } - -void CBurstyCpuEater::PoissonEvent() - { - TInt burstLen = ExpRV(CStaccatoCpuEater::Random(), iMeanBurstLength, 1); - EatMore(burstLen); - } - - - -/****************************************************************************** - * Stop the active scheduler after a certain time - ******************************************************************************/ -class CTimedStopper : public CActive - { -public: - static CTimedStopper* NewL(); - ~CTimedStopper(); - void Start(TInt64 aMicroseconds); -private: - CTimedStopper(); - virtual void RunL(); - virtual void DoCancel(); -private: - RTimer iTimer; - }; - -CTimedStopper::CTimedStopper() - : CActive(EPriorityHigh) - { - } - -CTimedStopper::~CTimedStopper() - { - Cancel(); - iTimer.Close(); - } - -CTimedStopper* CTimedStopper::NewL() - { - CTimedStopper* p = new (ELeave) CTimedStopper(); - CleanupStack::PushL(p); - User::LeaveIfError(p->iTimer.CreateLocal()); - CleanupStack::Pop(); - return p; - } - -void CTimedStopper::DoCancel() - { - iTimer.Cancel(); - } - -void CTimedStopper::RunL() - { - CActiveScheduler::Stop(); - } - -void CTimedStopper::Start(TInt64 aMicroseconds) - { - TInt p = (TInt)aMicroseconds; - iTimer.HighRes(iStatus, p); - SetActive(); - } - - - - - -/****************************************************************************** - * Do something CPU intensive to consume CPU time - ******************************************************************************/ -class CDefaultCpuEater : public CBase, public MCpuEater - { -public: - CDefaultCpuEater(); - ~CDefaultCpuEater(); - virtual void Eat(TInt aTime, TUint32* aWorkDone); -protected: - TUint64 iX; - }; - -CDefaultCpuEater::CDefaultCpuEater() - { - iX = 1; - } - -CDefaultCpuEater::~CDefaultCpuEater() - { - } - -void CDefaultCpuEater::Eat(TInt aTime, TUint32* aWorkDone) - { - const TUint64 KMagic = UI64LIT(0x9e3779b97f4a7c15); - TUint32 work = WorkValue(aTime); - if (aWorkDone) - *aWorkDone += work; - while (work--) - iX *= KMagic; - } - - - -/****************************************************************************** - * Do something CPU intensive to consume CPU time, partially serialized - ******************************************************************************/ -class CAmdahlCpuEater : public CDefaultCpuEater - { -public: - static CAmdahlCpuEater* NewLC(); - ~CAmdahlCpuEater(); - virtual void Eat(TInt aTime, TUint32* aWorkDone); -protected: - CAmdahlCpuEater(); - void ConstructL(); -protected: - RMutex iMutex; - TUint32 iFactor; - }; - -CAmdahlCpuEater::CAmdahlCpuEater() - { - } - -CAmdahlCpuEater::~CAmdahlCpuEater() - { - iMutex.Close(); - } - -CAmdahlCpuEater* CAmdahlCpuEater::NewLC() - { - CAmdahlCpuEater* p = new (ELeave) CAmdahlCpuEater(); - CleanupStack::PushL(p); - p->ConstructL(); - return p; - } - -void CAmdahlCpuEater::ConstructL() - { - User::LeaveIfError(iMutex.CreateLocal()); - iFactor = KMaxTUint32 / (4*TCpuUsage::N()); - } - -void CAmdahlCpuEater::Eat(TInt aTime, TUint32* aWorkDone) - { - TUint64 t(aTime); - t *= TUint64(iFactor); - t += TUint64(0x80000000u); - t >>= 32; - TInt stime = I64LOW(t); - if (IsCalibrated()) - { - iMutex.Wait(); - CDefaultCpuEater::Eat(stime, aWorkDone); - aTime -= stime; - iMutex.Signal(); - } - CDefaultCpuEater::Eat(aTime, aWorkDone); - } - - - -/****************************************************************************** - * Do something memory intensive to consume CPU time - ******************************************************************************/ -class CMemoryBandwidthEater : public CBase, public MCpuEater - { -public: - static CMemoryBandwidthEater* NewLC(TUint32 aSize, TUint32 aRegionSize, TUint32 aRegionOffset); - ~CMemoryBandwidthEater(); - virtual void Calibrate(); -protected: - CMemoryBandwidthEater(TUint32 aSize, TUint32 aRegionSize, TUint32 aRegionOffset); - void ConstructL(); - virtual void Eat(TInt aTime, TUint32* aWorkDone); - TAny* At(TUint32 aRegion, TUint32 aIndex); - TAny* StepWithinRegion(TAny* aInitial, TUint32 aStep); -protected: - volatile TUint32 iRegionAlloc; - TUint32 iPageSize; - TUint32 iSize; // multiple of page size - TAny* iData; // page aligned - RChunk iChunk; - TUint8 iLog2RegionSize; // log2(bytes per region) - TUint8 iLog2RO; // log2(offset from region n to n+1 in bytes) - TUint8 iLog2PageSize; - TUint8 iRegionBits; // number of bits to specify region - TUint32 iNRgn; - TUint32 iRegionMask; - TUint32 iLowerIndexMask; - TUint32 iUpperIndexMask; - }; - -TUint32 AtomicClearLS1(volatile TUint32* aMask) - { - TUint32 initial = *aMask; - TUint32 final; - do { - final = initial & (initial-1); - } while(!__e32_atomic_cas_ord32(aMask, &initial, final)); - return initial; - } - -TInt AtomicAllocBit(volatile TUint32* aMask) - { - return __e32_find_ls1_32(AtomicClearLS1(aMask)); - } - -TUint32 AtomicFreeBit(volatile TUint32* aMask, TInt aBit) - { - return __e32_atomic_ior_ord32(aMask, 1u<ConstructL(); - return p; - } - -CMemoryBandwidthEater::CMemoryBandwidthEater(TUint32 aSize, TUint32 aRegionSize, TUint32 aRegionOffset) - { - TInt r = HAL::Get(HAL::EMemoryPageSize, (TInt&)iPageSize); - assert(r==KErrNone); - iLog2PageSize = (TUint8)__e32_find_ms1_32(iPageSize); - assert( !(aRegionSize & (aRegionSize-1)) ); - assert( !(aRegionOffset & (aRegionOffset-1)) ); - iLog2RegionSize = (TUint8)__e32_find_ms1_32(aRegionSize); - iLog2RO = (TUint8)__e32_find_ms1_32(aRegionOffset); - TUint32 round = (aRegionSize>iPageSize) ? aRegionSize : iPageSize; - iSize = (aSize + round - 1) &~ (round - 1); - --iSize; - iSize |= (iSize>>1); - iSize |= (iSize>>2); - iSize |= (iSize>>4); - iSize |= (iSize>>8); - iSize |= (iSize>>16); - ++iSize; - iNRgn = iSize >> iLog2RegionSize; - if (iNRgn>=32) - iRegionAlloc = ~0u; - else - iRegionAlloc = ~((~0u)<>(iRegionBits+iLog2RO))<<(iRegionBits+iLog2RO); - } - -CMemoryBandwidthEater::~CMemoryBandwidthEater() - { - iChunk.Close(); - } - -void CMemoryBandwidthEater::ConstructL() - { - TInt mask = (1<<20)-1; - TInt maxSize = (TInt(iSize)+mask)&~mask; - User::LeaveIfError(iChunk.CreateLocal(iSize, maxSize, EOwnerThread)); - iData = iChunk.Base(); - } - -void CMemoryBandwidthEater::Calibrate() - { - MCpuEater::Calibrate(); - MCpuEater::Calibrate(); - } - -TAny* CMemoryBandwidthEater::At(TUint32 aRegion, TUint32 aIndex) - { - TUint32 offset = aIndex & iLowerIndexMask; - offset |= (aRegion<=0); - TUint32 done = 0; - TUint32 rgnsz = 1u << iLog2RegionSize; - for (; work; work-=done) - { - done = (work>rgnsz) ? rgnsz : work; - TUint8* p = (TUint8*)At(region,0); - TUint8 prev = *p; - TUint32 n = done; - do { - TUint8* q = p; - p = (TUint8*)StepWithinRegion(p, 31); - *q = *p; - } while(--n); - *p = prev; - } - AtomicFreeBit(&iRegionAlloc, region); - } - - -/****************************************************************************** - * Do lots of atomic operations to consume CPU time - ******************************************************************************/ -class CAtomicMemoryBandwidthEater : public CMemoryBandwidthEater - { -public: - static CAtomicMemoryBandwidthEater* NewLC(TUint32 aSize); - ~CAtomicMemoryBandwidthEater(); -protected: - CAtomicMemoryBandwidthEater(TUint32 aSize); - void ConstructL(); - virtual void Eat(TInt aTime, TUint32* aWorkDone); -protected: - volatile TUint32 iX; - }; - -CAtomicMemoryBandwidthEater* CAtomicMemoryBandwidthEater::NewLC(TUint32 aSize) - { - CAtomicMemoryBandwidthEater* p = new (ELeave) CAtomicMemoryBandwidthEater(aSize); - CleanupStack::PushL(p); - p->ConstructL(); - return p; - } - -CAtomicMemoryBandwidthEater::CAtomicMemoryBandwidthEater(TUint32 aSize) - : CMemoryBandwidthEater(aSize, aSize, aSize) - { - iX = TUint32(this) ^ RThread().Id().operator TUint(); - iX *= 0x9e3779b9u; - } - -CAtomicMemoryBandwidthEater::~CAtomicMemoryBandwidthEater() - { - } - -void CAtomicMemoryBandwidthEater::ConstructL() - { - CMemoryBandwidthEater::ConstructL(); - } - -TUint32 AtomicRandom(volatile TUint32* a) - { - TUint32 initial = *a; - TUint32 final; - do { - final = 69069*initial + 41; - } while(!__e32_atomic_cas_ord32(a, &initial, final)); - return final; - } - -void CAtomicMemoryBandwidthEater::Eat(TInt aTime, TUint32* aWorkDone) - { - TUint32 work = WorkValue(aTime); - if (aWorkDone) - *aWorkDone += work; - volatile TUint32* pW = (volatile TUint32*)iData; - const TUint32 mask = iSize/sizeof(TUint32)-1; - TUint32 x = AtomicRandom(&iX); - TUint32 n = work; - do { - TUint32 offset = (x>>2) & mask; - x = 69069*x+41; - __e32_atomic_add_rlx32(pW+offset, 1); - } while(--n); - } - - -/****************************************************************************** - * - ******************************************************************************/ -struct SThreadResult - { - TUint64 iElapsedTime; - TUint64 iCpuTime; - TUint32 iWorkDone; - TUint32 iInvocations; - }; - -struct SThreadParams - { - TInt64 iTestTime; - - TInt iId; - TUint32 iCpuAffinity; - - TInt iPriority; - RSemaphore iTurnstile; - - SThreadResult* iResult; - TInt iGroupId; - - MCpuEater* iE; - TUint32 iGranularity; - TUint32 iMeanIntraBurstGap; - TUint32 iMeanBurstLength; - TUint32 iMeanInterBurstGap; - }; - -class MThreadCompletion - { -public: - virtual void Complete(TBool aOk, SThreadParams* aParams)=0; - }; - -class CThreadI : public CBase - { -public: - CThreadI(); - ~CThreadI(); - static TInt ThreadFunc(TAny* aPtr); - TInt Run(); - void InitL(); -public: - CTrapCleanup* iCleanup; - CActiveScheduler* iAS; - CLoadSim* iL; - CBurstyCpuEater* iB; - CTimedStopper* iStopper; - RSemaphore iTurnstile; - SThreadParams* iParams; - }; - -CThreadI::CThreadI() - { - } - -CThreadI::~CThreadI() - { - iTurnstile.Close(); - delete iStopper; - delete iB; - delete iL; - delete iAS; - delete iCleanup; - } - -TInt CThreadI::ThreadFunc(TAny* aPtr) - { - CThreadI* p = new CThreadI; - if (!p) - return KErrNoMemory; - p->iParams = (SThreadParams*)aPtr; - return p->Run(); - } - -void CThreadI::InitL() - { - iTurnstile = iParams->iTurnstile; - User::LeaveIfError(iTurnstile.Duplicate(RThread(), EOwnerThread)); - iAS = new (ELeave) CActiveScheduler; - CActiveScheduler::Install(iAS); - iL = CLoadSim::NewL(); - CActiveScheduler::Add(iL); - const CBurstyCpuEater::SParams* params = (const CBurstyCpuEater::SParams*)&iParams->iE; - iB = new (ELeave) CBurstyCpuEater(iL, *params); - CActiveScheduler::Add(iB); - iStopper = CTimedStopper::NewL(); - CActiveScheduler::Add(iStopper); - memclr(iParams->iResult, sizeof(*iParams->iResult)); - RThread().SetPriority(TThreadPriority(iParams->iPriority)); - UserSvr::HalFunction(EHalGroupKernel, EKernelHalLockThreadToCpu, (TAny*)iParams->iCpuAffinity, 0); - } - -TInt CThreadI::Run() - { - iCleanup = CTrapCleanup::New(); - if (!iCleanup) - return KErrNoMemory; - TRAPD(r,InitL()); - if (r == KErrNone) - { - TThreadCpuUsageSample initial; - TThreadCpuUsageSample final; - RThread::Rendezvous(KErrNone); - iTurnstile.Wait(); - iB->Start(); - initial.Sample(RThread()); - iStopper->Start(iParams->iTestTime); - CActiveScheduler::Start(); - final.Sample(RThread()); - iParams->iResult->iWorkDone = iB->WorkDone(); - iParams->iResult->iInvocations = iB->Invocations(); - iParams->iResult->iElapsedTime = final.ElapsedTimeDelta(initial); - iParams->iResult->iCpuTime = final.CpuTimeDelta(initial); - } - delete this; - return r; - } - - -/****************************************************************************** - * - ******************************************************************************/ -class CThreadX : public CActive - { -public: - static CThreadX* NewL(SThreadParams* aParams, MThreadCompletion* aComplete); - static CThreadX* NewLC(SThreadParams* aParams, MThreadCompletion* aComplete); - CThreadX(); - ~CThreadX(); - void ConstructL(); - virtual void RunL(); - virtual void DoCancel(); -public: - RThread iThread; - RTimer iTimer; - SThreadParams* iParams; - MThreadCompletion* iComplete; - }; - -CThreadX::CThreadX() - : CActive(EPriorityStandard) - { - } - -CThreadX::~CThreadX() - { - Cancel(); - iTimer.Close(); - iThread.Close(); - } - -CThreadX* CThreadX::NewL(SThreadParams* aParams, MThreadCompletion* aComplete) - { - CThreadX* p = NewLC(aParams, aComplete); - CleanupStack::Pop(); - return p; - } - -CThreadX* CThreadX::NewLC(SThreadParams* aParams, MThreadCompletion* aComplete) - { - CThreadX* p = new (ELeave) CThreadX(); - p->iParams = aParams; - p->iComplete = aComplete; - CleanupStack::PushL(p); - p->ConstructL(); - return p; - } - -const TInt KThreadHeapMin = 0x1000; -const TInt KThreadHeapMax = 0x200000; -void CThreadX::ConstructL() - { - CActiveScheduler::Add(this); - TRequestStatus s0, s1; - User::LeaveIfError(iTimer.CreateLocal()); - User::LeaveIfError(iThread.Create(KNullDesC, &CThreadI::ThreadFunc, 0x1000, KThreadHeapMin, KThreadHeapMax, iParams)); - iThread.Rendezvous(s1); - if (s1!=KRequestPending) - { - User::WaitForRequest(s1); - User::Leave(s1.Int()); - } - iTimer.After(s0, 5*1000*1000); - iThread.Resume(); - User::WaitForRequest(s0, s1); - if (s1==KRequestPending) - { - iThread.Terminate(KErrCouldNotStart); - User::WaitForRequest(s1); - User::Leave(KErrTimedOut); - } - iTimer.Cancel(); - User::WaitForRequest(s0); - if (iThread.ExitType() != EExitPending) - { - User::Leave(KErrDied); - } - iThread.Logon(iStatus); - if (iStatus!=KRequestPending) - { - User::WaitForRequest(iStatus); - User::Leave(iStatus.Int()); - } - SetActive(); - User::LeaveIfError(s1.Int()); - } - -void CThreadX::DoCancel() - { - iThread.Terminate(KErrCouldNotStart); - } - -void CThreadX::RunL() - { - TBool ok = ETrue; - if (iThread.ExitType() != EExitKill) - ok = EFalse; - if (iThread.ExitReason() != KErrNone) - ok = EFalse; - if (iComplete) - iComplete->Complete(ok, iParams); - } - - -/****************************************************************************** - * - ******************************************************************************/ -struct STestThreadDesc - { - TUint32 iCpuAffinity; - TInt iPriority; - TInt iGroupId; - TUint16 iEaterType; - TUint16 iEaterInstance; - TUint32 iGranularity; - TUint32 iMeanIntraBurstGap; - TUint32 iMeanBurstLength; - TUint32 iMeanInterBurstGap; - - static STestThreadDesc* ContinuousL(TInt aPri = EPriorityNormal); - static STestThreadDesc* ContinuousLC(TInt aPri = EPriorityNormal); - static STestThreadDesc* StaccatoL(TUint32 aGranularity, TUint32 aMeanGap, TInt aPri = EPriorityNormal); - static STestThreadDesc* StaccatoLC(TUint32 aGranularity, TUint32 aMeanGap, TInt aPri = EPriorityNormal); - }; - -STestThreadDesc* STestThreadDesc::ContinuousLC(TInt aPri) - { - STestThreadDesc* p = (STestThreadDesc*)User::AllocLC(sizeof(STestThreadDesc)); - p->iCpuAffinity = 0xffffffff; - p->iPriority = aPri; - p->iGroupId = 0; - p->iEaterType = EEaterStd; - p->iEaterInstance = 0; - p->iGranularity = 0; - p->iMeanIntraBurstGap = 0; - p->iMeanBurstLength = KMaxTInt32; - p->iMeanInterBurstGap = 0; - return p; - } - -STestThreadDesc* STestThreadDesc::ContinuousL(TInt aPri) - { - STestThreadDesc* p = ContinuousLC(aPri); - CleanupStack::Pop(); - return p; - } - -STestThreadDesc* STestThreadDesc::StaccatoLC(TUint32 aGranularity, TUint32 aMeanGap, TInt aPri) - { - STestThreadDesc* p = (STestThreadDesc*)User::AllocLC(sizeof(STestThreadDesc)); - p->iCpuAffinity = 0xffffffff; - p->iPriority = aPri; - p->iGroupId = 0; - p->iEaterType = EEaterStd; - p->iEaterInstance = 0; - p->iGranularity = aGranularity; - p->iMeanIntraBurstGap = aMeanGap; - p->iMeanBurstLength = KMaxTInt32; - p->iMeanInterBurstGap = 0; - return p; - } - -STestThreadDesc* STestThreadDesc::StaccatoL(TUint32 aGranularity, TUint32 aMeanGap, TInt aPri) - { - STestThreadDesc* p = StaccatoLC(aGranularity, aMeanGap, aPri); - CleanupStack::Pop(); - return p; - } - - -class CTest : public CBase, public MThreadCompletion - { -public: - struct SStats - { - TInt64 iTotalCpu; - TInt64 iMinCpu; - TInt64 iMaxCpu; - TInt64 iTotalWork; - TInt64 iMinWork; - TInt64 iMaxWork; - }; -public: - static CTest* NewL(TInt64 aTestTime, TInt aNumTypes, ...); - ~CTest(); - RPointerArray& Threads() - { return iP; } - TInt Execute(); - void PrintResults() const; - void GetStats(SStats& aStats, TInt aFirstThread=0, TInt aCount=KMaxTInt) const; - TInt64 TotalCpuAll() const; -private: - CTest(); - void ConstructL(TInt64 aTestTime, TInt aNumTypes, VA_LIST aList); - SThreadParams* AddThreadParamsL(); - CThreadX* AddThreadXL(SThreadParams* aParams); - - virtual void Complete(TBool aOk, SThreadParams* aParams); -private: - RPointerArray iP; - RPointerArray iTX; - REaterArray iEaters; - RSemaphore iTurnstile; - TInt iCompleteCount; - TCpuUsage iInitialCpuUsage; - TCpuUsage iFinalCpuUsage; - }; - -CTest::CTest() - : iP(32), - iTX(32) - { - } - -CTest::~CTest() - { - iTX.ResetAndDestroy(); - - TInt i; - TInt c = iP.Count(); - for (i=0; iiResult); - User::Free(p); - } - } - iP.Close(); - iEaters.Close(); - iTurnstile.Close(); - } - -CTest* CTest::NewL(TInt64 aTestTime, TInt aNumTypes, ...) - { - VA_LIST list; - VA_START(list, aNumTypes); - CTest* p = new (ELeave) CTest; - CleanupStack::PushL(p); - p->ConstructL(aTestTime, aNumTypes, list); - CleanupStack::Pop(); - return p; - } - -SThreadParams* CTest::AddThreadParamsL() - { - SThreadResult* tr = (SThreadResult*)User::AllocLC(sizeof(SThreadResult)); - SThreadParams* tp = (SThreadParams*)User::AllocLC(sizeof(SThreadParams)); - memclr(tr, sizeof(SThreadResult)); - tp->iResult = tr; - iP.AppendL(tp); - CleanupStack::Pop(2); - tp->iTurnstile = iTurnstile; - return tp; - } - -CThreadX* CTest::AddThreadXL(SThreadParams* aP) - { - if (aP->iGranularity==0 && aP->iMeanInterBurstGap==0) - { - // continuous use thread - if (TInt64(aP->iMeanBurstLength) >= aP->iTestTime) - aP->iMeanBurstLength = I64LOW(aP->iTestTime) + (I64LOW(aP->iTestTime)>>1); - } - CThreadX* tx = CThreadX::NewLC(aP, this); - iTX.AppendL(tx); - CleanupStack::Pop(); - return tx; - } - -void CTest::Complete(TBool aOk, SThreadParams*) - { - if (!aOk || --iCompleteCount==0) - CActiveScheduler::Stop(); - } - - -void CTest::ConstructL(TInt64 aTestTime, TInt aNumTypes, VA_LIST aList) - { - typedef const STestThreadDesc* TTestThreadDescPtrC; - - User::LeaveIfError(iTurnstile.CreateLocal(0)); - TInt tt; - TInt tid = 0; - for (tt=0; ttiId = tid; - tp->iTestTime = aTestTime; - tp->iCpuAffinity = ttd.iCpuAffinity; - tp->iPriority = ttd.iPriority; - tp->iGroupId = ttd.iGroupId; - tp->iE = iEaters.FindOrCreateL(ttd.iEaterType, ttd.iEaterInstance); - tp->iGranularity = ttd.iGranularity; - tp->iMeanIntraBurstGap = ttd.iMeanIntraBurstGap; - tp->iMeanBurstLength = ttd.iMeanBurstLength; - tp->iMeanInterBurstGap = ttd.iMeanInterBurstGap; - AddThreadXL(tp); - } - } - } - -TInt CTest::Execute() - { - iCompleteCount = iP.Count(); - iInitialCpuUsage.Sample(); - iTurnstile.Signal(iCompleteCount); - CActiveScheduler::Start(); - iFinalCpuUsage.Sample(); - return iCompleteCount ? KErrGeneral : KErrNone; - } - -void CTest::GetStats(SStats& a, TInt aFirstThread, TInt aCount) const - { - a.iTotalCpu = 0; - a.iMinCpu = KMaxTInt64; - a.iMaxCpu = KMinTInt64; - a.iTotalWork = 0; - a.iMinWork = KMaxTInt64; - a.iMaxWork = KMinTInt64; - TInt nt = iP.Count(); - if (aFirstThread > nt) - aFirstThread = nt; - if (aCount > nt - aFirstThread) - aCount = nt - aFirstThread; - TInt i = aFirstThread; - for (; iiResult; - TInt64 cpu = tr->iCpuTime; - TInt64 work = tr->iWorkDone; - a.iTotalCpu += cpu; - a.iTotalWork += work; - if (cpu < a.iMinCpu) - a.iMinCpu = cpu; - if (cpu > a.iMaxCpu) - a.iMaxCpu = cpu; - if (work < a.iMinWork) - a.iMinWork = work; - if (work > a.iMaxWork) - a.iMaxWork = work; - } - } - -TInt64 CTest::TotalCpuAll() const - { - TInt i; - TInt nc = TCpuUsage::N(); - TInt64 totalCpuAll = 0; - for (i=0; iiResult; - test.Printf(_L("%2u: E=%10u C=%10u I=%10u W=%10u\n"), - i, I64LOW(tr->iElapsedTime), I64LOW(tr->iCpuTime), tr->iInvocations, tr->iWorkDone ); - totalCpu += tr->iCpuTime; - totalWork += TInt64(tr->iWorkDone); - } - test.Printf(_L("Total C=%12Lu W=%12Lu\n"), totalCpu, totalWork); - for (i=0; iCalibrate(); - return p; - } - case EEaterMemoryLocalS: - { - CMemoryBandwidthEater* p = CMemoryBandwidthEater::NewLC(0x8000, 0x0800, 0x0800); - p->Calibrate(); - return p; - } - case EEaterMemoryNonLocalS: - { - CMemoryBandwidthEater* p = CMemoryBandwidthEater::NewLC(0x100000, 0x10000, 0x4); - p->Calibrate(); - return p; - } - case EEaterMemoryLocalU: - { - CMemoryBandwidthEater* p = CMemoryBandwidthEater::NewLC(0x4000, 0x4000, 0x4000); - p->Calibrate(); - return p; - } - case EEaterMemoryNonLocalU: - { - CMemoryBandwidthEater* p = CMemoryBandwidthEater::NewLC(0x80000, 0x80000, 0x80000); - p->Calibrate(); - return p; - } - case EEaterMemoryAtomic: - { - CAtomicMemoryBandwidthEater* p = CAtomicMemoryBandwidthEater::NewLC(0x1000); - p->Calibrate(); - return p; - } - case EEaterMemoryAtomic2: - { - CAtomicMemoryBandwidthEater* p = CAtomicMemoryBandwidthEater::NewLC(0x8000); - p->Calibrate(); - return p; - } - case EEaterAmdahl: - { - CAmdahlCpuEater* p = CAmdahlCpuEater::NewLC(); - p->Calibrate(); - return p; - } - default: - User::Leave(KErrNotSupported); - } - return 0; - } - - - -/****************************************************************************** - * - ******************************************************************************/ - -void RunBenchmarkL(CTest::SStats& aB, STestThreadDesc* aT, TInt aLength) - { - const TInt NC = TCpuUsage::N(); - CTest* p; - TUint32 saved_aff = aT->iCpuAffinity; - aT->iCpuAffinity = NC-1; - p = CTest::NewL(aLength, 1, 1, aT); - TInt r = p->Execute(); - test_KErrNone(r); - p->PrintResults(); - p->GetStats(aB); - delete p; - aT->iCpuAffinity = saved_aff; - } - -void CompareToBenchmark(const CTest::SStats& aS, const CTest::SStats& aB) - { - TReal bCpu = (TReal)aB.iTotalCpu; - TReal bWork = (TReal)aB.iTotalWork; - TReal bEff = bWork/bCpu*1000000.0; - TReal tCpu = (TReal)aS.iTotalCpu; - TReal tWork = (TReal)aS.iTotalWork; - TReal tEff = tWork/tCpu*1000000.0; - TReal mCpu = (TReal)aS.iMinCpu; - TReal MCpu = (TReal)aS.iMaxCpu; - TReal mWork = (TReal)aS.iMinWork; - TReal MWork = (TReal)aS.iMaxWork; - test.Printf(_L("Total CPU usage %6.1f%% of benchmark\n"), 100.0*tCpu/bCpu); - test.Printf(_L("Total work done %6.1f%% of benchmark\n"), 100.0*tWork/bWork); - test.Printf(_L("Max/min ratio %6.1f%% (CPU) %6.1f%% (Work)\n"), 100.0*MCpu/mCpu, 100.0*MWork/mWork); - test.Printf(_L("Work/sec bench: %10.1f test: %10.1f Relative Efficiency %6.1f%%\n"), bEff, tEff, tEff/bEff*100.0); - } - -void ContinuousTestL(TInt aLength, TUint32 aWhich) - { - aLength *= 1000; - const TInt NC = TCpuUsage::N(); - TInt r = 0; - TInt i = 0; - CTest::SStats benchmark; - CTest::SStats st; - CTest::SStats stm; - CTest* p = 0; - TUint et = aWhich >> 28; - TBool separate = (aWhich & 0x08000000u); - TInt instance = 0; - STestThreadDesc* td[2*TCpuUsage::EMaxCpus] = {0}; - STestThreadDesc* tdm[TCpuUsage::EMaxCpus] = {0}; - STestThreadDesc* tdl[TCpuUsage::EMaxCpus] = {0}; - for (i=0; i<2*NC; ++i) - { - td[i] = STestThreadDesc::ContinuousLC(); - td[i]->iEaterType = (TUint16)et; - td[i]->iEaterInstance = (TUint16)(separate ? (instance++) : 0); - if (iiEaterType = (TUint16)et; - tdm[i]->iEaterInstance = (TUint16)(separate ? (instance++) : 0); - tdl[i] = STestThreadDesc::ContinuousLC(); - tdl[i]->iCpuAffinity = i; - tdl[i]->iEaterType = (TUint16)et; - tdl[i]->iEaterInstance = (TUint16)(separate ? (instance++) : 0); - } - } - - test.Printf(_L("\nTesting a single continuous CPU-locked thread\n")); - RunBenchmarkL(benchmark, tdl[NC-1], aLength); - - TInt n; - - if (aWhich & 1) - { - for (n=1; n<=2*NC; ++n) - { - test.Printf(_L("\nTesting %d continuous thread(s) ...\n"), n); - p = CTest::NewL(aLength, 1, -n, td); - r = p->Execute(); - test_KErrNone(r); - p->PrintResults(); - p->GetStats(st); - delete p; - CompareToBenchmark(st, benchmark); - } - } - - TInt h; - if (aWhich & 2) - { - for (h=1; hExecute(); - test_KErrNone(r); - p->PrintResults(); - p->GetStats(st, h, l); - p->GetStats(stm, 0, h); - delete p; - CompareToBenchmark(stm, benchmark); - if (l>0) - CompareToBenchmark(st, benchmark); - } - } - } - - if (aWhich & 4) - { - for (h=1; hExecute(); - test_KErrNone(r); - p->PrintResults(); - p->GetStats(st, h, l); - p->GetStats(stm, 0, h); - delete p; - CompareToBenchmark(stm, benchmark); - if (l>0) - CompareToBenchmark(st, benchmark); - } - } - } - - CleanupStack::PopAndDestroy(NC*4); - } - - -void TestWithOneSpecialL(const TDesC& aTitle, TInt aLength, const CTest::SStats& aB1, const CTest::SStats& aB0, STestThreadDesc* aT1, STestThreadDesc* aT0) - { - const TInt NC = TCpuUsage::N(); - CTest::SStats st; - CTest::SStats sti; - CTest* p = 0; - TInt n; - TInt r; - for (n=1; n<=2*NC-1; ++n) - { - test.Printf(_L("\nTesting %d continuous thread(s) plus %S ...\n"), n, &aTitle); - p = CTest::NewL(aLength, 2, 1, aT1, n, aT0); - r = p->Execute(); - test_KErrNone(r); - p->PrintResults(); - p->GetStats(st, 1, n); - p->GetStats(sti, 0, 1); - delete p; - CompareToBenchmark(sti, aB1); - CompareToBenchmark(st, aB0); - - test.Printf(_L("\nTesting %d continuous thread(s) plus %Sh ...\n"), n, &aTitle); - TInt orig_pri = aT1->iPriority; - aT1->iPriority = EPriorityMore; - p = CTest::NewL(aLength, 2, 1, aT1, n, aT0); - r = p->Execute(); - test_KErrNone(r); - p->PrintResults(); - p->GetStats(st, 1, n); - p->GetStats(sti, 0, 1); - delete p; - CompareToBenchmark(sti, aB1); - CompareToBenchmark(st, aB0); - aT1->iPriority = orig_pri; - } - } - -void ContinuousPlusIntermittentTestL(TInt aLength, TUint32 aWhich) - { - aLength *= 1000; - const TInt NC = TCpuUsage::N(); - TInt i = 0; - CTest::SStats bmc, bmilc, bmilf, bmihc, bmihf; - STestThreadDesc* td = STestThreadDesc::ContinuousLC(); - STestThreadDesc* tdl[TCpuUsage::EMaxCpus] = {0}; - STestThreadDesc* tdilc[TCpuUsage::EMaxCpus] = {0}; // light load, coarse grained - STestThreadDesc* tdilf[TCpuUsage::EMaxCpus] = {0}; // light load, fine grained - STestThreadDesc* tdihc[TCpuUsage::EMaxCpus] = {0}; // heavy load, coarse grained - STestThreadDesc* tdihf[TCpuUsage::EMaxCpus] = {0}; // heavy load, fine grained - for (i=0; iiCpuAffinity = i; - tdilc[i] = STestThreadDesc::StaccatoLC(45000, 500000); - tdilf[i] = STestThreadDesc::StaccatoLC(1000, 50000); - tdihc[i] = STestThreadDesc::StaccatoLC(400000, 500000); - tdihf[i] = STestThreadDesc::StaccatoLC(3000, 5000); - } - - test.Printf(_L("\nTesting a single continuous CPU-locked thread\n")); - RunBenchmarkL(bmc, tdl[NC-1], aLength); - - if (aWhich & 1) - { - test.Printf(_L("\nTesting a single ILC CPU-locked thread\n")); - RunBenchmarkL(bmilc, tdilc[NC-1], aLength); - } - - if (aWhich & 2) - { - test.Printf(_L("\nTesting a single ILF CPU-locked thread\n")); - RunBenchmarkL(bmilf, tdilf[NC-1], aLength); - } - - if (aWhich & 4) - { - test.Printf(_L("\nTesting a single IHC CPU-locked thread\n")); - RunBenchmarkL(bmihc, tdihc[NC-1], aLength); - } - - if (aWhich & 8) - { - test.Printf(_L("\nTesting a single IHF CPU-locked thread\n")); - RunBenchmarkL(bmihf, tdihf[NC-1], aLength); - } - - if (aWhich & 1) - { - TestWithOneSpecialL(_L("ILC"), aLength, bmilc, bmc, tdilc[0], td); - } - if (aWhich & 2) - { - TestWithOneSpecialL(_L("ILF"), aLength, bmilf, bmc, tdilf[0], td); - } - if (aWhich & 4) - { - TestWithOneSpecialL(_L("IHC"), aLength, bmihc, bmc, tdihc[0], td); - } - if (aWhich & 8) - { - TestWithOneSpecialL(_L("IHF"), aLength, bmihf, bmc, tdihf[0], td); - } - CleanupStack::PopAndDestroy(5*NC+1); - } - - -TInt E32Main() - { - RThread().SetPriority(EPriorityAbsoluteHigh); - test.Title(); - User::SetCritical(User::ESystemCritical); - TCpuUsage::Init(); - TInt r = 0; - - CTrapCleanup* cln = CTrapCleanup::New(); - test_NotNull(cln); - CActiveScheduler* as = new CActiveScheduler; - test_NotNull(as); - CActiveScheduler::Install(as); - - test.Printf(_L("\n************************************************************************\n")); - test.Printf(_L("* Testing with CPU intensive loads...\n")); - test.Printf(_L("************************************************************************\n")); - TRAP(r, ContinuousTestL(3000, 1+4)); - test_KErrNone(r); - TRAP(r, ContinuousTestL(10000, 1+4)); - test_KErrNone(r); - TRAP(r, ContinuousPlusIntermittentTestL(10000, 15)); - test_KErrNone(r); - - test.Printf(_L("\n************************************************************************\n")); - test.Printf(_L("* Testing with memory intensive loads, good locality...\n")); - test.Printf(_L("************************************************************************\n")); - TRAP(r, ContinuousTestL(3000, 1+4+0x08000000u+(EEaterMemoryLocalU<<28))); - test_KErrNone(r); - TRAP(r, ContinuousTestL(10000, 1+4+0x08000000u+(EEaterMemoryLocalU<<28))); - test_KErrNone(r); - - test.Printf(_L("\n************************************************************************\n")); - test.Printf(_L("* Testing with memory intensive loads, poor locality...\n")); - test.Printf(_L("************************************************************************\n")); - TRAP(r, ContinuousTestL(3000, 1+4+0x08000000u+(EEaterMemoryNonLocalU<<28))); - test_KErrNone(r); - TRAP(r, ContinuousTestL(10000, 1+4+0x08000000u+(EEaterMemoryNonLocalU<<28))); - test_KErrNone(r); - - test.Printf(_L("\n************************************************************************\n")); - test.Printf(_L("* Testing with memory intensive loads, atomic operations...\n")); - test.Printf(_L("************************************************************************\n")); - TRAP(r, ContinuousTestL(3000, 1+4+(EEaterMemoryAtomic<<28))); - test_KErrNone(r); - TRAP(r, ContinuousTestL(10000, 1+4+(EEaterMemoryAtomic<<28))); - test_KErrNone(r); - - test.Printf(_L("\n************************************************************************\n")); - test.Printf(_L("* Testing with memory intensive loads, atomic operations 2...\n")); - test.Printf(_L("************************************************************************\n")); - TRAP(r, ContinuousTestL(3000, 1+4+(EEaterMemoryAtomic2<<28))); - test_KErrNone(r); - TRAP(r, ContinuousTestL(10000, 1+4+(EEaterMemoryAtomic2<<28))); - test_KErrNone(r); - - test.Printf(_L("\n************************************************************************\n")); - test.Printf(_L("* Testing with CPU intensive loads with some serialization...\n")); - test.Printf(_L("************************************************************************\n")); - TRAP(r, ContinuousTestL(3000, 1+4+(EEaterAmdahl<<28))); - test_KErrNone(r); - TRAP(r, ContinuousTestL(10000, 1+4+(EEaterAmdahl<<28))); - test_KErrNone(r); - - delete as; - delete cln; - return r; - } -