Added benchmark data for privileged members under v8 (currently Figure 1)

master
Mike Gerwitz 2012-02-22 20:18:15 -05:00
parent a670dbf2e0
commit 63083533dc
6 changed files with 127 additions and 19 deletions

View File

@ -7,6 +7,7 @@
\usepackage{color}
\usepackage{hyperref}
\usepackage{cite}
\usepackage{multirow}
\hypersetup{colorlinks,
citecolor=black,
@ -21,6 +22,7 @@
\definecolor{lightgray}{rgb}{0.6,0.6,0.6}
\newcommand{\jsref}{\textmd Listing~\ref}
\newcommand{\fref}{\textmd Figure~\ref}
\lstdefinelanguage{JavaScript}{%
keywords={%

View File

@ -0,0 +1,12 @@
function Foo( name )
{
this.getName = function()
{
return name;
};
this.setName = function( newname )
{
name = newname;
};
}

View File

@ -0,0 +1,34 @@
/**
* Performance testing on an instance of Foo (rather specific, I know.
* Lazy-programmer-who-wants-to-get-back-to-writing-his-article syndrome).
*/
exports.run = function( Foo )
{
// get initial memory usage and start time in ms
var mem = process.memoryUsage().heapUsed,
start = ( new Date() ).getTime();
// instantiate a number of Foo's, keeping them in memory.
var i = count = 1e6, a = [];
while ( i-- )
{
a[i] = new Foo();
}
// gather final statistics before doing any additional work
var end = ( ( new Date() ).getTime() - start ),
mem_end = ( process.memoryUsage().heapUsed - mem ),
cstart = ( new Date() ).getTime();
i = count;
while ( i-- )
{
a[0].getName();
a[0].setName();
};
var cend = ( ( new Date() ).getTime() - cstart );
return [ mem_end, end, cend ];
}

View File

@ -0,0 +1,21 @@
/**
* Outputs the amount of time to run various tests against a protected member
* implementation of Foo
*/
function Foo( name )
{
this.getName = function()
{
return name;
};
this.setName = function( newname )
{
name = newname;
};
}
console.log(
require( './inst-call-cmp' ).run( Foo ).join( '\t' )
);

View File

@ -0,0 +1,25 @@
/**
* Outputs the amount of time to run various tests against a prototype
* implementation of Foo
*/
function Foo( name )
{
this.name = name;
}
Foo.prototype = {
getName: function()
{
return this.name;
},
setName: function( newname )
{
this.name = newname;
},
};
console.log(
require( './inst-call-cmp' ).run( Foo ).join( '\t' )
);

View File

@ -216,23 +216,10 @@ that functions introduce scope, allowing us to define a local variable (or use
an argument) within the constructor that is only accessible to the
\dfn{privileged member} \func{getName()}.
\begin{lstlisting}[%
\lstinputlisting[%
label=lst:privileged,
caption=Using privileged members to encapsulate data
]
function Foo( name )
{
this.getName = function()
{
return name;
};
this.setName = function( newname )
{
name = newname;
};
}
\end{lstlisting}
]{lst/privileged-members.js}
If \var{name} in \jsref{lst:privileged} is encapsulated within the constructor,
our methods that \emph{access} that encapsulated data must \emph{too} be
@ -240,12 +227,39 @@ declared within the constructor;\footnote{One may mix prototypes and privileged
members.} otherwise, if placed within the prototype, \var{name} would be out of
scope. This implementation has an unfortunate consequence --- our methods are
now being \emph{redeclared} each and every time a new instance of \var{Foo} is
created, which has obvious performance penalties.\footnote{As a general rule
of thumb, one should only use privileged members for methods that access
encapsulated data; all other members should be part of the prototype.}
created, which has obvious performance penalties (see
\fref{fig:proto-priv-cmp}).\footnote{As a general rule of thumb, one should only
use privileged members for methods that access encapsulated data; all other
members should be part of the prototype.}
\begin{figure}
\center
\begin{tabular}{r|r|r|r|}
\cline{2-4}
& Heap Usage
& \multicolumn{1}{|c|}{Inst. Time}
& \multicolumn{1}{|c|}{Call Time} \\
\hline
\multicolumn{1}{|r|}{\jsref{lst:proto-proper}}
& 49.7M & 234ms & 17ms \\
\multicolumn{1}{|r|}{\jsref{lst:privileged}}
& 236.0M & 1134ms & 28ms \\
\hline
\multicolumn{1}{|r|}{\% Change} & 374.8\% & 384.6\% & 64.7\% \\
\hline
\end{tabular}
\caption{Comparing performance of privileged member and prototype
implementations under v8. The heap usage column represents the
heap usage after instantiating \var{Foo} under the respective implementation $n$
times, and the Inst. CPU column reflects the amount of time spent instantiating
the $n$ objects. The Call CPU column reflects the amount of time spent invoking
\emph{each} member of \emph{one} instance $n$ times. $n = 1,000,000$. Lower
numbers are better. Different environments may have different results.}
\label{fig:proto-priv-cmp}
\end{figure}
Due to these performance concerns, it is often undesirable to use privileged
members; many developers will instead simply prefix with an underscore members
members; many developers will instead prefix, with an underscore, members
intended to be private (e.g. \code{this.\_name}) while keeping all methods on
the prototype.\footnote{One example of a library that uses underscores in place
of privileged members is Dojo at http://dojotoolkit.org.} This serves as a clear