Solved

PHP 5:  Performance Issue - Session Variable.

Posted on 2004-08-05
4
472 Views
Last Modified: 2006-11-17
Hello Experts!

Using PHP 5 and smarty:
I have mapped my system out such that individual processes (such as registering users etc) are controlled by a control class (object).  This control object has an associative array of variables which it stores and extracts from the session.  Each time the page is loaded, the object constructor retrieves this array from the session.  Data could be status, error messages etc.  The controls basically handle all logic and  load templates based on the status that is kept through the session.  My question has to do with performance in extracting and updating this array from the session.

PHP 5's array model is unlike its object model.  That is, if I have a large array, when copied, the array will be deep copied as well.  Objects held in the array will obviously keep only a reference.  But for my control variable array there are a collection of strings, objects etc.  

If I do the following

$controlVarArray = $_SESSION['controlVarArray'];

// Lets say controlVarArray has a single object and an error message ("error").
$o = $controlVarArray['myObject'];
$mess = $controlVarArray['errorMessage']; // $mess = "error"

// If I then update the object, the reference changes and I don't have to worry about updating the session variable again.
$o->setName('myNewName');

// But if I change the error, it is not by reference so it doesn't change in the session
$controlVarArray['errorMessage'] = "newMessage";

If I output the session data now it will have the object with myNewName, but it will still have "error" as the errorMessage.  

So there are a number of things I could do.  Which one would you do and why would be ideal.

1.  I could get a reference to the session variable.  Something like $controlVarArray = &$_SESSION['controlVarArray'];
I am worried that I am getting the same performance hit as referencing the slow $_SESSION variable.

2.  I could update the session variable explicitly each time.  Something like $_SESSION['controlVarArray'] = $controlVarArray.
This is a little ugly in my definition, as it would require the update after each change (or status change).  

3.  I could wrap the array in an object and simply set and extract that reference.  I am leaning towards this, however I am not sure if I am suffering from the same penalty as 1.  I believe it would depend on how the object reference is stored. . .

I will reward people with any useful insight. . .

I apologize if this is worded  unclearly. . .

dtan
0
Comment
Question by:d_tan
  • 2
  • 2
4 Comments
 
LVL 4

Expert Comment

by:aratani
ID: 11732798
As far as I believe, session variables are loaded when session_start is called and not written back until the script is over or session_write_close is called. So, the session variables once in memory are always in memory.

I believe that using option 2 will be the best or using the $_SESSION array directly if  you can like $_SESSION ['controlarray']['errmessage']. It should store the values in memory and write them back when the script is done.

The script below shows that using a reference to a session (7 seconds for 500,000 references) is better than wrapping the array around an object and using a reference to it (10 seconds for 500.000 references). Thats cause when you wrap an object around an array you have to go one more reference deep to get to the same data.

I hope that was your question, and you have a better understanding of sessions.

Regards,

AJ

This is the script that I wrote and tested against,

<?php

      class A {
            public $x;

            function __construct () {      
                  $x = 50;
            }
      }

      class B {
            public $arr;

            function __construct ($arr) {
                  $this->arr = $arr;
            }
      }

      session_start ();
      
      $a = new A ();
      $str = "test";

      $arr = array ();
      $arr ['class'] = $a;
      $arr ['err'] = $str;

      $_SESSION ['arr'] = $arr;

      $arrwrap = new B ($arr);

      $_SESSION ['arrwrap'] = $arrwrap;

      echo "<pre>";
      print_r ($_SESSION);
      echo "</pre>";

      $arr [0]->x = 100;

      echo "<pre>";
      print_r ($_SESSION);
      echo "</pre>";

      $sessiondata = &$_SESSION;

      $stime = time ();

      echo "<br>".$stime;

      for ($i = 0; $i < 5000000; $i++) {

            //read session data
            $data = $sessiondata ['arr']['class'];
      }

      $stime = time ();

      echo "<br>".$stime;

      //This took 7 seconds
      $stime = time ();

      echo "<br>".$stime;

      for ($i = 0; $i < 5000000; $i++) {

            //read session data
            $data = $sessiondata ['arrwrap']->arr ['class'];
      }

      $stime = time ();

      echo "<br>".$stime;
      //This took 10 seconds

?>
0
 
LVL 2

Author Comment

by:d_tan
ID: 11733031
Hi Aratani.  

Your script was helpful but missed the target by just a little bit.  I modified your script to the following:

////////////////////////////////////
class A {
    public $x;

    function __construct ()
    {
        $x = 50;
    }
}

class B {
    public $arr;

    function __construct ( $arr )
    {
        $this->arr = $arr;
    }
}

session_start ();
// reset
$_SESSION = array();
set_time_limit( 100 );
// create an object
$a = new A ();
$str = "test";

$arr = array ();
$arr ['class'] = $a;
$arr ['err'] = $str;
// Set the session array
$_SESSION ['arr'] = $arr;
// Set the wrapper in the session
$arrwrap = new B ( $arr );
$_SESSION ['arrwrap'] = $arrwrap;
// Print the session array
echo "<pre>";
print_r ( $_SESSION );
echo "</pre>";
// Prove that Session is holding a reference
$a->x = 100;
echo "<pre>";
print_r ( $_SESSION );
echo "</pre>";
//////////////////////////////////////
// First test (access session directly)
$stime = time ();
// Access the session array
for ( $i = 0; $i < 3000000; $i++ ) {
    // read session data
    $data = $_SESSION['arr']['err'];
    $data = "test1";
    $_SESSION['arr']['err'] = $data;
}

$etime = time ();
echo "<br>Accessing session directly";
echo "<br>Start: $stime<br>Finished $etime";
// This took 7 seconds
echo "<pre>";
print_r ( $_SESSION );
echo "</pre>";
//////////////////////////////////////
// Second test (access array through reference)
$wrap = $_SESSION['arrwrap'];
$wrapArr = &$wrap->arr;

$stime = time ();
for ( $i = 0; $i < 3000000; $i++ ) {
    // read session data
    $data = $wrapArr['err'];
    $data = "test2";
    $wrapArr['err'] = $data;
}

$etime = time ();
echo "<br>Indirect accessing";
echo "<br>Start: $stime<br>Finished $etime";
echo "<pre>";
print_r ( $_SESSION );
echo "</pre>";
//////////////////////////////////////
// Third test (gain copy and write out at end)
$arrayCopy = $_SESSION['arr'];
$stime = time ();
// Access the session array
for ( $i = 0; $i < 3000000; $i++ ) {
    // read session data
    $data = $arrayCopy['err'];
    $data = "test3";
    $arrayCopy['err'] = $data;
}

$_SESSION['arr'] = $arrayCopy;
$etime = time ();
echo "<br>Copying session and restoring";
echo "<br>Start: $stime<br>Finished $etime";
echo "<pre>";
print_r ( $_SESSION );
echo "</pre>";

//////////////////////////////////////
// Forth test (reference)
$arrayRef = &$_SESSION['arr'];
$stime = time ();
// Access the session array
for ( $i = 0; $i < 3000000; $i++ ) {
    // read session data
    $data = $arrayRef['err'];
    $data = "test4";
    $arrayRef['err'] = $data;
}

$etime = time ();
echo "<br>Accessing reference to session array";
echo "<br>Start: $stime<br>Finished $etime";
echo "<pre>";
print_r ( $_SESSION );
echo "</pre>";
//////////////////////////////////////////////////////////


Hoping that you could help verify my test runs. . .

Test 1 corresponds to accessing the session directly.  It was the slowest of the tests, however this could be due to the extra step in depth through accessing the associative array through the session array.

Test 2 corresponds to 3 (wrapping the array and then using the wrapped reference.  The wrapping essentially removes the need to explicitly set the reference (See Test 3).  This is not a big deal but saves me writing code after each control status change. This was the fastest but only by 1 second.

Test 3 corresponds to copying the array and then updating data.  FInally I updat ethe session array as this is needed to ensure consistancy through the tests.  This was always 1 second slower than 2.

Test 4 corresponds to referencing the array directly (like 2 without the wrapper).  Turns out this is equal speed to 2.  

So it appears as though I blindly followed the advice given in the php manual.  As you said, it appears that the session vars are read into memory at the script start, and written at end. . .

thoughts??

dtan

0
 
LVL 4

Accepted Solution

by:
aratani earned 350 total points
ID: 11738279
The $_SESSION variables are read into memory at the time of loading. This way the script doesn't have to go and write back to disk when every value is changed or new values are added. When a value changes it changes in memory, and then when the script gets over, it writes it back to disk (or the command session_write_close ()).

Also, you cannot have two session_start ()'s running simultaneously (like you might in a framed). Since, when the session is loading it blocks all the other session_start.

Anyway, back to real topic. I ran you script and you are right. I believe that the reason the accessing $_SESSION directly takes more time is because you are accessing an array within an array.

So, I would say the best way is use option 4 (in the script above) of yours, since you don't have to go two levels deep (when you use the $_SESSION variable directly). I believe your option 2 is an overkill, and option 3 is not an elegant coding solution.

I hope this helped you out. If you have more questions, please post one more message.

Take care

AJ

0
 
LVL 2

Author Comment

by:d_tan
ID: 11741767
Hey AJ,

Thanks for the follow up. . .helped me out quite a bit.  I agree wthat option 4 is probably best.  I actually ran the script again using large arrays held in mem.  References were best.  

Thanks again. . .

dtan
0

Featured Post

How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
whm high memory usage in processes 7 38
Magneto product importing through API script 10 32
Button Click 11 26
update field on focusout 15 24
Author Note: Since this E-E article was originally written, years ago, formal testing has come into common use in the world of PHP.  PHPUnit (http://en.wikipedia.org/wiki/PHPUnit) and similar technologies have enjoyed wide adoption, making it possib…
This article discusses four methods for overlaying images in a container on a web page
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

759 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

21 Experts available now in Live!

Get 1:1 Help Now