Index: src/includes/admin/tools.php
===================================================================
--- src/includes/admin/tools.php	(revision 5594)
+++ src/includes/admin/tools.php	(working copy)
@@ -1372,6 +1372,7 @@
 	$statement = __( 'Recalculating reply menu order &hellip; %s', 'bbpress' );
 	$result    = __( 'No reply positions to recalculate!',         'bbpress' );
 
+
 	// Delete cases where `_bbp_reply_to` was accidentally set to itself
 	if ( is_wp_error( $wpdb->query( "DELETE FROM `{$wpdb->postmeta}` WHERE `meta_key` = '_bbp_reply_to' AND `post_id` = `meta_value`;" ) ) ) {
 		return array( 1, sprintf( $statement, $result ) );
@@ -1379,38 +1380,130 @@
 
 	// Post type
 	$rpt = bbp_get_reply_post_type();
+	$pst = bbp_get_pending_status_id();
 
-	// Get an array of reply id's to update the menu oder for each reply
-	$replies = $wpdb->get_results( "SELECT `a`.`ID` FROM `{$wpdb->posts}` AS `a`
+	// Set the menu_order of pending replies who have a post_date_gmt of 0 (posts that have been posted but never published)
+	$wpdb->update('wp_posts', array('menu_order'=>'0'), array('post_type'=>$rpt, 'post_status'=>$pst, 'post_date_gmt'=>'0000-00-00 00:00:00') );
+
+	// Post type
+	$offset = 0;
+	$limit = 500;
+	$done = false;
+	$replies_count = 0;
+	
+	$queryTimes = array();
+	$updateTimes = array();
+	
+	// ready steady GO
+	$startTime = microtime(true);
+	
+	// first look through the menu_order 0 replies. Are there any in topics which aren't spammed or pending? Only topics that are....
+	$replies_mozero = $wpdb->get_results( "SELECT `a`.`post_parent` FROM `{$wpdb->posts}` AS `a`
 										INNER JOIN (
 											SELECT `menu_order`, `post_parent`
-											FROM `{$wpdb->posts}`
+											FROM `{$wpdb->posts}` WHERE `post_type` = '{$rpt}' AND `menu_order` = 0 
 											GROUP BY `menu_order`, `post_parent`
 											HAVING COUNT( * ) >1
 										)`b`
 										ON `a`.`menu_order` = `b`.`menu_order`
 										AND `a`.`post_parent` = `b`.`post_parent`
-										WHERE `post_type` = '{$rpt}';", OBJECT_K );
+	
+									 LIMIT {$limit}", OBJECT_K );
+	
+	while( !$done ){
+		// Get an array of reply id's to update the menu oder for each reply
+		$startQueryTime = microtime(true);
+		$replies = $wpdb->get_results( "SELECT `a`.`ID` FROM `{$wpdb->posts}` AS `a`
+										INNER JOIN (
+											SELECT `menu_order`, `post_parent`
+											FROM `{$wpdb->posts}` WHERE `post_type` = '{$rpt}' AND `menu_order` != 0 
+											GROUP BY `menu_order`, `post_parent`
+											HAVING COUNT( * ) >1
+										)`b`
+										ON `a`.`menu_order` = `b`.`menu_order`
+										AND `a`.`post_parent` = `b`.`post_parent`
+	
+									 LIMIT {$limit}", OBJECT_K );
 
-	// Bail if no replies returned
-	if ( empty( $replies ) ) {
-		return array( 1, sprintf( $statement, $result ) );
-	}
+		$endQueryTime = microtime(true);
+		
+		$queryTimes[] = ( $endQueryTime - $startQueryTime ); 
+		
+		if( $offset >= 2000 ){
+			$done = true;
+		}
 
-	// Recalculate the menu order position for each reply
-	foreach ( $replies as $reply ) {
-		bbp_update_reply_position( $reply->ID );
+		$replies_count += count( $replies );
+		$offset += $limit;
+	
+		// Bail if no replies returned
+		if ( $replies_count == 0 ) {
+			return array( 1, sprintf( $statement, $result ) );
+		}
+
+		// Recalculate the menu order position for each reply
+		foreach ( $replies as $reply ) {
+			$startUpdateTime = microtime(true);
+			bbp_update_reply_position( $reply->ID );
+			$endUpdateTime = microtime(true);
+			$updateTimes[] = ( $endUpdateTime - $startUpdateTime ); 
+			
+		}
 	}
-
 	// Cleanup
 	unset( $replies, $reply );
-
+	$midTime = microtime(true);
+	
+	$averageQueryTime = array_sum( $queryTimes ) / count( $queryTimes ); 
+		
+	$averageUpdateTime = array_sum( $updateTimes ) / count( $updateTimes ); 
+	$stdDevUpdateTime = stats_standard_deviation( $updateTimes );
+	
 	// Flush the cache; things are about to get ugly.
 	wp_cache_flush();
+	$endTime = microtime(true);
 
-	return array( 0, sprintf( $statement, __( 'Complete!', 'bbpress' ) ) );
+	return array( 0, sprintf( $statement, __('Average update time: ' . $averageUpdateTime . "\n\nStandard deviation: " . $stdDevUpdateTime . "\n\nAverage query time: " . $averageQueryTime . "\n\nOffset: " . $offset . ' // Replies count: ' . $replies_count . ' // Mid time: ' . ($midTime - $startTime) . ' // End time: ' . ($endTime - $startTime), 'bbpress' ) ) );
 }
 
+/**
+ *  standard deviation thing from http://php.net/manual/en/function.stats-standard-deviation.php
+ */
+
+if (!function_exists('stats_standard_deviation')) {
+    /**
+     * This user-land implementation follows the implementation quite strictly;
+     * it does not attempt to improve the code or algorithm in any way. It will
+     * raise a warning if you have fewer than 2 values in your array, just like
+     * the extension does (although as an E_USER_WARNING, not E_WARNING).
+     * 
+     * @param array $a 
+     * @param bool $sample [optional] Defaults to false
+     * @return float|bool The standard deviation or false on error.
+     */
+    function stats_standard_deviation(array $a, $sample = false) {
+        $n = count($a);
+        if ($n === 0) {
+            trigger_error("The array has zero elements", E_USER_WARNING);
+            return false;
+        }
+        if ($sample && $n === 1) {
+            trigger_error("The array has only 1 element", E_USER_WARNING);
+            return false;
+        }
+        $mean = array_sum($a) / $n;
+        $carry = 0.0;
+        foreach ($a as $val) {
+            $d = ((double) $val) - $mean;
+            $carry += $d * $d;
+        };
+        if ($sample) {
+           --$n;
+        }
+        return sqrt($carry / $n);
+    }
+}
+
 /** Reset ********************************************************************/
 
 /**
