GDC compiler settings on codechef

Currently codechef provides GDC 6.3 as the only implementation of the D programming language. This version had been released in 2016 and nowadays is missing some of the useful language features. Upgrade to a more recent version would be very much appreciated. But apparently this is not the only problem.

During the latest contest, doing binary search suffered from TLE and this forced me to do some investigation. I suspect that there may be something wrong with the used optimization settings for the GDC compiler on the codechef platform. The performance of the following reduced code snippet is very bad:

import std.algorithm, std.conv, std.range, std.stdio, std.string, std.stdint;

// calculate integer square root using binary search
static int64_t isqrt(int64_t x) {
  return iota(0, min(x, 3037000499) + 1).map!(v => (v * v > x)).assumeSorted.lowerBound(true).length - 1;
}

void main() {
  // calculate integer square roots for all numbers in the [0, 2000000) range and print their sum
  2000000.iota.map!isqrt.sum.writeln;
}

If I paste it to codechef ide and run, then the execution time is around 1.3 seconds. If I paste it to AtCoder custom test page and run, then the execution time is only around 0.15 seconds. Yes, the version of GDC is different and AtCoder is using different hardware, but almost an order of magnitude difference still looks abnormal.

So in order to do an additional test, I have installed an old version of Debian Linux (so that it has the same GDC 6.3 as codechef) on my old laptop with a 2GHz Intel Core2 Duo processor and also tried to benchmark this code snippet there:

  • If I compile this code without any optimizations by running “gdc test.d”, then the execution time is 2.2 seconds.
  • If I compiler this code with -O2 optimizations by running “gdc -O2 -frelease test.d” (the same optimization settings as used by AtCoder), then the execution time reduces to 0.36 seconds.

This makes me think that codechef probably doesn’t enable optimizations for GDC. And if this is the case, then it is very unfortunate because the performance difference may be gigantic. And writing code in functional style apparently takes the biggest hit. I already encountered a very strange performance issue earlier (TLE vs. AC), which fortunately could be workarounded by rewriting the array printing code in imperative style during that contest.

If the root cause is indeed just a missing “-O2” compiler option for GDC, then I believe that the problem can be resolved very easily if @admin takes a look at it.

All existing compiler flags are mentioned in Are any compiler flags set on the online judge?

Since D isn’t listed there, it does mean that we don’t pass any flags for it as of now. But we’ve noted your points, and in the next iteration of language updates, we’ll make it a point to introduce this.

Thanks! How long does it usually take until the next iteration of language updates? The comment about the status of programming languages and compilers was last updated in 2017. I can see that in 2019 there was a report about Rust compiler not enabling optimizations, but nobody replied in the forum. Running the test program from that forum post shows that the Rust optimizations issue is resolved now.

I have implemented a configuration sanity checker for D language too, which can be run in the codechef IDE mode:

import std.stdio, std.range, std.algorithm;

// Optimizing compilers should do tail call optimization here, so
// 0xBADF00D magic constant won't be found in stack if optimizations
// are enabled. Is there a cleaner way to detect optimizations?
bool detect_opt() {
  int[100] filler;
  bool detect_tail_call_opt(int depth, int magic) {
    if (depth > 100) {
      int x; foreach (i ; 20 .. 80) if (*(&x + i) == magic) return false;
      return true;
    }
    return detect_tail_call_opt(depth + 1, magic);
  }
  return filler[0] || detect_tail_call_opt(0, 0xBADF00D);
}

// GDC11 was the first version to start supporting getTargetInfo traits
bool detect_gdc11() {
  version(GNU) { return __traits(compiles, __traits(getTargetInfo, "cppStd")); }
  else return false;
}

void main() {
  bool assert_on = false, optimizations_on = detect_opt();
  version(assert) { assert_on = true; }
  string[] warnings;
  version(GNU) {
    writeln("Detected compiler: GDC");
    if (!optimizations_on)
      warnings ~= "Performance warning: '-O3' option was not used!";
    if (assert_on)
      warnings ~= "Performance warning: '-frelease' option was not used!";
    if (detect_gdc11())
      warnings ~= "Note: GDC11 or newer may need '-flto' option, see https://gcc.gnu.org/PR102765";
  } else version(LDC) {
    writefln("Detected compiler: LDC (optimizing for %s)", __traits(targetCPU));
    if (!optimizations_on)
      warnings ~= "Performance warning: '-O' option was not used!";
    if (assert_on)
      warnings ~= "Performance warning: '-release' option was not used!";
    if (__traits(targetCPU) == "pentium4")
      warnings ~= "Performance warning: '-mcpu=native' option was not used!";
  } else version(DigitalMars) {
    writeln("Detected compiler: DMD");
    if (!optimizations_on)
      warnings ~= "Performance warning: '-O' option was not used!";
    if (assert_on)
      warnings ~= "Performance warning: '-release' option was not used!";
    warnings ~= "Performance warning: DMD generates much slower code than GDC or LDC!";
  } else {
    warnings ~= "Unknown compiler";
  }
  if (size_t.sizeof < 8)
    warnings ~= "Performance warning: not a 64-bit compiler!";
  if (warnings.empty)
    writeln("Everything seems to be properly configured.");
  else
    writeln(warnings.joiner("\n"));
}

Would it be possible to just quickly add “-O3 -frelease” options to the command line of your GDC 6.3 compiler?

There is no time limit multiplier for D and it’s treated as a high performance optimizing compiler with the same time limits as C++. Also looks like I’m the only remaining user of the D language on the codechef platform at the moment (based on checking submissions for a few recent contests). So the risk of making the users unhappy by breaking something is pretty low.

We hope to have the next iteration in the next month or two.
Will check if we can make the -O2 change sooner, but not sure.

Thanks! Looking forward to this next iteration. Just a few final notes:

  • Regarding “-O2” vs. “-O3”. In most cases the performance difference is very small and either of these options may be slightly faster. But -O3 is generally a better choice on average. And in some cases -O3 produces significantly faster code (when autovectorization kicks in or when more aggressive unrolling/inlining pays off). The users of the C++ language can freely override the compiler command line settings via GCC pragmas and get access to “-O3”. There’s unfortunately no way to do something like this with GDC 6.3 and that’s why having optimal command line options does matter a lot. If you have reliability concerns, then Google Kick Start already uses -O3 for their GDC compiler without any problems since a very long time ago.

  • Regarding “-frelease”. This option is also very important for optimal performance, primarily because it disables arrays bounds checking. Bounds checking can be rather expensive when arrays indexing is done a lot in a submitted solution. The difference between C++ and D languages on this matter:

    • When using C++, we have performance by default and safety checks can be optionally enabled via “-fsanitize=undefined -fsanitize=address”.
    • When using D, we have safety checks by default and performance can be optionally enabled via “-frelease”.
  • Regarding the latest version of the GDC compiler (GDC 11.2.0). If you happen to consider using it on your platform after the next compilers upgrade, then one more command line option would be necessary to avoid a performance disaster. The exact details are here. GDC versions up to and including 10 are fine. And a modern LLVM based LDC compiler is a great alternative too.

I’m ready to answer any questions and help with testing.

I see that Rust is going to get an update soon. It would be great if @hrishik85 could confirm whether upgrading D compiler is also planned by the end of June. Thanks!

Thanks to the recent compilers upgrade, now CodeChef is using GDC 11.1.0 instead of GDC 6.3 for judging solutions. This is good, because newer language features are supported. But the compiler is still used without any optimization options at all, which causes severe performance penalties and a major risk of TLE when solving problems with tight time limits.

When run in the CodeChef IDE, the following diagnostics program reports:

Detected compiler: GDC (frontend v2.76)
Performance warning: '-O2' or '-O3' option was not used!
Performance warning: '-frelease' option was not used!
Performance warning: '-flto' or '-fno-weak-templates' option was not used!
Note: see https://gcc.gnu.org/PR102765

And as a more practical example, one of the fastest C++ solutions for the problem INTCONCA can be directly converted to D language with only a few cosmetic adjustments:

With proper optimization settings for both C++ and D and using the same version of GCC/GDC, these two submissions should have nearly identical performance (confirmed on my computer). GCC and GDC are using the same code generation backend after all. The almost 4x time difference is caused by having no optimization enabled for GDC on CodeChef.

Is it possible to finally resolve this problem?

Do you have any updates on this?