Skip to main content
deleted 3 characters in body; added 125 characters in body
Source Link

I'm pretty bummed about the slow foreach loop over my Recordset object, by the slower internal seek and fetch speeds within Recordset, and by the fact that nothing can even compare to the old while ($MySQLI_STMT->fetch()) {}. I've already cached certain result information, such as num_rows and class name, and try to avoid chaining internal function calls, which speeds things up considerably. Unfortunately, this cuts down on proper internal data validation checks. The constant data_seek calls do slow things a little. I'd like for this to work better. Any suggestions or insight how to speed up foreach loops and internal row fetching?

  • foreach($Recordset as $rec) {$x = $rec;} ...
    0.0723 s1033s
  • for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset[$i];} ...
    0.0523 s0445s
  • for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset->fetchRow($i);} ...
    0.04060344 s
  • while ($x = $Recordset[$i]) {$i++; if (!isset($Recordset[$i])) break;} ...
    0.06180542 s
  • for ($i=0, $count=MySQLI_STMT->num_rows; $i < $count; $i++)) {$MySQLI_STMT->fetch(); $x = $bind_row;} ...
    0.04340414 s
  • while ($MySQLI_STMT->fetch()) {$x = $bind_row;} ...
    0.0091009 s

I'm pretty bummed about the slow foreach loop over my Recordset object, by the slower internal seek and fetch speeds within Recordset, and by the fact that nothing can even compare to the old while ($MySQLI_STMT->fetch()) {}. I've already cached certain result information, such as num_rows and class name, which speeds things up considerably. The constant data_seek calls do slow things a little. I'd like for this to work better. Any suggestions or insight how to speed up foreach loops and internal row fetching?

  • foreach($Recordset as $rec) {$x = $rec;} ...
    0.0723 s
  • for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset[$i];} ...
    0.0523 s
  • for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset->fetchRow($i);} ...
    0.0406 s
  • while ($x = $Recordset[$i]) {$i++; if (!isset($Recordset[$i])) break;} ...
    0.0618 s
  • for ($i=0, $count=MySQLI_STMT->num_rows; $i < $count; $i++)) {$MySQLI_STMT->fetch(); $x = $bind_row;} ...
    0.0434 s
  • while ($MySQLI_STMT->fetch()) {$x = $bind_row;} ...
    0.0091 s

I'm pretty bummed about the slow foreach loop over my Recordset object, by the slower internal seek and fetch speeds within Recordset, and by the fact that nothing can even compare to the old while ($MySQLI_STMT->fetch()) {}. I've already cached certain result information, such as num_rows and class name, and try to avoid chaining internal function calls, which speeds things up considerably. Unfortunately, this cuts down on proper internal data validation checks. The constant data_seek calls do slow things a little. I'd like for this to work better. Any suggestions or insight how to speed up foreach loops and internal row fetching?

  • foreach($Recordset as $rec) {$x = $rec;} ...
    0.1033s
  • for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset[$i];} ...
    0.0445s
  • for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset->fetchRow($i);} ...
    0.0344 s
  • while ($x = $Recordset[$i]) {$i++; if (!isset($Recordset[$i])) break;} ...
    0.0542 s
  • for ($i=0, $count=MySQLI_STMT->num_rows; $i < $count; $i++)) {$MySQLI_STMT->fetch(); $x = $bind_row;} ...
    0.0414 s
  • while ($MySQLI_STMT->fetch()) {$x = $bind_row;} ...
    0.009 s
added 553 characters in body
Source Link
class Recordset implements Iterator, ArrayAccess, Countable {

    private $MySQLi_set; // MySQLi_STMT or MySQLi_Result object
    private $MySQLi_set_type; // cache of MySQLi_STMT or MySQLi_Result object type to avoid repeat instanceof calls
    private $field_metadata; // array of field info objects returned by query
    private $num_rows; // cache row count to avoid repeat mysqli_x->num_rows call
    private $bind_row; // binding array for MySQLi_STMT results
    private $pointer; // for tracking iterations over records in query


    function __construct($sql) {
        $bind_params = func_get_args();
        array_shift($bind_params);
        try {
            if (count($bind_params) === 0) {
                // expecting MYSQLI_Result obj returned, replace code as necessary
                $this->MySQLi_set = DB_Main::query($sql);  
                }
            else {
                // expecting MySQLi_STMT obj returned, replace code as necessary
                $this->MySQLi_set = DB_Main::statement($sql, $bind_params);  
                }
            }
        catch (Exception $e) {
            throw new RuntimeException(((count($bind_params)) ? 'Statement' : 'Query') . ' failed:  ' . $e->getMessage(), $e->getCode(), $e);
            }
        // cache name of object to avoid repeat instanceof calls
        $this->MySQLi_set_type = get_class($this->MySQLi_set);
        if ($this->MySQLi_set_type === 'mysqli_result') {
            // set $this->field_metadata to array of field info objects
            $this->field_metadata = $this->MySQLi_set->fetch_fields();
            }
        else if ($this->MySQLi_set_type === 'mysqli_stmt') {
            // Set up special handling for MySQLi_STMT objects
            // set $this->field_metadata to array of field info objects used later for result bindings for MySQLi_STMT object
            $result_metadata = $this->MySQLi_set->result_metadata();
            $this->field_metadata = $result_metadata->fetch_fields();   
            $result_metadata->free_result();
            unset($result_metadata);
            // store results to allow object to act as a countable, seekable array
            if (!$this->MySQLi_set->store_result())
                throw new RuntimeException('MySQLi_STMT Result failed to store.  Error: ' . $this->MySQLi_set->error);
            // because bind_result() cannot bind to an object or array, use the 'call_user_func_array()' technique instead
            $this->bind_row = array();
            $bind_row2 = array();
            foreach($this->field_metadata as $field)
                 $bind_row2[$field->name] =& $this->bind_row[$field->name];
            if (!call_user_func_array(array($this->MySQLi_set, 'bind_result'), $this->bind_row))
                throw new RuntimeException($this->MySQLi_set->error);
            if ($this->MySQLi_set->error)
                throw new RuntimeException($this->MySQLi_set->error);
            }
        else {
            throw new RuntimeException('No MySQLi_STMT or MySQLi_Result object to work with.  OnlyClass: ' . $this->MySQLi_set_type . ' providedattempted.');
            }
        // cache row count to avoid constant calls to MySQLi_x->num_rows
        $this->num_rows = $this->MySQLi_set->num_rows;
        // if rows were returned, set internal pointer to 0
        if ($this->num_rows > 0) $this->pointer = 0;
        }


    function fetchRow($offset = null) {
        // private function internally using regular fetch() methods to progress through records
        // sets $this->bind_row to an associative array of the next record for MySQLi_STMT wrapped objects
        $this->MySQLi_set->data_seek((($offset !== null) ? (int) $offset : $this->pointer));
        if ($this->MySQLi_set_type === 'mysqli_result')
            return $this->MySQLi_set->fetch_assoc();
        // else this is a MySQLi_STMT object
        $fetch_status = $this->MySQLi_set->fetch();
        if ($fetch_status === true) return $this->bind_row;
        else if ($fetch_status === null) return null; // no more rows
        // MySQLi_STMT row fetch failed
        throw new RuntimeException('Error fetching row: . ' . $this->MySQLi_set->error);
        }

        
    function count() {return $this->num_rows;}
        
    function current() {return $this->fetchRow();}

    function key() {return $this->pointer;}
    
    function next() {$this->pointer++;}
    
    function rewind() {$this->pointer = 0;}

    function valid() {return $this->offsetExists($this->pointer);}


    function offsetExists($offset) {
        // Part of the ArrayAccess interface.  Used to determine if the selected record in the set actually exists or is out of bounds.
    function offsetExists   // Example: 'isset($offset$Recordset[22])' {returns true if there were 23 or more records returned from the query.
        return ((int) $offset < $this->num_rows AND (int) $offset >= 0);}
        }


    function offsetGet($offset) {
        // Part of the ArrayAccess interface.  Used to retrieve the assoc array of the chosen record in line.
        // allow index referencing without altering "internal array pointer"
        // Example: 'print_r($Recordset[22])' prints the 23rd record returned by the query.
        if (!((int) $offset < $this->offsetExists>num_rows AND (int) $offset >= 0))
            throw new OutOfBoundsException('Out of range of result set.');
        return $this->fetchRow($offset);
        }
 
    }

The code is roughly 200 lines long, so instead of making a giant post, here's a pastebin link... http://pastebin.com/WPaHeguXhttp://pastebin.com/n17MuEzf

  • foreach($Recordset as $rec) {$x = $rec;} ...
    0.1050723 s
  • for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset[$i];} ...
    0.0560523 s
  • for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset->fetchRow($i);} ...
    0.03510406 s
  • while ($x = $Recordset[$i]) {$i++; if (!isset($Recordset[$i])) break;} ...
    0.06470618 s
  • for ($i=0, $count=$MySQLI_STMT$count=MySQLI_STMT->num_rows; $i < $count; $i++)) {$MySQLI_STMT->fetch(); $x = $bind_row;} ...
    0.04090434 s
  • while ($MySQLI_STMT->fetch()) {$x = $bind_row;} ...
    0.0090091 s
class Recordset implements Iterator, ArrayAccess, Countable {

    private $MySQLi_set; // MySQLi_STMT or MySQLi_Result object
    private $MySQLi_set_type; // cache of MySQLi_STMT or MySQLi_Result object type to avoid repeat instanceof calls
    private $field_metadata; // array of field info objects returned by query
    private $num_rows; // cache row count to avoid repeat mysqli_x->num_rows call
    private $bind_row; // binding array for MySQLi_STMT results
    private $pointer; // for tracking iterations over records in query


    function __construct($sql) {
        $bind_params = func_get_args();
        array_shift($bind_params);
        try {
            if (count($bind_params) === 0) {
                // expecting MYSQLI_Result obj returned, replace code as necessary
                $this->MySQLi_set = DB_Main::query($sql);  
                }
            else {
                // expecting MySQLi_STMT obj returned, replace code as necessary
                $this->MySQLi_set = DB_Main::statement($sql, $bind_params);  
                }
            }
        catch (Exception $e) {
            throw new RuntimeException(((count($bind_params)) ? 'Statement' : 'Query') . ' failed:  ' . $e->getMessage(), $e->getCode(), $e);
            }
        // cache name of object to avoid repeat instanceof calls
        $this->MySQLi_set_type = get_class($this->MySQLi_set);
        if ($this->MySQLi_set_type === 'mysqli_result') {
            // set $this->field_metadata to array of field info objects
            $this->field_metadata = $this->MySQLi_set->fetch_fields();
            }
        else if ($this->MySQLi_set_type === 'mysqli_stmt') {
            // Set up special handling for MySQLi_STMT objects
            // set $this->field_metadata to array of field info objects used later for result bindings for MySQLi_STMT object
            $result_metadata = $this->MySQLi_set->result_metadata();
            $this->field_metadata = $result_metadata->fetch_fields();   
            $result_metadata->free_result();
            unset($result_metadata);
            // store results to allow object to act as a countable, seekable array
            if (!$this->MySQLi_set->store_result())
                throw new RuntimeException('MySQLi_STMT Result failed to store.  Error: ' . $this->MySQLi_set->error);
            // because bind_result() cannot bind to an object or array, use the 'call_user_func_array()' technique instead
            $this->bind_row = array();
            $bind_row2 = array();
            foreach($this->field_metadata as $field)
                 $bind_row2[$field->name] =& $this->bind_row[$field->name];
            if (!call_user_func_array(array($this->MySQLi_set, 'bind_result'), $this->bind_row))
                throw new RuntimeException($this->MySQLi_set->error);
            if ($this->MySQLi_set->error)
                throw new RuntimeException($this->MySQLi_set->error);
            }
        else {
            throw new RuntimeException('No MySQLi_STMT or MySQLi_Result object to work with.  Only ' . $this->MySQLi_set_type . ' provided.');
            }
        // cache row count to avoid constant calls to MySQLi_x->num_rows
        $this->num_rows = $this->MySQLi_set->num_rows;
        // if rows were returned, set internal pointer to 0
        if ($this->num_rows > 0) $this->pointer = 0;
        }


    function fetchRow($offset = null) {
        // private function internally using regular fetch() methods to progress through records
        // sets $this->bind_row to an associative array of the next record for MySQLi_STMT wrapped objects
        $this->MySQLi_set->data_seek((($offset !== null) ? (int) $offset : $this->pointer));
        if ($this->MySQLi_set_type === 'mysqli_result')
            return $this->MySQLi_set->fetch_assoc();
        // else this is a MySQLi_STMT object
        $fetch_status = $this->MySQLi_set->fetch();
        if ($fetch_status === true) return $this->bind_row;
        else if ($fetch_status === null) return null; // no more rows
        // MySQLi_STMT row fetch failed
        throw new RuntimeException('Error fetching row: . ' . $this->MySQLi_set->error);
        }

        
    function count() {return $this->num_rows;}
        
    function current() {return $this->fetchRow();}

    function key() {return $this->pointer;}
    
    function next() {$this->pointer++;}
    
    function rewind() {$this->pointer = 0;}

    function valid() {return $this->offsetExists($this->pointer);}
        
    function offsetExists($offset) {return ((int) $offset < $this->num_rows AND (int) $offset >= 0);}
        
    function offsetGet($offset) {
        if (!$this->offsetExists($offset))
            throw new OutOfBoundsException('Out of range of result set.');
        return $this->fetchRow($offset);
        }
    }

The code is roughly 200 lines long, so instead of making a giant post, here's a pastebin link... http://pastebin.com/WPaHeguX

  • foreach($Recordset as $rec) {$x = $rec;} ...
    0.105 s
  • for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset[$i];} ...
    0.056 s
  • for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset->fetchRow($i);} ...
    0.0351 s
  • while ($x = $Recordset[$i]) {$i++; if (!isset($Recordset[$i])) break;} ...
    0.0647 s
  • for ($i=0, $count=$MySQLI_STMT->num_rows; $i < $count; $i++)) {$MySQLI_STMT->fetch(); $x = $bind_row;} ...
    0.0409 s
  • while ($MySQLI_STMT->fetch()) {$x = $bind_row;} ...
    0.009 s
class Recordset implements Iterator, ArrayAccess, Countable {

    private $MySQLi_set; // MySQLi_STMT or MySQLi_Result object
    private $MySQLi_set_type; // cache of MySQLi_STMT or MySQLi_Result object type to avoid repeat instanceof calls
    private $field_metadata; // array of field info objects returned by query
    private $num_rows; // cache row count to avoid repeat mysqli_x->num_rows call
    private $bind_row; // binding array for MySQLi_STMT results
    private $pointer; // for tracking iterations over records in query


    function __construct($sql) {
        $bind_params = func_get_args();
        array_shift($bind_params);
        try {
            if (count($bind_params) === 0) {
                // expecting MYSQLI_Result obj returned, replace code as necessary
                $this->MySQLi_set = DB_Main::query($sql);  
                }
            else {
                // expecting MySQLi_STMT obj returned, replace code as necessary
                $this->MySQLi_set = DB_Main::statement($sql, $bind_params);  
                }
            }
        catch (Exception $e) {
            throw new RuntimeException(((count($bind_params)) ? 'Statement' : 'Query') . ' failed:  ' . $e->getMessage(), $e->getCode(), $e);
            }
        // cache name of object to avoid repeat instanceof calls
        $this->MySQLi_set_type = get_class($this->MySQLi_set);
        if ($this->MySQLi_set_type === 'mysqli_result') {
            // set $this->field_metadata to array of field info objects
            $this->field_metadata = $this->MySQLi_set->fetch_fields();
            }
        else if ($this->MySQLi_set_type === 'mysqli_stmt') {
            // Set up special handling for MySQLi_STMT objects
            // set $this->field_metadata to array of field info objects used later for result bindings for MySQLi_STMT object
            $result_metadata = $this->MySQLi_set->result_metadata();
            $this->field_metadata = $result_metadata->fetch_fields();   
            $result_metadata->free_result();
            unset($result_metadata);
            // store results to allow object to act as a countable, seekable array
            if (!$this->MySQLi_set->store_result())
                throw new RuntimeException('MySQLi_STMT Result failed to store.  Error: ' . $this->MySQLi_set->error);
            // because bind_result() cannot bind to an object or array, use the 'call_user_func_array()' technique instead
            $this->bind_row = array();
            $bind_row2 = array();
            foreach($this->field_metadata as $field)
                 $bind_row2[$field->name] =& $this->bind_row[$field->name];
            if (!call_user_func_array(array($this->MySQLi_set, 'bind_result'), $this->bind_row))
                throw new RuntimeException($this->MySQLi_set->error);
            if ($this->MySQLi_set->error)
                throw new RuntimeException($this->MySQLi_set->error);
            }
        else {
            throw new RuntimeException('No MySQLi_STMT or MySQLi_Result object to work with.  Class: ' . $this->MySQLi_set_type . ' attempted.');
            }
        // cache row count to avoid constant calls to MySQLi_x->num_rows
        $this->num_rows = $this->MySQLi_set->num_rows;
        // if rows were returned, set internal pointer to 0
        if ($this->num_rows > 0) $this->pointer = 0;
        }


    function fetchRow($offset = null) {
        // private function internally using regular fetch() methods to progress through records
        // sets $this->bind_row to an associative array of the next record for MySQLi_STMT wrapped objects
        $this->MySQLi_set->data_seek((($offset !== null) ? (int) $offset : $this->pointer));
        if ($this->MySQLi_set_type === 'mysqli_result')
            return $this->MySQLi_set->fetch_assoc();
        // else this is a MySQLi_STMT object
        $fetch_status = $this->MySQLi_set->fetch();
        if ($fetch_status === true) return $this->bind_row;
        else if ($fetch_status === null) return null; // no more rows
        // MySQLi_STMT row fetch failed
        throw new RuntimeException('Error fetching row: . ' . $this->MySQLi_set->error);
        }

        
    function count() {return $this->num_rows;}
        
    function current() {return $this->fetchRow();}

    function key() {return $this->pointer;}
    
    function next() {$this->pointer++;}
    
    function rewind() {$this->pointer = 0;}

    function valid() {return $this->offsetExists($this->pointer);}


    function offsetExists($offset) {
        // Part of the ArrayAccess interface.  Used to determine if the selected record in the set actually exists or is out of bounds.
        // Example: 'isset($Recordset[22])' returns true if there were 23 or more records returned from the query.
        return ((int) $offset < $this->num_rows AND (int) $offset >= 0);
        }


    function offsetGet($offset) {
        // Part of the ArrayAccess interface.  Used to retrieve the assoc array of the chosen record in line.
        // allow index referencing without altering "internal array pointer"
        // Example: 'print_r($Recordset[22])' prints the 23rd record returned by the query.
        if (!((int) $offset < $this->num_rows AND (int) $offset >= 0))
            throw new OutOfBoundsException('Out of range of result set.');
        return $this->fetchRow($offset);
        }
 
    }

The code is roughly 200 lines long, so instead of making a giant post, here's a pastebin link... http://pastebin.com/n17MuEzf

  • foreach($Recordset as $rec) {$x = $rec;} ...
    0.0723 s
  • for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset[$i];} ...
    0.0523 s
  • for ($i = 0, $count = count($Recordset); $i < $count; $i++) {$x = $Recordset->fetchRow($i);} ...
    0.0406 s
  • while ($x = $Recordset[$i]) {$i++; if (!isset($Recordset[$i])) break;} ...
    0.0618 s
  • for ($i=0, $count=MySQLI_STMT->num_rows; $i < $count; $i++)) {$MySQLI_STMT->fetch(); $x = $bind_row;} ...
    0.0434 s
  • while ($MySQLI_STMT->fetch()) {$x = $bind_row;} ...
    0.0091 s
edited body
Source Link

I'm pretty bummed about the slow foreach loop over my Recordset object, by the slower internal seek and fetch speeds within Recordset, and by the fact that nothing can even compare to the old while ($MySQLI_STMT->fetch()) {}. I've already cached certain result information, such as num_rows and class name, which speeds things up considerably. The constant data_seek calls do slow things a little. I'd like for this to work better. Any suggestions or insight how to speed up foreach loops and internal row retchingfetching?

I'm pretty bummed about the slow foreach loop over my Recordset object, by the slower internal seek and fetch speeds within Recordset, and by the fact that nothing can even compare to the old while ($MySQLI_STMT->fetch()) {}. I've already cached certain result information, such as num_rows and class name, which speeds things up considerably. The constant data_seek calls do slow things a little. I'd like for this to work better. Any suggestions or insight how to speed up foreach loops and internal row retching?

I'm pretty bummed about the slow foreach loop over my Recordset object, by the slower internal seek and fetch speeds within Recordset, and by the fact that nothing can even compare to the old while ($MySQLI_STMT->fetch()) {}. I've already cached certain result information, such as num_rows and class name, which speeds things up considerably. The constant data_seek calls do slow things a little. I'd like for this to work better. Any suggestions or insight how to speed up foreach loops and internal row fetching?

added 639 characters in body
Source Link
Loading
deleted 51 characters in body
Source Link
Loading
added 174 characters in body
Source Link
Loading
added 5 characters in body; added 30 characters in body; added 33 characters in body
Source Link
Loading
added 105 characters in body; added 2 characters in body; edited body; added 325 characters in body
Source Link
Loading
added 3644 characters in body; deleted 93 characters in body; added 105 characters in body
Source Link
Loading
deleted 275 characters in body
Source Link
Loading
added 89 characters in body; Post Made Community Wiki
Source Link
Loading
deleted 81 characters in body
Source Link
Loading
edited tags
Link
Loading
added 20 characters in body; added 6 characters in body
Source Link
Loading
added 254 characters in body; added 10 characters in body
Source Link
Loading
deleted 1 characters in body; deleted 5 characters in body
Source Link
Loading
Tweeted twitter.com/#!/StackCodeReview/status/31228094983438336
added 266 characters in body; edited body; added 43 characters in body
Source Link
Loading
added 57 characters in body; added 92 characters in body; deleted 5 characters in body
Source Link
Loading
added 124 characters in body; added 28 characters in body
Source Link
Loading
added 3 characters in body
Source Link
Loading
deleted 26 characters in body
Source Link
Loading
added 20 characters in body; added 3 characters in body; added 6 characters in body; added 23 characters in body
Source Link
Loading
Source Link
Loading