diff --git a/lang/en/vpl.php b/lang/en/vpl.php
index 3fc71bb31153fab287cb9af845fa541cea19fb57..a23c695fddbd86fcaa514fe50634f70198882ebc 100644
--- a/lang/en/vpl.php
+++ b/lang/en/vpl.php
@@ -128,6 +128,7 @@ $string ['gradeoptions'] = 'Grade options';
 $string ['grader'] = "Evaluator";
 $string ['gradercomments'] = 'Assessment report';
 $string ['graderemoved'] = 'The grade has been removed';
+$string ['graphcomputationfailed'] = 'This graph data could not be computed.<br>The files data may be too large.';
 $string ['groupwork'] = 'Group work';
 $string ['inconsistentgroup'] = 'You are not member of only one group (0 o >1)';
 $string ['incorrect_file_name'] = 'Incorrect file name';
diff --git a/similarity/diff.class.php b/similarity/diff.class.php
index 709df0850f57dc9fc2469a6040fecfb854ea8015..21d2ab501cedb8f6b15a6c74519c1492976a39be 100644
--- a/similarity/diff.class.php
+++ b/similarity/diff.class.php
@@ -406,7 +406,7 @@ class vpl_diff {
             return $n1 + $n2;
         }
         $queue = array_fill(0, $n1 + $n2 + 1, array());
-        $done = array_fill(0, $n1 + 1, array_fill(0, $n2 + 1, false));
+        $done = array_fill(0, $n1 + 1, array());
         $z = new stdClass();
         $z->i1 = 0;
         $z->i2 = 0;
@@ -422,7 +422,7 @@ class vpl_diff {
             $z = array_pop($queue[$priority]);
             $i1 = $z->i1;
             $i2 = $z->i2;
-            if ($done[$i1][$i2]) {
+            if (isset($done[$i1][$i2])) {
                 continue;
             } else {
                 $done[$i1][$i2] = true;
@@ -472,31 +472,112 @@ class vpl_diff {
      * Computes diff between two files.
      * @param string $filedata1
      * @param string $filedata2
+     * @param int $timelimit The maximum amount of time to spend on this computation (in milliseconds).
+     *  If provided and the time limit is exceeded, an exception will be thrown.
      * @return int The diff in number of chars.
      */
-    static public function compute_filediff($filedata1, $filedata2) {
+    static public function compute_filediff($filedata1, $filedata2, $timelimit=0) {
         if (strlen($filedata1) == 0 || strlen($filedata2) == 0) {
             return strlen($filedata1) + strlen($filedata2);
         }
+        $starttime = microtime(true);
         $lines1 = explode("\n", $filedata1);
         $lines2 = explode("\n", $filedata2);
-        $linesdiff = self::calculatediff($lines1, $lines2, false);
-        $totaldiff = 0;
-        foreach ($linesdiff as $diff) {
-            switch ($diff->type) {
-                case '=' :
-                    break;
-                case '<' :
-                    $totaldiff += strlen( $lines1[$diff->ln1 - 1] ) + 1;
-                    break;
-                case '>' :
-                    $totaldiff += strlen( $lines2[$diff->ln2 - 1] ) + 1;
-                    break;
-                default :
-                    $totaldiff += self::compute_linediff( $lines1[$diff->ln1 - 1], $lines2[$diff->ln2 - 1] );
-                    break;
+        $n1 = count($lines1);
+        $n2 = count($lines2);
+        $queue = array_fill(0, $n1 + $n2 + 1, array());
+        $prev = array_fill(0, $n1 + 1, array());
+        $z = new stdClass();
+        $z->i1 = 0;
+        $z->i2 = 0;
+        $z->d = 0;
+        $z->prev = 0;
+        $queue[0][] = $z;
+        $priority = 0;
+
+        // A-star search of shortest diff.
+        while (true) {
+            if ($timelimit > 0 && microtime(true)-$starttime > $timelimit/1000) {
+                // Time out.
+                throw new Exception();
+            }
+            while (count($queue[$priority]) == 0) {
+                $priority++;
+            }
+            $z = array_pop($queue[$priority]);
+            $i1 = $z->i1;
+            $i2 = $z->i2;
+            if (isset($prev[$i1][$i2])) {
+                continue;
+            } else {
+                $prev[$i1][$i2] = $z->prev;
+            }
+
+            // Identical substring: 0 distance.
+            while ($i1 < $n1 && $i2 < $n2 && $lines1[$i1] == $lines2[$i2]) {
+                $i1++;
+                $i2++;
+            }
+
+            if ($i1 == $n1 && $i2 == $n2) {
+                // Found shortest diff amongst lines.
+                // Backtrack to compute total diff as a sum of lines diff.
+                $totaldiff = 0;
+                while ($i1 > 0 && $i2 > 0) {
+                    if (!isset($prev[$i1][$i2])) {
+                        $i1--;
+                        $i2--;
+                    } else {
+                        switch ($prev[$i1][$i2]) {
+                            case 1 :
+                                $totaldiff += strlen( $lines1[$i1-1] ) + 1;
+                                $i1--;
+                                break;
+                            case 2 :
+                                $totaldiff += strlen( $lines2[$i2-1] ) + 1;
+                                $i2--;
+                                break;
+                            case 3 :
+                                $totaldiff += self::compute_linediff( $lines1[$i1 - 1], $lines2[$i2 - 1] );
+                            case 0 :
+                                $i1--;
+                                $i2--;
+                                break;
+                        }
+                    }
+                }
+                return $totaldiff;
+            }
+
+            // Line addition in file1.
+            if ($i1 < $n1) {
+                $z1 = new stdClass();
+                $z1->i1 = $i1 + 1;
+                $z1->i2 = $i2;
+                $z1->d = $z->d + 1;
+                $z1->prev = 1;
+                $queue[$z1->d + abs(($n1 - $z1->i1) - ($n2 - $z1->i2))][] = $z1;
+            }
+
+            // Line addition in file2.
+            if ($i2 < $n2) {
+                $z2 = new stdClass();
+                $z2->i1 = $i1;
+                $z2->i2 = $i2 + 1;
+                $z2->d = $z->d + 1;
+                $z2->prev = 2;
+                $queue[$z2->d + abs(($n1 - $z2->i1) - ($n2 - $z2->i2))][] = $z2;
+            }
+
+            // Line change.
+            if ($i1 < $n1 && $i2 < $n2) {
+                $z3 = new stdClass();
+                $z3->i1 = $i1 + 1;
+                $z3->i2 = $i2 + 1;
+                $z3->d = $z->d + 1;
+                $z3->prev = 3;
+                $queue[$z3->d + abs(($n1 - $z3->i1) - ($n2 - $z3->i2))][] = $z3;
             }
         }
-        return $totaldiff;
     }
 }
diff --git a/styles.css b/styles.css
index bbd9957565b6cba4b9b3d5c2ace787575cd31bdb..776b8efbd3c8168d28f496b08f21d82839a8f2db 100644
--- a/styles.css
+++ b/styles.css
@@ -8,6 +8,17 @@
     display: inline-block;
 }
 
+.path-mod-vpl.pagelayout-incourse .chart-area.nograph {
+    text-align: center;
+    vertical-align: top;
+    top: 2em;
+    position: relative;
+}
+
+.path-mod-vpl.pagelayout-incourse .chart-area.nograph b {
+    font-size: .9em;
+}
+
 .path-mod-vpl .chart-area canvas {
     width: 100%;
     height: 100%;
diff --git a/views/vpl_grapher.class.php b/views/vpl_grapher.class.php
index 4769428c36c1ba329eb6afdf08a8634950252f07..d5a5c35660ff21bcd72cab31b6e3b637d22dff0a 100644
--- a/views/vpl_grapher.class.php
+++ b/views/vpl_grapher.class.php
@@ -133,55 +133,62 @@ class vpl_grapher {
         if (count( $this->submissions ) == 0 || $this->userid === null) {
             return;
         }
-        $submissionslist = array_reverse( $this->submissions );
+        try {
+            $title = $this->username . ' - ';
+            if ($diff) {
+                $title .= get_string( 'filesdiffevolution', VPL );
+            } else {
+                $title .= get_string( 'filessizeevolution', VPL );
+            }
 
-        $interval = self::compute_xaxis_legend_interval(count( $submissionslist ), 20);
+            $submissionslist = array_reverse( $this->submissions );
+            $interval = self::compute_xaxis_legend_interval(count( $submissionslist ), 20);
 
-        $i = 1;
-        $subsn = array();
-        $series = array();
-        $names = array();
-        $totalseries = array();
-        if ($diff) {
-            $reqfiles = $this->vpl->get_fgm('required')->getAllFiles();
-        }
-        foreach ($submissionslist as $subinstance) {
-            $submission = new mod_vpl_submission( $this->vpl, $subinstance );
-            $files = $submission->get_submitted_files();
-            $total = 0;
-            foreach ($files as $name => $data) {
-                if (!isset($series[$name])) {
-                    $names[] = $name;
-                    $series[$name] = array_fill(0, $i - 1, null);
+            $i = 1;
+            $subsn = array();
+            $series = array();
+            $names = array();
+            $totalseries = array();
+            if ($diff) {
+                $reqfiles = $this->vpl->get_fgm('required')->getAllFiles();
+            }
+            foreach ($submissionslist as $subinstance) {
+                $submission = new mod_vpl_submission( $this->vpl, $subinstance );
+                $files = $submission->get_submitted_files();
+                $total = 0;
+                foreach ($files as $name => $data) {
+                    if (!isset($series[$name])) {
+                        $names[] = $name;
+                        $series[$name] = array_fill(0, $i - 1, null);
+                    }
+                    if ($diff && isset($reqfiles[$name])) {
+                        $value = vpl_diff::compute_filediff($reqfiles[$name], $data, 500);
+                    } else {
+                        $value = strlen( $data );
+                    }
+                    $series[$name][] = $value;
+                    $total += $value;
                 }
-                if ($diff && isset($reqfiles[$name])) {
-                    $value = vpl_diff::compute_filediff($reqfiles[$name], $data);
-                } else {
-                    $value = strlen( $data );
+                foreach ($series as &$serie) {
+                    if (count($serie) < $i) {
+                        $serie[] = null;
+                    }
                 }
-                $series[$name][] = $value;
-                $total += $value;
+                $totalseries[] = $total;
+                $subsn[] = $i % $interval == 0 ? $i : '';
+                $i++;
             }
-            foreach ($series as &$serie) {
-                if (count($serie) < $i) {
-                    $serie[] = null;
-                }
+            if ($diff && count($names) > 1) {
+                $names[] = get_string('total');
+                $series[get_string('total')] = $totalseries;
             }
-            $totalseries[] = $total;
-            $subsn[] = $i % $interval == 0 ? $i : '';
-            $i++;
-        }
-        if ($diff && count($names) > 1) {
-            $names[] = get_string('total');
-            $series[get_string('total')] = $totalseries;
-        }
-        $title = $this->username . ' - ';
-        if ($diff) {
-            $title .= get_string( 'filesdiffevolution', VPL );
-        } else {
-            $title .= get_string( 'filessizeevolution', VPL );
+            self::draw( $title, get_string( 'submissions', VPL ) , get_string( "sizeb" ), $subsn, $series, $names );
+        } catch (Exception $e) {
+            echo '<span class="chart-area nograph">';
+            echo '<b>' . $title . '</b><br>';
+            echo get_string('graphcomputationfailed', VPL);
+            echo '</span>';
         }
-        self::draw( $title, get_string( 'submissions', VPL ) , get_string( "sizeb" ), $subsn, $series, $names );
     }
 
     static protected function new_working_period($workduration, &$firstwork, $intervals) {