Tuning Perforce for Performance
Your Perforce server should normally be a light consumer of system resources. As your installation grows, however, you might want to revisit your system configuration to ensure that it is configured for optimal performance.
This chapter briefly outlines some of the factors that can affect the performance of a Perforce server, provides a few tips on diagnosing network-related difficulties, and offers some suggestions on decreasing server load for larger installations:
- It describes the variables that affect performance: operating system, disk subsystem, file system, CPU, memory, network connectivity settings, journal and archive location, use patterns, the use of read-only clients, and parallel processing for submits and syncs.
- It explains how you can improve performance with lockless reads.
- It explains how you can diagnose slow response times.
- It describes the factors that create server swamp.
- It explains how you can improve performance by rebalancing B-trees.
Tuning for performance
In general, Perforce performs well on any server-class hardware platform. The following variables can affect the performance of your Perforce server.
Operating systems
32-bit operating systems might not be able to address large amounts of physical memory, which can restrict the effective size of the filesystem cache. The various 64-bit operating systems each have their own performance characteristics that can favor a particular Perforce workload. In general, Linux distributions using later Linux 2.6 64-bit kernels have good performance characteristics for most Perforce workloads.
Disk subsystem
For I/O requests that must be satisfied from beyond the filesystem cache,
there might be several improvements possible for the I/O subsystem. The
storage subsystem containing the db.*
files should have a memory
cache; maximizing the storage subsystem’s memory cache is also a good
recommendation. For best performance, write-back caching should be enabled,
which of course requires that the storage subsystem’s memory have battery
backup power. I/O latency to the logical drive where the db.\*
files
are located should be minimized, including the rotational latency of the
physical drives themselves. Minimizing I/O latency might require direct
connections between the host and the storage subsystem, and usually
requires physical drives with the fastest rotational speed (such as 15K
RPM).
RAID 1+0 (or RAID 10) is usually the better performing RAID configuration,
and is recommended for the logical drive where the db.*
files are
located. The number of physical drives in the logical drive can also have
an affect on *p4d
* performance. Generally, performance improves as the
number of physical drives in the logical drive increases. For a given
amount of disk space required, better performance might result from using
more smaller-capacity physical drives. The stripe size for the logical
drive can also affect performance; the optimal stripe size might be
dependent upon the number of physical drives in the logical drive.
Hardware-based RAID implementations (that is, RAID logic that is not
implemented as software running on the host) usually have good performance
characteristics. Software-based RAID implementations can require CPU cycles
that might otherwise be needed for p4d
processes. Therefore,
software-based RAID implementations should be avoided.
File systems
Filesystem performance is an important component of operating system
performance. The various operating systems usually offer several
filesystems, each with their own performance characteristics that can favor
a particular Perforce workload. For best p4d
performance, the db.*
files should be located on a high-performance filesystem. In general, the
XFS filesystem has good performance characteristics for most Perforce
workloads. The XFS filesystem is available on several operating systems,
including Linux distributions using later Linux 2.6 64-bit kernels.
Reading pages into a cache in anticipation of being requested is an optimization that is often implemented within various I/O subsystem components. This optimization is commonly known as "read-ahead". In some implementations, read-ahead can be tuned, which might result in better performance. But tuning read-ahead can be a bit of an art. For example, increasing the read-ahead size might result in better performance for operations requiring mostly sequential reads. But the same increased read-ahead size applied consistently during random reads might unnecessarily discard previously-cached data that might have satisfied subsequent requests.
CPU
CPU resource consumption can be adversely affected by compression, lockless reads, or a badly designed protections table. In general, there is a trade-off between speed and the number of cores. A minimum of 2.4 GHZ and 8 cores is recommended. With greater speed, fewer cores will do: for example, a 3.2 GHZ and 4-core processor will also work.
Faster processors and memory in the machine where p4d
executes
might result in faster execution of p4d
commands. Since portions of
some commands acquire and hold resources that might block other commands,
it is important that these portions of the commands execute as fast as
possible. For example, most p4d
commands have a compute phase, during
which shared locks are acquired and held on some of the db.*
files. A
shared lock on a db.\*
file blocks an operation that writes to the same
db.\*
file. If the data needed for a command’s compute phase is cached
within the operating system’s filesystem cache, only the processor and
memory speed constrains the compute phase.
If you are using lockless reads, CPU speed is not as critical, but can still be helpful for good performance. Since some readers will no longer block a writer (and a writer will no longer block some readers), speeding commands through the server might not be as critical from a concurrency point of view. And since more commands might now run concurrently through the Perforce Server, more CPU cores might be better utilized.
The complexity of the site’s protections table and of client views can
affect CPU requirements. You can monitor CPU utilization using OS utilities
such as top
(on Linux and Unix) and perfmon
(on Windows).
Installations with high CPU utilization on the machine where p4d
executes that are already using faster processors might need more
processors and/or processors with more cores while maintaining the speed of
the processors.
Note
If you are using SSL to secure client-server connections, choose a CPU that supports the AES instruction set. Perforce normally uses AES-256 to encrypt its SSL connections, so using a CPU that supports AES will minimize the encryption overhead: in most CPUs, it will eliminate the performance penalty.
Some processors and operating systems support dynamic frequency scaling,
which allows the processor to vary power consumption by dynamically
adjusting the processor voltage and core frequency. As more demand is
placed on the processor, the voltage and core frequency increase. Until the
processor is ramped up to full speed, p4d
performance might be
impacted. Although the power-saving capability of the dynamic frequency
scaling feature is useful for mobile computers, it is not recommended for
the machine where p4d
executes.
Examples of dynamic frequency scaling include the following:
- Intel SpeedStep - available on some Xeon processors and generally available on mobile computers
- AMD PowerNow! - available on an array of AMD processors, including server-level processors
Both features are supported on Linux (and enabled by default in some SuSE
distributions), Windows, and Mac OS X platforms. If this feature is enabled
on the machine where p4d
executes, we recommend disabling it. In some
Linux distributions, such as SuSE, this feature can be disabled by setting
the "powersaved" service to "off".
You might be able to determine the current speed of the processors on
your computer. On Linux, the current speed of each core is reported on
the "cpu MHz" line in the output from the cat /proc/cpuinfo
OS
command.
Memory
Server performance is highly dependent upon having sufficient memory.
Two bottlenecks are relevant. The first bottleneck can be avoided by
ensuring that the server doesn’t page when it runs large queries, and
the second by ensuring that the db.rev
table (or at least as much of
it as practical) can be cached in main memory:
- Determining memory requirements for large queries is fairly straightforward: the server requires about 1 kilobyte of RAM per file to avoid paging; 10,000 files will require 10 MB of RAM.
- To cache
db.rev
, the size of thedb.rev
file in an existing installation can be observed and used as an estimate. New installations of Perforce can expectdb.rev
to require about 150-200 bytes per revision, and roughly three revisions per file, or about 0.5 kilobytes of RAM per file. - I/O requests that can be satisfied from a larger filesystem cache complete faster than requests that must be satisfied from beyond the filesystem cache.
Thus, if there is 1.5 kilobytes of RAM available per file, or 150 MB for 100,000 files, the server does not page, even when performing operations involving all files. It is still possible that multiple large operations can be performed simultaneously and thus require more memory to avoid paging. On the other hand, the vast majority of operations involve only a small subset of files.
One way to determine if you have allocated sufficient memory is to look at the physical read rate on the device that contains only the database files. This read rate should be trivial.
Network
Perforce can run over any TCP/IP network. For remote users or distributed configurations, Perforce offers options like proxies and the commit/edge architecture that can enhance performance over a WAN. Compression in the network layer can also help.
Perforce uses a TCP/IP connection for each client interaction with the
server. The server’s port address is defined by P4PORT
, but the TCP/IP
implementation picks a client port number. After the command completes
and the connection is closed, the port is left in TIME_WAIT
state for
two minutes. Although the port number ranges from 1025
to 32767
,
generally only a few hundred or thousand can be in use simultaneously.
It is therefore possible to occupy all available ports by invoking a
Perforce command many times in rapid succession, such as with a script.
By default, idle connections are not kept alive. If your network
silently drops idle connections, this behavior may cause unexpected
connectivity issues. (Consider a p4 pull
thread that transfers
data between a master server and a replica at a remote site; depending
on each site’s respective business hours and user workloads, such a
connection could be idle for several hours per day.) Four configurables
are available to manage the state of idle connections.
Configurable | Default Value | Meaning |
---|---|---|
|
|
If non-zero, disable the sending of TCP keepalive packets. |
|
|
Idle time (in seconds) before starting to send keepalives. |
|
|
Interval (in seconds) between sending keepalive packets. |
|
|
Number of unacknowledged keepalives before failure. |
If your network configuration requires keepalive packets, consider
setting net.keepalive.idle
to a suitably long value, for example 3,600
seconds (1 hour), and an interval measured in tens of minutes.
Journal and archive location
For recoverability, the live journal should not be on the same physical
device that contains the db.*
files. Separating the live journal and
the db.\*
files also improves performance. During operations that write
to the db.*
files, entries are written to the live journal as records
are written to the db.\*
files. If the live journal and the db.*
files are on the same physical device, the I/O throughput to the db.\*
files is degraded. For best performance, the live journal should be on a
separate storage subsystem connected to a separate host adapter. The live
journal should be on a logical drive and filesystem that is optimized for
sequential writes.
The versioned files should be located on a separate logical drive than the
logical drives where the db.*
files and the live journal are located.
For best performance, the logical drive where the versioned files are
located should be on a separate storage subsystem connected to a separate
host adapter. Since the versioned files typically require significantly
more disk space and the I/O throughput is not as critical as for the
db.\*
files, a more economical RAID configuration, such as RAID 5, can
be used for the logical drive where the versioned files are located.
Use patterns
Perforce usage can affect performance. There are several usage patterns
that can have a direct effect on performance. Since the depot filenames are
the leading portion of the key in several important db.\*
files
(db.rev
, db.revhx
, and db.integed
are among the more notable), the
length of paths in the depot filenames have a direct effect on performance.
As the length of paths increase, performance decreases. It is therefore
prudent to discourage the use of overly-descriptive paths in the depot
filenames.
The development methodology can also have a direct effect on
performance. If the development methodology calls for frequent creation
of full branches (perhaps branching for each bug fix), then the amount
of metadata rapidly increases, resulting in more levels within the db.*
file B-trees. As the number of levels increase, more key comparisons and
I/O requests are required to traverse to the leaf pages, which will
impact performance. Creating full branches also requires more metadata
read and written; the additional metadata read and written might affect
the filesystem cache to the detriment of other Perforce tasks. Rather
than frequent creation of full branches, it might be prudent to branch
only those files needed for each bug fix, or consider a development
methodology in which multiple bug fixes can occur on the same branch.
Using read-only clients in automated builds
Build automation scripts, which routinely create, sync, and tear down
clients, may fragment the db.have
table over time. To avoid this, you
can specify the type readonly
for these clients. Such clients cannot
add, delete, edit, integrate, or submit files, but this should not be an
issue in build scripts.
A readonly client is assigned its own personal db.have
database table,
and the location of this table is specified using the
client.readonly.dir
configurable.
To set up a read-only client:
-
Set the
client.readonly.dir
configurable to the directory where thedb.*
tables for the client should be stored.For example, if you create a read-only client whose name is
myroc
and you setclient.readonly.dir
to/perforce/1
, then syncing files using this client will write to the following database/perforce/1/server.dbs/client/
hashdir
/db.myroc - Set the
Type
field of the client spec toreadonly
.
Using parallel processing for submits and syncs
You can configure the server to transfer files in parallel for submit and sync processing. Parallel processing is most effective with long-haul, high latency networks or with other network configuration that prevents the use of available bandwidth with a single TCP flow. Parallel processing might also be appropriate when working with large compressed binary files, where the client must perform substantial work to decompress the file.
- Use the
net.parallel.max
configurable to transfer files in parallel during the submit process. For this feature to work, you must have both server and client upgraded to version 2015.1. - Use the
net.parallel.max
configurable to speed up sync processing by having thep4 sync
command transfer files using multiple threads. You do this by setting thenet.parallel.max
configuration variable to a value greater than one and by using the--parallel
option to thep4 sync
command.
For more information see the p4 submit
command and the p4
sync
command in P4 Command Reference.
Improving concurrency with lockless reads
Prior to Release 2013.3, commands that only read data from the database take a read-lock on one (or more) database tables. Although other commands can read from the tables at the same time, any commands attempting to write to the read-locked tables are forced to wait for the read-lock to complete before writing could begin. Currently, the default behavior is to allow some commands to perform lock-free reads (or "peeks") on these tables, without sacrificing consistency or isolation. This provides significant performance improvement by ensuring that write operations on these tables can run immediately, rather than being held until the read-lock is released.
Note
Lockless reads require that server locks be enabled. Because this can
cause issues for long duration syncs, the default value for controlling
the 'sync' server lock (server.locks.sync
) is currently disabled by
default.
maxlocktime
has been changed when peeking is enabled. To revert to the old
behavior, set the dbpeeking.usemaxlock
configurable to 1
.
To change the setting of lockless reads on your Perforce Server, use the
p4 configure set db.peeking=N
command.
Any change to db.peeking
requires a server restart to take effect.
Possible values for db.peeking
are as follows:
db.peeking |
Meaning |
---|---|
|
If This corresponds to the behavior of Perforce at release 2013.2 and below. |
|
If This configuration is intended primarily for diagnostic purposes. |
|
If This configuration is expected to provide the best performance results for most sites. It is the default value. |
|
If This configuration involves a trade-off between concurrency and command
completion speed; in general, if a repository has many revisions per
file, then some commands will complete more slowly with |
Commands implementing lockless reads
When peeking is enabled, the following commands run lockless:
Command | Notes |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Applies to |
|
|
|
when |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Applies to |
|
when |
|
Applies to |
|
|
|
|
|
The following commands run partially lockless; in most cases these commands will operate lock-free, but lockless operation is not guaranteed:
Command | Notes |
---|---|
|
|
|
|
|
when |
|
in the context of |
|
in the context of |
|
|
|
when |
Overriding the default locking behavior
You can override the db.peeking
setting on a per-command basis by
using the -Zpeeking=
flag followed by your preferred value. For
example, to disable peeking for one command, run the following command:
$ p4 -Zpeeking=1 fstat
and compare the results with:
$ p4 -Zpeeking=2 fstat
Observing the effect of lockless reads
To determine whether read locks are impacting performance (and the
extent to which enabling lockless reads has improved performance), you
can examine the server logs, or you can use the -Ztrack
flag to
output, for any given command, the lines that would be written to the
P4LOG
. For example:
$ p4 -Zpeeking=1 -Ztrack sync
produces output for 11 database tables. The relevant lines here are
those that refer to "locks read/write
".
... --- db.counters --- pages in+out+cached 3+0+2 --- locks read/write 1/0 rows get+pos+scan put+del 1+0+0 0+0 --- db.user --- pages in+out+cached 3+0+2 --- locks read/write 1/0 rows get+pos+scan put+del 1+0+0 0+0 ...
The 1
appearing in ("locks read/write 1/0
") every table’s
locking results shows one read lock taken per table. By contrast, the
diagnostic output from:
$ p4 -Zpeeking=2 -Ztrack sync
... --- db.counters --- pages in+out+cached 3+0+2 --- locks read/write 0/0 rows get+pos+scan put+del 1+0+0 0+0 ...
shows that the sync operation completed without any read or write locks
required on db.counters
(if you try it yourself, on many other
tables); when peeking is enabled, many commands will show read/write
0/0
locks (or at least, fewer locks) taken.
Side-track servers must have the same db.peeking level
A single Perforce instance can detect and ignore inadvertent attempts to
override db.peeking
that would change table locking order and risk
deadlock. (For example, if you attempt to use db.peeking=3
on a server
for which peeking is disabled by having db.peeking
set to 0
(or
unset), the service ignores the attempt altogether and the command
proceeds with the old behavior.
In the case of "side-track servers" described in the following Knowledge Base article:
http://answers.perforce.com/articles/KB_Article/Setting-Up-a-Side-track-Server
this protection is not available.
Warning
All side-track servers must have the same db.peeking
setting as the
main server. Server deadlock may result.
Diagnosing slow response times
Perforce is normally a light user of network resources. Although it is
possible that an extremely large user operation could cause the Perforce
server to respond slowly, consistently slow responses to p4
commands are usually caused by network problems. Any of the following
can cause slow response times:
- Misconfigured domain name system (DNS)
- Misconfigured Windows networking
- Difficulty accessing the
p4
executable on a networked file system
A good initial test is to run p4 info
. If this does not respond
immediately, then there is a network problem. Although solving network
problems is beyond the scope of this manual, here are some suggestions
for troubleshooting them.
Hostname vs. IP address
Try setting P4PORT
to the service’s IP address instead of its
hostname. For example, instead of using:
P4PORT=host.domain:1666
try using:
P4PORT=1.2.3.4:1666
with your site-specific IP address and port number.
On most systems, you can determine the IP address of a host by invoking:
$ ping hostname
If p4 info
responds immediately when you use the IP address, but
not when you use the hostname, the problem is likely related to DNS.
Windows wildcards
In some cases, p4
commands on Windows can result in a delayed
response if they use unquoted file patterns with a combination of depot
syntax and wildcards, such as:
$ p4 files //depot/*
You can prevent the delay by putting double quotes around the file pattern, like this:
$ p4 files "//depot/*"
The cause of the problem is the p4
command’s use of a Windows
function to expand wildcards. When quotes are not used, the function
interprets //depot
as a networked computer path and spends time in a
futile search for a machine named depot
.
DNS lookups and the hosts file
On Windows, the %SystemRoot%\system32\drivers\etc\hosts
file can be
used to hardcode IP address-hostname pairs. You might be able to work
around DNS problems by adding entries to this file. The corresponding
UNIX file is /etc/hosts
.
Location of the p4 executable
If none of the above diagnostic steps explains the sluggish response
time, it’s possible that the p4
executable itself is on a
networked file system that is performing very poorly. To check this, try
running:
$ p4 -V
This merely prints out the version information, without attempting any
network access. If you get a slow response, network access to the
p4
executable itself might be the problem. Copying or
downloading a copy of p4
onto a local filesystem should improve
response times.
Working over unreliable networks
To set a hard upper bound on how long a connection is willing to wait on
any single network read or write, set the net.maxwait
configurable to
the number of seconds to wait before disconnecting with a network error.
Users working over unreliable connections can set net.maxwait
value
either in their P4CONFIG
files, or use
-vnet.maxwait=
on a
per-command basis, where t
t
is the number of seconds to wait before
timing out.
Note
Although net.maxwait
can be set on the Perforce server, it is
generally inadvisable to do so. For example, if net.maxwait
is set to
60
on the server, users of the Command-Line Client must complete every
interactive form within one minute before the command times out. If,
however, individual users set net.maxwait
in their own P4CONFIG
files (which reside on their own workstations) their connections are not
subject to this limitation; commands only fail if the versioning service
takes more than 60 seconds to respond to their requests.
It is useful to combine net.maxwait
with the -rN
global option,
where N
is the number of times to attempt reconnection in the event
that the network times out. For example:
$ p4 -r3 -vnet.maxwait=60 sync
attempts to sync the user’s workspace, making up to three attempts to resume the sync if interrupted. The command fails after the third 60-second timeout.
Because the format of the output of a command that times out and is
restarted cannot be guaranteed (for example, if network connectivity is
broken in the middle of a line of output), avoid the use of -r
on any
command that reads from standard input. For example, the behavior of the
following command, which reads a list of files from stdin and passes it
to p4 add
, can result in the attempted addition of "half a
filename" to the depot.
$ find . -print | p4 -x - -r3 add
To prevent this from happening (for example, if adding a large number of files over a very unreliable connection), consider an approach like the following:
$ find directoryname
-type f -exec p4 -r5 -vmax.netwait=60 add {} \;
All files (-type f
) in directoryname
are found, and added one at a
time, by invoking the command "p4 -r5 -vmax.netwait=60 add
"
for each file individually.
After all files have been added, assign the changelist a changelist
number with p4 change
, and submit the numbered atomically with:
$ p4 -r5 -vmax.netwait=60 submit -c changenum
If connectivity is interrupted, the numbered changelist submission is resumed.
Preventing server swamp
Generally, Perforce’s performance depends on the number of files a user tries to manipulate in a single command invocation, not on the size of the depot. That is, syncing a client view of 30 files from a 3,000,000-file depot should not be much slower than syncing a client view of 30 files from a 30-file depot.
The number of files affected by a single command is largely determined by the following factors:
-
p4
command-line arguments (or selected folders in the case of GUI operations)Without arguments, most commands operate on, or at least refer to, all files in the client workspace view.
-
Client views, branch views, label views, and protections
Because commands without arguments operate on all files in the workspace view, it follows that the use of unrestricted views and unlimited protections can result in commands operating on all files in the depot.
When the server answers a request, it locks down the database for the duration of the computation phase. For normal operations, this is a successful strategy, because the server can "get in and out" quickly enough to avoid a backlog of requests. Abnormally large requests, however, can take seconds, sometimes even minutes. If frustrated users press CTRL+C and retry, the problem gets even worse; the server consumes more memory and responds even more slowly.
Warning
The p4 obliterate command scans the entire database once per file argument and locks the entire database while scanning. It is best to do this during off hours for large sites.
At sites with very large depots, unrestricted views and unqualified commands make a Perforce server work much harder than it needs to. Users and administrators can ease load on their servers by the following:
- Using "tight" views
- Assigning protections
- Limiting
maxresults
- Limiting simultaneous connections with
server.maxcommands
- Unloading infrequently-used metadata
- Writing efficient scripts
- Using compression efficiently
- Other server configurables
The following sections examine each of these solutions:
Using tight views
The following "loose" view is trivial to set up but could invite trouble on a very large depot:
//depot/... //workspace/...
In the loose view, the entire depot was mapped into the client workspace; for most users, this can be "tightened" considerably. The following view, for example, is restricted to specific areas of the depot:
//depot/main/srv/devA/... //workspace/main/srv/devA/... //depot/main/drv/lport/... //workspace/main/dvr/lport/... //depot/rel2.0/srv/devA/bin/... //workspace/rel2.0/srv/devA/bin/... //depot/qa/s6test/dvr/... //workspace/qa/s6test/dvr/...
Client views, in particular, but also branch views and label views, should also be set up to give users just enough scope to do the work they need to do.
Client, branch, and label views are set by a Perforce administrator or
by individual users with the p4 client
, p4 branch
, and
p4 label
commands, respectively.
Two of the techniques for script optimization (described in Using branch views and Using a temporary client workspace) rely on similar techniques. By limiting the size of the view available to a command, fewer commands need to be run, and when run, the commands require fewer resources.
Assigning protections
Protections (see Authorizing access) are actually another type of
Perforce view. Protections are set with the p4 protect
command
and control which depot files can be affected by commands run by users.
Unlike client, branch, and label views, however, the views used by protections can be set only by Perforce superusers. (Protections also control read and write permission to depot files, but the permission levels themselves have no impact on server performance.) By assigning protections in Perforce, a Perforce superuser can effectively limit the size of a user’s view, even if the user is using "loose" client specifications.
Protections can be assigned to either users or groups. For example:
write user sam * //depot/admin/... write group rocketdev * //depot/rocket/main/... write group rocketrel2 * //depot/rocket/rel2.0/...
Perforce groups are created by superusers with the p4 group
command. Not only do they make it easier to assign protections, they
also provide useful fail-safe mechanisms in the form of maxresults
and
maxscanrows
, described in the next section.
Limiting database queries
Each Perforce group has an associated maxresults, maxscanrows, and
maxlocktime value. The default for each is unset
, but a superuser
can use p4 group
to limit it for any given group.
MaxResults
prevents the server from using excessive memory by limiting
the amount of data buffered during command execution. Users in limited
groups are unable to run any commands that buffer more database rows
than the group’s MaxResults
limit. (For most sites, MaxResults
should be larger than the largest number of files anticipated in any one
user’s individual client workspace.)
Like MaxResults
, MaxScanRows
prevents certain user commands from
placing excessive demands on the server. (Typically, the number of rows
scanned in a single operation is roughly equal to MaxResults
multiplied by the average number of revisions per file in the depot.)
Finally, MaxLockTime
is used to prevent certain commands from locking
the database for prolonged periods of time. Set MaxLockTime
to the
number of milliseconds for the longest permissible database lock.
To set these limits, fill in the appropriate fields in the p4
group
form. If a user is listed in multiple groups, the highest of
the MaxResults
(or MaxScanRows
, or MaxLockTime
) limits (including
unlimited
, but not including the default unset
setting) for those
groups is taken as the user’s MaxResults
(or MaxScanRows
, or
MaxLockTime
) value.
Example 11. Effect of setting maxresults, maxscanrows, and maxlocktime.
As an administrator, you want members of the group rocketdev
to be
limited to operations of 20,000 files or less, that scan no more than
100,000 revisions, and lock database tables for no more than 30 seconds:
Group: rocketdev MaxResults: 20000 MaxScanRows: 100000 MaxLockTime: 30000 Timeout: 43200 Subgroups: Owners: Users: bill ruth sandy
Suppose that Ruth has an unrestricted (loose) client view. She types:
$ p4 sync
Her sync
command is rejected if the depot contains more than
20,000 files. She can work around this limitation either by restricting
her client view, or, if she needs all of the files in the view, by
syncing smaller sets of files at a time, as follows:
$ p4 sync //depot/projA/...
$ p4 sync //depot/projB/...
Either method enables her to sync her files to her workspace, but without tying up the server to process a single extremely large command.
Ruth tries a command that scans every revision of every file, such as:
$ p4 filelog //depot/projA/...
If there are fewer than 20,000 revisions, but more than 100,000
integrations (perhaps the projA
directory contains 1,000 files, each
of which has fewer than 20 revisions and has been branched more than 50
times), the MaxResults
limit does not apply, but the MaxScanRows
limit does.
Regardless of which limits are in effect, no command she runs will be
permitted to lock the database for more than the MaxLockTime
of 30,000
milliseconds.
To remove any limits on the number of result lines processed (or
database rows scanned, or milliseconds of database locking time) for a
particular group, set the MaxResults
or MaxScanRows
, or
MaxLockTime
value for that group to unlimited
.
Because these limitations can make life difficult for your users, do not
use them unless you find that certain operations are slowing down your
server. Because some Perforce applications can perform large operations,
you should typically set MaxResults
no smaller than 10,000, set
MaxScanRows
no smaller than 50,000, and MaxLockTime
to somewhere
within the 1,000-30,000 (1-30 second) range.
For more information, including a comparison of Perforce commands and the number of files they affect, type:
$ p4 help maxresults
$ p4 help maxscanrows
$ p4 help maxlocktime
from the command line.
MaxResults, MaxScanRows and MaxLockTime for users in multiple groups
As mentioned earlier, if a user is listed in multiple groups, the
highest numeric MaxResults
limit of all the groups a user belongs to
is the limit that affects the user.
The default value of unset
is not a numeric limit; if a user is in a
group where MaxResults
is set to unset
, he or she is still limited
by the highest numeric MaxResults
(or MaxScanRows
or MaxLockTime
)
setting of the other groups of which he or she is a member.
A user’s commands are truly unlimited only when the user belongs to no
groups, or when any of the groups of which the user is a member have
their MaxResults
set to unlimited
.
Limiting simultaneous connections
If monitoring is enabled (p4 configure set monitor=1
or higher),
you can set the server.maxcommands
configurable to limit the number of
simultaneous command requests that the service will attempt to handle.
Ideally, this value should be set low enough to detect a runaway script or denial of service attack before the underlying hardware resources are exhausted, yet high enough to maintain a substantial margin of safety between the typical average number of connections and your site’s peak activity.
If P4LOG
is set, the server log will contain lines of the form:
Server is now using nnn
active threads.
You can use the server log to determine what levels of activity are
typical for your site. As a general guideline, set server.maxcommands
to at least 200-500% of your anticipated peak activity.
Unloading infrequently-used metadata
Over time, a Perforce server accumulates metadata associated with old
projects that are no longer in active development. On large sites,
reducing the working set of data, (particularly that stored in the
db.have
and db.labels
tables) can significantly improve performance.
Create the unload depot
To create an unload depot named //unload
, enter p4 depot
unload
, and fill in the resulting form as follows:
Depot: unload Type: unload Map: unloaded/...
In this example, unloaded metadata is stored in flat files in the
/unloaded
directory beneath your server root (that is, as specified by
the Map:
field).
After you have created the unload depot, you can use p4 unload
and p4 reload
to manage your installation’s handling of
workspace and label-related metadata.
Unload old client workspaces, labels, and task streams
The p4 unload
command transfers infrequently-used metadata from
the versioning engine’s db.*
files to a set
of flat files in the unload depot.
Individual users can use the -c
, -l
, and -s
flags to unload client
workspaces, static labels, or task streams that they own. For example,
maintainers of build scripts that create one workspace and/or label per
build, particularly in continuous build environments, should be
encouraged to unload the labels after each build:
$ p4 unload -c oldworkspace
$ p4 unload -l oldlabel
Similarly, developers should be encouraged to unload (p4 unload -s
) or delete (oldtaskstream
p4 stream -d
) task streams after use.
oldtaskstream
To manage old or obsolete metadata in bulk, administrators can use the
-a
, -al
, or -ac
flags in conjunction with the -d date
and/or
-u user
flags to unload all static labels and workspaces older than a specific
date
, owned by a specific user
, or both.
By default, only unlocked labels or workspaces are unloaded; use the
-L
flag to unload locked labels or workspaces.
To unload or reload a workspace or label, a user must be able to scan
all the files in the workspace’s have list and/or files tagged by the
label. Set MaxScanrows
and MaxResults
high enough (see
MaxResults, MaxScanRows and MaxLockTime for users in multiple groups) that users do not need to ask for assistance with p4
unload
or p4 reload
operations.
Accessing unloaded data
By default, Perforce commands such as p4 clients
, p4
labels
, p4 files
, p4 sizes
, and p4 fstat
ignore unloaded metadata. Users who need to examine unloaded workspaces
and labels (or other unloaded metadata) can use the -U
flag when using
these commands. For more information, see the P4 Command Reference.
Reloading workspaces and labels
If it becomes necessary to restore unloaded metadata back into the
db.have
or db.labels
table, use the p4 reload
command.
Scripting efficiently
The Perforce Command-Line Client, p4
, supports the scripting of
any command that can be run interactively. The Perforce server can
process commands far faster than users can issue them, so in an
all-interactive environment, response time is excellent. However,
p4
commands issued by scripts — triggers, or command wrappers,
for example — can cause performance problems if you haven’t paid
attention to their efficiency. This is not because p4
commands
are inherently inefficient, but because the way one invokes p4
as an interactive user isn’t necessarily suitable for repeated
iterations.
This section points out some common efficiency problems and solutions.
Iterating through files
Each Perforce command issued causes a connection thread to be created
and a p4d
subprocess to be started. Reducing the number of
Perforce commands your script runs might make it more efficient if the
command is lockless. Depending on the use of shared locks however, it
might be more efficient to have several commands operate on smaller sets
of files than having one command operate on a large set of files.
To minimize the number of commands, try this approach:
for i in p4 diff2 path1/... path2/...
do
[process diff output]
done
Instead of an inefficient approach like:
for i in p4 files path1/...
do
p4 diff2 path1/$i path2/$i
[process diff output]
done
Using list input files
Any Perforce command that accepts a list of files as a command-line
argument can also read the same argument list from a file. Scripts can
make use of the list input file feature by building up a list of files
first, and then passing the list file to p4 -x
.
For example, if your script might look something like this:
for components in header1 header2 header3
do
p4 edit ${component}.h
done
A more efficient alternative would be:
for components in header1 header2 header3
do
echo ${component}.h >> LISTFILE
done
p4 -x LISTFILE edit
The -x file
flag instructs p4
to read arguments, one per line,
from the named file. If the file is specified as -
(a dash), the standard
input is read.
By default, the server processes arguments from -x file
in batches of
128 arguments at a time; you can change the number of arguments processed
by the server by using the -b batchsize
flag to pass arguments in
different batch sizes.
Using branch views
Branch views can be used with p4 integrate
or p4 diff2
to reduce the number of Perforce command invocations. For example, you
might have a script that runs:
$ p4 diff2 pathA/src/... pathB/src/...
$ p4 diff2 pathA/tests/... pathB/tests/...
$ p4 diff2 pathA/doc/... pathB/doc/...
You can make it more efficient by creating a branch view that looks like this:
Branch: pathA-pathB View: pathA/src/... pathB/src/... pathA/tests/... pathB/tests/... pathA/doc/... pathB/doc/...
…and replacing the three commands with one:
$ p4 diff2 -b pathA-pathB
Limiting label references
Repeated references to large labels can be particularly costly. Commands that refer to files using labels as revisions will scan the whole label once for each file argument. To keep from hogging the Perforce server, your script should get the labeled files from the server, and then scan the output for the files it needs.
For example, this:
$ p4 files path/...@label | egrep "path/f1.h|path/f2.h|path/f3.h"
imposes a lighter load on the Perforce server than either this:
$ p4 files path/f1.h@label path/f1.h@label path/f3.h@label
or this:
$ p4 files path/f1.h@label
$ p4 files path/f2.h@label
$ p4 files path/f3.h@label
The "temporary client workspace" trick described below can also reduce the number of times you have to refer to files by label.
On large sites, consider unloading infrequently-referenced or obsolete labels from the database. See Unloading infrequently-used metadata.
Using a temporary client workspace
Most Perforce commands can process all the files in the current workspace view with a single command-line argument. By making use of a temporary client workspace with a view that contains only the files on which you want to work, you might be able to reduce the number of commands you have to run, or to reduce the number of file arguments you need to give each command.
For instance, suppose your script runs these commands:
$ p4 sync pathA/src/...@label
$ p4 sync pathB/tests/...@label
$ p4 sync pathC/doc/...@label
You can combine the command invocations and reduce the three label scans to one by using a client workspace specification that looks like this:
Client: XY-temp View: pathA/src/... //XY-temp/pathA/src/... pathB/tests/... //XY-temp/pathB/tests/... pathC/doc/... //XY-temp/pathC/doc/...
Using this workspace specification, you can then run:
$ p4 -c XY-temp sync @label
Using compression efficiently
There are cases where compression is automatically handled:
-
By default, revisions of files of type
binary
are compressed when stored on the Perforce server. Some file formats (for example, .GIF and .JPG images, .MPG and .AVI media content, files compressed with.gz
compression) include compression as part of the file format.Attempting to compress such files on the Perforce server results in the consumption of server CPU resources with little or no savings in disk space. To disable server storage compression for these file types, specify such files as type
binary+F
(binary, stored on the server in full, without compression) either from the command line or from thep4 typemap
table.For more about
p4 typemap
, including a sample typemap table, see Defining filetypes with p4 typemap. - By default compression is enabled between the Perforce server and the
proxy; if this connection is going across a VPN that is already
doing compression at a lower layer, you might want to disable the
compression for the proxy (
-c
flag).
Other server configurables
The Perforce server has many configurables that may be changed for performance purposes.
A complete list of configurables may be found by running p4 help
configurables
.
Checkpoints for database tree rebalancing
Perforce’s internal database stores its data in structures called Bayer trees, more commonly referred to as B-trees. While B-trees are a very common way to structure data for rapid access, over time, the process of adding and deleting elements to and from the trees can eventually lead to imbalances in the data structure.
Eventually, the tree can become sufficiently unbalanced that performance
is degraded. The Perforce checkpoint and restore processes
(see Backup and recovery concepts) re-create the trees in a balanced manner, and
consequently, you might see some improvement in server performance
following a backup, a removal of the db.*
files, and the re-creation of the db.*
files
from a checkpoint.
Given the length of time required for the trees to become unbalanced during normal Perforce use, we expect that the majority of sites will never need to restore the database from a checkpoint (that is, rebalance the trees) to improve performance.
(The changes to the B-trees between Perforce 2013.2 and 2013.3 require that any upgrade that crosses this release boundary must be performed by taking a checkpoint with the older release and restoring that checkpoint with the newer release. See Upgrading p4d - between 2013.2 and 2013.3 for details.)