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.

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

2.0.3  Functions

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}


3.13  Init

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

3.14  Report

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

3.20  Solve

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.