PDQ User Manual
PDQ User Manual
Updated: Apr 18, 2007
This online edition of the PDQ
User Manual is getting a makeover. Please bear with us while the changes are made.
It is being updated to include descriptions for all the supported languages; starting with C, Perl and Python.
Function synopses are also being cross-linked.
The C language version is a complete update for the manual presented in
The Practical Performance Analyst
and corrects several typos found in the original printing.
The Perl version is intended to accompany Chapter 6 of the
Perl::PDQ
book.
| Please note that the PDQ function CreateMultiNode defined in Section 6.6.2 (p. 226) of
the Perl::PDQ book, has not yet been implemented, but see the
Download
page for recent news about this topic.
|
Contents
1 Overview
1.1 System Requirements
1.2 Human Requirements
2 The PDQ Library
2.0.1 Data Types
2.0.2 Global Variables
2.0.3 Functions
3 Function Synopses
3.1 CreateClosed
3.2 CreateNode
3.3 CreateOpen
3.4 GetLoadOpt
3.5 GetNodesCount
3.6 GetQueueLength
3.7 GetResidenceTime
3.8 GetResponse
3.9 GetStreamsCount
3.10 GetThruMax
3.11 GetThruput
3.12 GetUtilization
3.13 Init
3.14 Report
3.15 SetDebug
3.16 SetDemand
3.17 SetTUnit
3.18 SetVisits
3.19 SetWUnit
3.20 Solve
4 Simple Example
4.1 Creating the Model
4.2 PDQ Report
5 Summary
1 Overview
PDQ (Pretty Damn Quick) is a
queueing model solver,
not a
simulator. The queueing theory models discussed in Chapters 2 and 3 of
The Practical Performance Analyst.
are incorporated into the solution methods used by PDQ. This saves you the
labor of implementing thos codes each times you construct a
queueing model and allows you to concentrate on the accuracy of your model
rather than the accuracy of the solutions. PDQ guarantees that for you.
Because PDQ uses algorithmic techniques, rather than an event-based
simulation (such as that used by
C++SIM,
NS, and
REAL) it is
extremely fast at calculating solutions. It is not too far
fetched to say that, most often, PDQ takes more time to print out
the result than it takes to calculate it!
Moreover, PDQ is a flexible library of functions rather than a
hard-wired binary application. This flexibility requires that you
express your performance model in the C language, and then compile
it. Therefore, some programming is required. The good news is, you
do not need to learn yet another strange modeling language. The bad
news is, you have to learn the programming language called C.
In the spirit of the open-source development model (in fact,
before it was even fashionable), PDQ is provided as multiple C
source files to enable it to be compiled and used in the
environment of your choice - from micros to mainframes.
This philosophy imposes two significant requirements.
1.1 System Requirements
You will need access to a C compiler e,g, gcc. Almost by
definition, C compilers are available in every UNIX and Linux environment
and C packages are now provided with most mainframes. There are also a
great many C compiler products available for personal computer operating
systems such Linux, DOS, Windows, and MacOS. For some readers (e.g., those
with a mainframe background) this may appear as a burden. In fact, it
greatly enhances the portability of your PDQ models.
1.2 Human Requirements
Since the source code provided with the book really constitutes a
library of functions for solving performance models, all PDQ
models must be written in C. PDQ works by linking its library routines
into the C code for your performance model. This allows you to employ all
the constructs of a modern procedural programming language such as:
extensible data structures, block coding design, procedure calls, and recursion,
to create and solve potentially very elaborate performance models.
All the examples in this book are constructed using this paradigm. Setting
up a PDQ model is really quite straightforward. To help you gain
familiarity with PDQ, you should first read the section on constructing a
simple example
in this manual. You should then read through a few of examples in the book
(especially those in Chapters 2 and 3) to get a better idea of some of the
variations on coding structure useful for creating and solving a PDQ model.
For those readers not yet familiar with the C language (or C++),
PDQ offers another motivation to learn. The classic reference for
ANSI standard C programming
is authored by Kernighan and Ritchie. Many other excellent
introductory texts
on the C language are also available.
Remember, serious performance modeling is mostly serious programming.
2 The PDQ Library
In this section we describe the global variables, public data types, and
public procedure calls available in the PDQ_Lib.h file.
2.0.1 Data Types
The following data types (implemented as #defines in the C
language) are used in conjunction with PDQ library functions. See
the synopses of procedures for actual syntax.
- PDQ Node Types
- CEN: Generic queueing center. Used with PDQ_CreateNode
- DLY: Generic delay center. Used with PDQ_CreateNode
- integer: Specifies the number of servers for an MSQ node. Used with PDQ_CreateNode
- Service Disciplines
- FCFS: First-come first-served. Used with PDQ_CreateNode
- ISRV: Infinite server. Used with PDQ_CreateNode
- LCFS: Last-come first-served. Used with PDQ_CreateNode
- MSQ: Multi Server Queue. Used with PDQ_CreateNode
- PSHR: Processor sharing server.
Used with PDQ_CreateNode
- Workload Streams
- BATCH:
A batch class workload which is defined to be one with zero
thinktime. Only consistent in the context of a closed queueing
circuit to distinguish from TERM.
Used with PDQ_CreateClosed
- TERM:
A terminal class workload which is defined to be one with
non-zero thinktime. Only consistent in the context of a
closed queueing circuit to distinguish from BATCH.
Used with PDQ_CreateClosed
- TRANS:
A transaction class workload which is defined by an arrival
rate rather than a thinktime. Only consistent in the context
of a open queueing circuit.
Used with PDQ_CreateOpen
- Solution Methods
- APPROX:
Uses the approximate MVA solution technique. See chapter 2
for details. Only consistent in the context of solving a
closed queueing circuit. An approximation to the EXACT or
iterative MVA solution method. Usedwith PDQ_Solve
- CANON:
Uses the canonical solution technique. See Chapter 2 (and the online PDQ
User Manual) for more details. Only consistent in the context of a open
queueing circuit. Used as an argument for the PDQ_Solve() function.
- EXACT:
This solution technique uses the iterative MVA (Mean Value Analysis) method
for up to three workload classes in versions of PDQ later than the original
release 1.0. Only consistent in the context of a closed queueing circuit.
Used as an argument for the PDQ_Solve function.
2.0.2 Global Variables
The following global variables are available to your PDQ models in C.
int nodes;
Cumulative counter for the number of nodes assigned in a PDQ model.
Use PDQ_GetNodesCount.
int streams;
Cumulative counter for the number of workload streams assigned in a PDQ model.
Use PDQ_GetStreamsCount.
int DEBUG;
Flag to toggle PDQ debug facility. Default is FALSE. Passed as an
argument to PDQ_SetDebug
char model[MAXCHARS];
Character array containing the model name (up to MAXCHARS chars).
Initialized by PDQ_Init
MAXBUF = 128
MAXCHARS = 64
double tolerance;
Controls the number of iterations used in the APPROXimate MVA
solution method.
char s1[], s2[], s3[], s4[];
Character array buffers (up to MAXBUF chars) for passing strings in C.
Initialized by PDQ_Init
PDQ functions in C have a PDQ_ prefix.
PDQ functions in Perl have a pdq:: prefix.
PDQ functions in Python have a pdq. prefix.
Function synopses are now cross-linked.
3 Function Synopses
3.1 CreateClosed
SYNOPSIS
int PDQ_CreateClosed(char *name, int TERM, float pop, float think);
pdq::CreateClosed($name, $pdq::TERM, $pop, $think);
pdq.CreateClosed(name, pdq.TERM, pop, think)
DESCRIPTION
Define the workload for a closed circuit queueing circuit. A separate call
is required for workload streams having different characteristics.
OPTIONS
name: The string used to identify the workload in PDQ Report or PDQ Debug logs
class: Either TERM, or BATCH type
pop: The number of active user processes in the closed circuit. This
argument is a float to accommodate measured activity e.g., 57.4 average
active users
think: The user delay or "thinktime" before a request re-enters the
queueing system}
RETURNS: the cumulative number of PDQ workload streams.
EXAMPLE in C
main() {
...
numStreams = PDQ_CreateClosed("DB_workers", TERM, 57.4, 31.6);
numStreams = PDQ_CreateClosed("fax_tasks", BATCH, 10.0);
...
}
EXAMPLE in Perl
use pdq;
...
$pdq::numStreams = pdq::CreateClosed("DB_workers", $pdq::TERM, 57.4, 31.6);
$pdq::numStreams = pdq::CreateClosed("fax_tasks", $pdq::BATCH, 10.0);
...
EXAMPLE in Python
import pdq
...
pdq.CreateClosed("DB_workers", pdq.TERM, 57.4, 31.6)
pdq.CreateClosed("fax_tasks", pdq.BATCH, 10.0)
...
SEE ALSO
CreateOpen 3.3, Init 3.13
3.2 CreateNode
SYNOPSIS
int PDQ_CreateNode(char *name, int device, int sched);
pdq::CreateNode($name, $pdq::CEN, $pdq::FCFS);
pdq.CreateNode(name, pdq.CEN, pdq.FCFS)
DESCRIPTION
Defines a queueing service node for either a closed or open circuit model.
A separate call is required for each queueing node.
name: A string used to identify the service node in reports or debug logs.
device: Type of device. Typically CEN.
sched: The queueing discipline. Most commonly FCFS.
RETURNS: the the cumulative number of queueing nodes.
EXAMPLE in C
main() {
int numNodes;
...
numNodes = PDQ_CreateNode("cpu", CEN, FCFS);
numNodes = PDQ_CreateNode("bus", CEN, FCFS);
numNodes = PDQ_CreateNode("disk", CEN, FCFS);
...
}
EXAMPLE in Perl
use pdq;
...
$numNodes = pdq::CreateNode("cpu", $pdq::CEN, $pdq::FCFS);
$numNodes = pdq::CreateNode("bus", $pdq::CEN, $pdq::FCFS);
$numNodes = pdq::CreateNode("disk", $pdq::CEN, $pdq::FCFS);
...
EXAMPLE in Python
import pdq
...
numNodes = pdq.CreateNode("cpu", pdq.CEN, pdq.FCFS)
numNodes = pdq.CreateNode("bus", pdq.CEN, pdq.FCFS)
numNodes = pdq.CreateNode("disk", pdq.CEN, pdq.FCFS)
...
SEE ALSO
CreateOpen 3.3, Init 3.13,
SetDemand 3.16
3.3 CreateOpen
SYNOPSIS
int PDQ_CreateOpen(char *name, float lambda);
pdq::CreateOpen($name, $lambda);
pdq.CreateOpen(name, lambda)
DESCRIPTION
Define an OPEN circuit workflow or transactional workload. A separate call
is required for workload streams having different characteristics.
name: A string used to identify the workload in reports or debug logs.
lambda: The arrival rate per unit time into the queueing circuit.
RETURNS: the cumulative number of PDQ streams.
EXAMPLE in C
main() {
int numStreams;
...
numStreams = PDQ_CreateOpen("IO_Cmds", 10.0);
...
}
EXAMPLE in Perl
use pdq;
...
$numStreams = pdq::CreateOpen("IO_Cmds", 10.0);
...
EXAMPLE in Python
import pdq
...
numStreams = pdq.CreateOpen("IO_Cmds", 10.0);
...
SEE ALSO
CreateNode 3.2,
CreateClosed 3.1, Init 3.13
3.4 GetLoadOpt
SYNOPSIS
double PDQ_GetLoadOpt(int class, char *wname);
pdq::GetLoadOpt($pdq::CLASS, $wname);
pdq.GetLoadOpt(pdq.CLASS, wname)
DESCRIPTION
PDQ_GetLoadOpt is used to determine the system throughput for the specified
workload.
class: TERM, or BATCH type.
wname: A string containing the name of the workload.
RETURNS: returns the optimal user load as a decimal number.
EXAMPLE
main() {
double nopt;
...
nopt = PDQ_GetLoadOpt(TRANS, "IO_Cmds");
printf("Nopt(%s): %3.4f\n", "IO_Cmds", tp);
...
}
SEE ALSO
GetThruput, GetResponse
3.5 GetNodesCount
SYNOPSIS
Not officially released.
int PDQ_GetNodesCount(char *model);
pdq::GetNodesCount($model);
pdq.GetNodesCount(model)
DESCRIPTION
Replaces the integer value returned by CreateNode in earlier versions of
PDQ. The current plan is to eliminate that return in PDQ 5.0.
GetNodesCount is called to determine the number nodes that have
been allocated in the model.
model: A string containing the name of the PDQ model.
RETURNS: returns the integer number of nodes.
EXAMPLE in C
main() {
int numStreams;
...
numStreams = PDQ_GetNodesCount("My PDQ Model");
...
}
EXAMPLE in Perl
use pdq;
...
numStreams = pdq::GetNodesCount("My PDQ Model");
...
EXAMPLE in Python
import pdq
...
numStreams = pdq.GetNodesCount("My PDQ Model")
...
SEE ALSO
CreateClosed 3.1, CreateOpen 3.3
3.6 GetQueueLength
SYNOPSIS
double PDQ_GetQueueLength(char *device, char *work, int class);
pdq::GetQueueLength($device, $work, $pdq::CLASS);
pdq.GetQueueLength(device, work, pdq.CLASS)
DESCRIPTION
GetQueueLength is used to determine the queue length (number of requests
waiting plus the number in service) of the designated service node by the
specified workload. It should only be called after the PDQ model has been
solved.
device: A string containing the name of the service node.
work: A string containing the name of the workload.
class: TRANS, TERM, or BATCH type.
RETURNS: returns the queue length as a decimal number.
EXAMPLE in C
main() {
double qlength;
...
PDQ_Solve(APPROX);
...
qlength = PDQ_GetQueueLength("disk", "IOs", TERM);
printf("Q(%s): %3.4f\n","IOs", qlength);
...
}
EXAMPLE in Perl
use pdq;
...
pdq::Solve($pdq::APPROX);
...
$qlength = pdq::GetQueueLength("disk", "IOs", $pdq::TERM);
printf("Q(%s): %3.4f\n","IOs", $qlength);
...
EXAMPLE in Python
import pdq
...
pdq.Solve(pdq.APPROX)
...
qlength = pdq.GetQueueLength("disk", "IOs", pdq.TERM)
print "Q(%s): %3.4f\n", % ("IOs", qlength)
...
}
SEE ALSO
GetResidenceTime, GetUtilization
3.7 GetResidenceTime
SYNOPSIS
double PDQ_GetResidenceTime(char *device, char *work, int class);
pdq::GetResidenceTime($device, $work, $pdq::CLASS);
pdq.GetResidenceTime(device, work, pdq.CLASS);
DESCRIPTION
PDQ_GetResidenceTime is used to determine the residence time at the
designated service node by the specified workload. It should only
be called after the PDQ model has been solved.
device: A string containing the name of the queueing service node.
work: A string containing the name of the workload.
class: TRANS, TERM, or BATCH type.
RETURNS: returns the residence time as a decimal number.
EXAMPLE in C
main() {
double reztime;
...
PDQ_Solve(APPROX);
...
reztime = PDQ_GetResidenceTime("disk", "IOs", TERM);
printf("R(%s): %3.4f\n","IOs", reztime);
}
EXAMPLE in Perl
use pdq;
...
pdq::Solve($pdq::APPROX);
...
$reztime = pdq::GetResidenceTime("disk", "IOs", $pdq::TERM);
printf("Q(%s): %3.4f\n","IOs", $reztime);
...
EXAMPLE in Python
import pdq
...
pdq.Solve(pdq.APPROX)
...
reztime = pdq.GetResidenceTime("disk", "IOs", pdq.TERM)
print "Q(%s): %3.4f\n", % ("IOs", reztime)
...
}
SEE ALSO
GetQueueLength 3.6, GetUtilization 3.12
3.8 GetResponse
SYNOPSIS
double PDQ_GetResponse(int class, char *wname);
pdq::GetResponse($pdq::CLASS, $wname);
pdq.GetResponse(pdq.CLASS, wname)
DESCRIPTIONs
PDQ_GetResponse used to determine the system response time for the
specified workload.
class: TRANS, TERM, or BATCH type.
wname: A string containing the name of the workload.
RETURNS: the system response time as a decimal number.
EXAMPLE in C
main() {
double resptime;
...
resptime = PDQ_GetResponse(TRANS, "IOs");
printf("R(%s): %3.4f\n","IOs", resptime);
...
}
EXAMPLE in Perl
use pdq;
...
$resptime = pdq::GetResponse($pdq::TRANS, "IOs");
printf("R(%s): %3.4f\n","IOs", $resptime);
...
}
EXAMPLE in Python
import pdq
...
resptime = pdq.GetResponse(pdq.TRANS, "IOs")
print "R(%s): %3.4f" % ("IOs", resptime)
...
}
SEE ALSO
CreateClosed 3.1, CreateOpen 3.3, Init 3.13
3.9 GetStreamsCount
SYNOPSIS
Not officially released.
int PDQ_GetStreamsCount(char *model);
pdq::GetStreamsCount($model);
pdq.GetStreamsCount(model)
DESCRIPTION
Replaces the integer value returned by CreateOpen and CreateClosed in earlier
versions of PDQ. The current plan is to eliminate that return in PDQ 5.0.
GetStreamsCount is called to determine the number distinct workflow streams (OPEN)
or workload streams (CLOSED) that have been allocated in the model.
model: A string containing the name of the PDQ model.
RETURNS: returns the integer number of streams.
EXAMPLE in C
main() {
int numStreams;
...
numStreams = PDQ_GetStreamsCount("My PDQ Model");
...
}
EXAMPLE in Perl
use pdq;
...
numStreams = pdq::GetStreamsCount("My PDQ Model");
...
EXAMPLE in Python
import pdq
...
numStreams = pdq.GetStreamsCount("My PDQ Model")
...
SEE ALSO
CreateClosed, CreateOpen
3.10 GetThruMax
NAME
PDQ_GetThruMax - determine the system throughput at saturation
SYNOPSIS
double PDQ_GetThruMax(int class, char *wname);
DESCRIPTION
PDQ_GetThruMax is used to determine the system throughput for the
specified workload.
class: TERM, or BATCH type.
wname: A string containing the name of the workload.
RETURNS: returns the saturation system throughput as a decimal number.
EXAMPLE
main() {
double xmax;
...
xmax = PDQ_GetThruMax(TRANS, "IO_Cmds");
printf("R(%s): %3.4f\n", "IO_Cmds", xmax);
...
}
SEE ALSO
PDQ_GetThruput
3.11 GetThruput
NAME
PDQ_GetThruput - determine the system throughput
SYNOPSIS
double PDQ_GetThruput(int class, char *wname);
DESCRIPTION
PDQ_GetThruput is used to determine the system throughput for the
specified workload.
class: TRANS, TERM, or BATCH type.
wname: A string containing the name of the workload.
RETURNS: returns the system throughput as a decimal number.
EXAMPLE
main() {
double tp;
...
tp = PDQ_GetThruput(TRANS, "IO_Cmds");
printf("R(%s): %3.4f\n", "IO_Cmds", tp);
...
}
SEE ALSO
PDQ_GetResponse
3.12 GetUtilization
SYNOPSIS
double PDQ_GetUtilization(char *device, char *work, int class);
DESCRIPTION
PDQ_GetUtilization is used to determine the utilization of the
designated service node by the specified workload. It should only
be called after the PDQ model has been solved.
device: A string containing the name of the queueing service node.
work: A string containing the name of the workload.
class: TRANS, TERM, or BATCH type.
RETURNS: returns the utilization as a decimal fraction in the range 0.0 to 1.0.
EXAMPLE in C
main() {
double ut;
...
PDQ_Solve(EXACT);
...
ut = PDQ_GetUtilization("disk", "IO_Cmds", TERM);
printf("R(%s): %3.4f\n","IO_Cmds", ut);
}
SEE ALSO
GetResponse~\ref{proc:GetResponse}, GetThruput, Solve~\ref{proc:Solve}
SYNOPSIS
void PDQ_Init(char *name);
pdq::Init($name);
pdq.Init(name)
DESCRIPTION
Initializes all internal PDQ variables. Must be called prior to any other
PDQ function. It also resets all internal PDQ variables so that no separate
cleanup function call required.
PDQ_Init can be called an arbitrary number of times from within the same
model.
name: A string containing the name of the performance model that will
appear in the PDQ report banner. To maintain cosmetic appearances, the
model name should not exceed 24 characters (including spaces).
RETURNS: None.
EXAMPLE in C
main() {
PDQ_Init("Application Model 1");
ns = PDQ_CreateOpen(...);
...
PDQ_Solve(CANON);
PDQ_Report();
...
PDQ_Init("Application Model 2");
ns = CreateClosed(...);
...
PDQ_Solve(EXACT);
PDQ_Report();
}
EXAMPLE in Perl
use pdq;
pdq::Init("Application Model 1");
ns = pdq::CreateOpen(...);
...
pdq::Solve($pdq::CANON);
pdq::Report();
...
pdq::Init("Application Model 2");
ns = pdq::CreateClosed(...);
...
pdq::Solve($pdq::EXACT);
pdq::Report();
EXAMPLE in Python
import pdq
pdq.Init("Application Model 1")
ns = pdq.CreateOpen(...)
...
pdq.Solve(pdq.CANON)
pdq.Report()
...
pdq.Init("Application Model 2")
ns = pdq.CreateClosed(...)
...
pdq.Solve(pdq.EXACT)
pdq.Report()
SEE ALSO
CreateOpen 3.3, CreateClosed 3.1,
Report 3.14, Solve 3.20
SYNOPSIS
void PDQ_Report();
pdq::Report();
pdq.Report()
DESCRIPTION
PDQ_Report generates a formatted report that includes the total
number of nodes and workloads created in the model, system level
performance measures such as throughput and response time for each
workload, and service node performance measures such as node
utilization and queue lengths. A comment field is available to
audit input parameter variations across multiple runs of the same
model.
RETURNS: None.
EXAMPLE in C
main() {
...
PDQ_Solve(EXACT);
PDQ_Report();
}
EXAMPLE in Perl
use pdq;
...
pdq::Solve($pdq::EXACT);
pdq::Report();
EXAMPLE in Python
import pdq
...
pdq.Solve(pdq.EXACT)
pdq.Report()
SEE ALSO
Init 3.13, Solve 3.20
3.15 SetDebug
NAME
PDQ_SetDebug - enable diagnostic printout
SYNOPSIS
void PDQ_SetDebug(int flag);
DESCRIPTION
Enables diagnostic printout of internal variables and procedures
used in solving a PDQ model.
flag: Set either TRUE or FALSE to toggle the debug facility.
RETURNS: None. Output is written to file e.g., debug.log.
EXAMPLE
main() {
...
PDQ_SetDebug(TRUE);
nodes = PDQ_CreateNode("server", CEN, FCFS);
streams = PDQ_CreateOpen("work", 0.5);
PDQ_SetDemand("server", "work", 1.0);
...
PDQ_SetDebug(FALSE);
}
Produces the following output.
DEBUG: PDQ_CreateNode
Entering
Node[0]: CEN FCFS "server"
Exiting
Stream[0]: TRANS "work"; Lambda: 0.5
DEBUG: PDQ_SetDemand()
Entering
DEBUG: getnode_index()
Entering
node:"server" index: 0
Exiting
...
SEE ALSO
Init 3.13
3.16 SetDemand
SYNOPSIS
void PDQ_SetDemand(char *nodename, char *workname, float servicetime);
pdq::SetDemand($nodename, $workname, $servicetime);
pdq.SetDemand(nodename, workname, servicetime)
DESCRIPTION
Define the service demand of a specific workload. The named node and
workload must have been defined previously. A separate call is required for
each workload stream that accesses the same node.
nodename: the string name of the queueing node.
workname: the string name of the workload.
servicetime: service demand (in units of time) required by the workload at
that node.
RETURNS: None.
EXAMPLE in C
main() {
...
numStreams = PDQ_CreateClosed("DB_Workers", TERM, 57.4, 31.6);
numStreams = PDQ_CreateClosed("Fax_Report", BATCH, 10.0);
numNodes = PDQ_CreateNode("cpu", CEN, FCFS);
PDQ_SetDemand("cpu", "DB_Workers", 0.130);
PDQ_SetDemand("cpu", "Fax_Report", 3.122);
...
}
EXAMPLE in Perl
use pdq;
...
$numStreams = pdq::CreateClosed("DB_Workers", $pdq::TERM, 57.4, 31.6);
$numStreams = pdq::CreateClosed("Fax_Report", $pdq::BATCH, 10.0);
$numNodes = pdq::CreateNode("cpu", $pdq::CEN, $pdq::FCFS);
pdq::SetDemand("cpu", "DB_Workers", 0.130);
pdq::SetDemand("cpu", "Fax_Report", 3.122);
...
EXAMPLE in Python
import pdq
...
numStreams = pdq.CreateClosed("DB_Workers", pdq.TERM, 57.4, 31.6);
numStreams = pdq.CreateClosed("Fax_Report", pdq.BATCH, 10.0);
numNodes = pdq.CreateNode("cpu", pdq.CEN, pdq.FCFS);
pdq.SetDemand("cpu", "DB_Workers", 0.130);
pdq.SetDemand("cpu", "Fax_Report", 3.122);
...
SEE ALSO
CreateClosed 3.1, CreateNode 3.2,
CreateOpen 3.3, SetVisits 3.18,
Solve 3.20
3.17 SetTUnit
SYNOPSIS
void PDQ_SetTUnit(char *unitname);
pdq::SetTUnit($unitname);
pdq.SetTUnit(unitname)
DESCRIPTION
Change the name of the time unit that appears in the PDQ
report. The default time unit is Seconds.
Cannot be invoked prior to calling CreateOpen or CreateClosed.
unitname: a string name of the unit
RETURNS: None.
EXAMPLE in C
main() {
...
PDQ_SetTUnit("Minutes");
...
}
EXAMPLE in Perl
use pdq;
...
pdq::SetTUnit("Minutes");
...
EXAMPLE in Python
import pdq
...
pdq.SetTUnit("Minutes")
...
SEE ALSO
Report 3.14, SetWUnit 3.19
3.18 SetVisits
SYNOPSIS
void PDQ_SetVisits(char *nodename, char *workname, float visits, float stime);
pdq::SetVisits($nodename, $workname, $visits, $stime);
pdq.SetVisits(nodename, workname, visits, stime)
DESCRIPTION
Used to define the service demand of a specific workload in terms of the
explicit service time and visit count. The named node and workload must
exist. A separate call is required for each workload stream that accesses
the same node. SetVisits is different from SetDemand in the way
node-level performance metrics are formatted in the Report output. The
number of visits shows up in the Report INPUTS section. The throughput
in the RESOURCE Performance section shows up as counts per unit time.
nodename: name of the queueing node.
workname: name of the workload.
visits: number of visits to that node.
stime: service time the workload requires at that node (in timebase units).
RETURNS: None.
EXAMPLE in C
main() {
...
numStreams = PDQ_CreateClosed("DB_Workers", TERM, 57.4, 31.6);
numNodes = PDQ_CreateNode("cpu", CEN, FCFS);
PDQ_SetVisits("cpu", "DB_Workers", 10.0, 0.013);
...
}
EXAMPLE in Perl
use pdq;
...
$numStreams = pdq::CreateClosed("DB_Workers", $pdq::TERM, 57.4, 31.6);
$numNodes = pdq::CreateNode("cpu", $pdq::CEN, $pdq::FCFS);
pdq::SetVisits("cpu", "DB_Workers", 10.0, 0.013);
...
EXAMPLE in Python
import pdq
...
numStreams = pdq.CreateClosed("DB_Workers", pdq.TERM, 57.4, 31.6)
numNodes = pdq.CreateNode("cpu", pdq.CEN, pdq.FCFS)
pdq.SetVisits("cpu", "DB_Workers", 10.0, 0.013)
...
SEE ALSO
CreateClosed 3.1,
CreateNode 3.2,
CreateOpen 3.3,
SetDemand 3.16
3.19 SetWUnit
SYNOPSIS
void PDQ_SetWUnit(char *unitname);
pdq::SetWUnit($unitname);
pdq.SetWUnit(unitname)
DESCRIPTION
PDQ_SetWUnit changes the name of the work unit that appears in the PDQ
report. The default work unit is Job.
Cannot be invoked prior to calling CreateOpen or CreateClosed.
unitname: The name of the work unit.
RETURNS: None.
EXAMPLE in C
main() {
...
PDQ_SetWUnit("I/O_Reqs");
...
}
EXAMPLE in Perl
use pdq;
...
pdq::SetWUnit("I/O_Reqs");
...
EXAMPLE in Python
import pdq
...
pdq.SetWUnit("I/O_Reqs")
...
SEE ALSO
Report 3.14, SetTUnit 3.17
SYNOPSIS
int PDQ_Solve(int method);
pdq::Solve($method);
pdq.Solve(method)
DESCRIPTION
PDQ_Solve is called after the PDQ model has been created. An appropriate
solution method must be passed as an argument or an error will reported at
runtime.
method: APPROX or CANON.
Note: The EXACT method is highly complex and has not been included in this
version of the PDQ library. See Chapter 3 for more details.
RETURNS: None.
EXAMPLE in C
main() {
...
PDQ_Solve(APPROX);
PDQ_Report();
}
EXAMPLE in Perl
use pdq;
...
pdq::Solve($pdq::APPROX);
pdq::Report();
}
EXAMPLE in Python
import pdq
...
pdq.Solve(pdq.APPROX)
pdq.Report()
}
SEE ALSO
Report 3.14
4 Simple Example
The following annotated C source code is intended to provide a
simple boiler-plate for creating a PDQ performance model. The
entire model is defined, and solved within the main function. In
more sophisticated models, it may be preferable to write separate
functions to handle different aspects of the model.
4.1 Creating the Model
It is a good idea to start the PDQ model off with a unique filename and
a description of its purpose. All this goes in a C comment field
commencing with /* and ending with */.
/* min_mod.c
Illustrate use of PDQ using a single open workload server.
*/
The filename for our model is min_mod.c.
It is good practice to include the following header files in any
PDQ model source code.
#include <stdio.h>
#include <math.h>
#include "PDQ_Lib.h"
main() {
It is necessary to declare the PDQ global variables: nodes and
streams. They enable other internal PDQ procedures to keep track
of the global state of the queueing circuit created in PDQ.
extern int nodes, streams;
Variables that are specific to the model are declared next. In
this case, we declare the interarrival time and the service time
for the workload.
float inter_arriv_time = 0.5;
float service_time = 1.0;
The model is initialized by a call to
PDQ_Init
with the name of the model that will appear in the PDQ report.
The actual PDQ model name that will appear in the PDQ report is
Minimal Model and need not be the same as the PDQ filename,
although it is a good idea to keep them as similar as possible.
We also assign time and work units that are different from the
defaults.
PDQ_Init("Minimal Model");
PDQ_SetWUnits("Compltns");
PDQ_SetTUnits("Time");
The next step is define the queueing centers or nodes that
comprise the queueing circuit and the workload that will place
service demands on those nodes.
nodes = PDQ_CreateNode("server", CEN, FCFS);
There is only one node and its name is server. We create an open
circuit model by calling
PDQ_CreateOpen
and passing the workload name work and its interarrival time.
This model is an open circuit type.
streams = PDQ_CreateOpen("work", inter_arriv_time);
The service demand placed in the node server by the workload work
is defined in the call to
PDQ_SetDemand.
PDQ_SetDemand("server", "work", service_time);
At this point, the PDQ model is now defined and created in memory.
All that remains is to solve it and examine the performance data
predicted by the PDQ model. Since this is an open circuit model,
the appropriate solution technique is the canonical method.
PDQ_Solve(CANON);
PDQ_Report();
} /* main */
The report generated by the call to
PDQ_Report
is annotated in the next section. The C source code for more
elaborate PDQ models appear throughout the book.
4.2 PDQ Report
This is an annotated version of the report generated for the
Minimal Model described in the previous section. The banner
includes the PDQ model name, Minimal Model, that was passed as an
argument to
PDQ_Init.
The banner is followed by the comment field (if any has been
supplied).
***************************************
****** Pretty Damn Quick REPORT *******
***************************************
*** of : Tue Apr 2 19:56:07 1996 ***
*** for: Minimal Model ***
*** Ver: PDQ Analyzer v1.0 ***
***************************************
***************************************
****** Comments *******
I couldn't think of anything to say.
The following section of the report headed: PDQ Model INPUTS,
summarizes the number and types of nodes and workloads. In this
case there is only one node and one workload in an open circuit
model. The service demand placed on the node by the workload is 1
Second.
***************************************
****** PDQ Model INPUTS *******
***************************************
Node Sched Resource Workload Class Demand
---- ----- -------- -------- ----- ------
CEN FCFS server work TRANS 1.0000
Queueing Circuit Totals:
Generators: 0.00
Streams : 1
Nodes : 1
There are no generators because this an open queueing circuit, not a
closed circuit with a finite population generating work. Next, the
workload and their demands are summarized.
WORKLOAD Parameters
Arrivals per Sec Demand
-------- ------- ------
work 0.5000 1.0000
The next section , entitled: PDQ Model OUTPUTS, summarizes both system
level and node level performance measures.
***************************************
****** PDQ Model OUTPUTS *******
***************************************
Solution Method: CANON
The PDQ report reminds us which of the solution techniques was used in
solving the model. In this case, it's the canonical solution.
Each workload component is identified and the system level performance
measures are reported that workload component. In the case of our
Minimal Model, there is only one component which was named "work." There
is no bounds analysis performed since this is an open circuit model.
****** SYSTEM Performance *******
Metric Value Unit
----------------- ----- ----
Workload: "work"
Mean Throughput 0.5000 Compltns/Time
Response Time 2.0000 Time
Bounds Analysis:
Max Throughput 1.0000 Compltns/Time
Finally, node level attribites such as, node utilization and queue
length at the node are reported. In the case of our Minimal Model,
there is only one node which was named ßerver" and it has service
demands placed on it by the single workload component called
work.
****** RESOURCE Performance *******
Metric Resource Work Value Unit
--------- -------- ---- ----- ----
Throughput server work 0.5000 Compltns/Time
Utilization server work 50.0000 Percent
Queue Length server work 1.0000 Compltns
Residence Time server work 2.0000 Time
This is the end of the PDQ Report.
5 Summary
All the examples used throughout the book, are constructed using the PDQ
library described in this manual. After reading how to create the
Minimal Model discussed in this manual, you should turn some of the
examples in the text, especially those in Chapters 2 and 3, to get a
better idea of how to apply PDQ to the creation and solution of queueing
performance model.
File translated from
TEX
by
TTH,
version 3.38.
On 18 Apr 2007, 11:20.