Skip to content

Commit f3f8c09

Browse files
author
Guillaume Crico
committed
Improve TimeEfficientImplementation performances.
Using a SplFixedArray seems to reduce memory allocation (-90% memory peak usage) and have greater lookup speed (+15%) than the two-dimension dynamic array storing the LCS matrix. It also seems that a SplFixedArray is wiped earlier by the PHP garbage collector. An other optimization: avoid usage of "array_unshift" (Pushing into the result array and reversing it at the end is faster in most cases). If this patch is OK, the "Differ->calculateEstimatedFootprint()" should be also adjusted, because the SplFixedArray memory cost seems to be completely different than the one of (partially filled) arrayXarray matrix. (+ Removed unclear comment in the TimeEfficientImplementationTest case.)
1 parent 0afc0ed commit f3f8c09

File tree

2 files changed

+23
-27
lines changed

2 files changed

+23
-27
lines changed

src/LCS/TimeEfficientLongestCommonSubsequenceImplementation.php

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,24 +66,26 @@ class TimeEfficientImplementation implements LongestCommonSubsequence
6666
public function calculate(array $from, array $to)
6767
{
6868
$common = array();
69-
$matrix = array();
7069
$fromLength = count($from);
7170
$toLength = count($to);
71+
$width = $fromLength + 1;
72+
$matrix = new \SplFixedArray($width * ($toLength + 1));
7273

7374
for ($i = 0; $i <= $fromLength; ++$i) {
74-
$matrix[$i][0] = 0;
75+
$matrix[$i] = 0;
7576
}
7677

7778
for ($j = 0; $j <= $toLength; ++$j) {
78-
$matrix[0][$j] = 0;
79+
$matrix[$j * $width] = 0;
7980
}
8081

8182
for ($i = 1; $i <= $fromLength; ++$i) {
8283
for ($j = 1; $j <= $toLength; ++$j) {
83-
$matrix[$i][$j] = max(
84-
$matrix[$i-1][$j],
85-
$matrix[$i][$j-1],
86-
$from[$i-1] === $to[$j-1] ? $matrix[$i-1][$j-1] + 1 : 0
84+
$o = ($j * $width) + $i;
85+
$matrix[$o] = max(
86+
$matrix[$o - 1],
87+
$matrix[$o - $width],
88+
$from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0
8789
);
8890
}
8991
}
@@ -93,16 +95,19 @@ public function calculate(array $from, array $to)
9395

9496
while ($i > 0 && $j > 0) {
9597
if ($from[$i-1] === $to[$j-1]) {
96-
array_unshift($common, $from[$i-1]);
98+
$common[] = $from[$i-1];
9799
--$i;
98100
--$j;
99-
} elseif ($matrix[$i][$j-1] > $matrix[$i-1][$j]) {
100-
--$j;
101101
} else {
102-
--$i;
102+
$o = ($j * $width) + $i;
103+
if ($matrix[$o - $width] > $matrix[$o - 1]) {
104+
--$j;
105+
} else {
106+
--$i;
107+
}
103108
}
104109
}
105110

106-
return $common;
111+
return array_reverse($common);
107112
}
108113
}

tests/LCS/TimeEfficientImplementationTest.php

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -191,21 +191,12 @@ public function testSingleElementSubsequenceAtEnd()
191191

192192
public function testReversedSequences()
193193
{
194-
// The values are unique and the second sequence is the reverse
195-
// of the first sequence.
196-
// The LCS must return a single value: the first value of the
197-
// first sequence (which is the same than the last value of the
198-
// second sequence).
199-
//
200-
// I don't know --yet-- if it is really important!
201-
// As the values are unique, both results are "longest common
202-
// sequence"! In fact, any value from teh set would be valid!
203-
//
204-
// And that too bad, because if we were allowed to return the
205-
// first value of the first sequence, it would be much faster!
206-
// (by building the matrix backward and gathering the common
207-
// items in the original direction.)
208-
//
194+
$from = array('A', 'B');
195+
$to = array('B', 'A');
196+
$expected = array('A');
197+
$common = $this->implementation->calculate($from, $to);
198+
$this->assertEquals($expected, $common);
199+
209200
foreach ($this->stress_sizes as $size) {
210201
$from = range(1, $size);
211202
$to = array_reverse($from);

0 commit comments

Comments
 (0)