0

The following piece of code takes ~700 ms to run in Debug mode (Visual Studio 2017). I have read numerous questions on how to speed it up, but the majority of things I tried does not seem to have any effect.

By bisecting compiler flags, I found that the main slowdowns result from from /RTC1//RTCs (vs. /RTCu) and from /Od (vs. /O2). (I previously stated in the comments that they did not have a big effect, but I had been running the Debug/x86 configuration while modifying the Debug/x64 configuration...) _ITERATOR_DEBUG_LEVEL has a measurable, but negligible effect, and I don't worry too much about the remaining difference to Release mode.

My aim is to modify just a single piece of code (ideally, a function with just the std::equal call) that is not critical for routine debugging. I have been able to bring runtimes down "locally" (without changing global project options) by #pragma runtime_checks, basically countering /RTCs; but the same won't work for #pragma optimize, it seems.

// Debug (Default, /Od and /RTC1): 700 ms
// Debug (/Od /RTCs): 700 ms
// Debug (/Od /RTCu): 200 ms
// Debug (/Od /RTC1 with #pragma runtime_checks at main): 440 ms
// Debug (/Od /RTC1 with #pragma runtime_checks at top): 220 ms
// Debug (/Od): 200 ms
// Debug (/O2): 60 ms
// Debug (/O2 with _ITERATOR_DEBUG_LEVEL 0): 50 ms
// Release: 20 ms

#pragma runtime_checks( "s", off )
#pragma optimize( "gsy", on )
#define _ITERATOR_DEBUG_LEVEL 0

#include <algorithm>
#include <chrono>
#include <iostream>
#include <fstream>

using namespace std;
using clk = chrono::steady_clock;

// #pragma runtime_checks( "s", off )
int main()
{
  const ifstream is(R"(C:\Windows\explorer.exe)", ios::binary);

  auto begin = clk::now();
  equal(
    istreambuf_iterator<char>(is.rdbuf()),
    istreambuf_iterator<char>(),
    istreambuf_iterator<char>(is.rdbuf())
  );
  auto end = clk::now();

  cout << chrono::duration_cast<chrono::milliseconds>(end - begin).count();
  cout << " ms" << endl;
}

The documentation for #pragma runtime_checks says

You can't enable a run-time check that wasn't enabled by a compiler option.

I suspect something similar holds for #pragma optimize, in the sense that you cannot enable optimization that wasn't enabled by a compiler option. Could this be true? Is there anything else I can try to enforce optimization locally (apart from putting the function in its own file and configuring compiler options in the properties, which does work)?

bers
  • 4,817
  • 2
  • 40
  • 59
  • 1
    There's nothing to try. It is what it is. – Sam Varshavchik Aug 17 '22 at 12:29
  • 8
    Debug mode isn't for speed, it's for debugging. Why do you want a fast debug build? – NathanOliver Aug 17 '22 at 12:29
  • @NathanOliver Clearly someone envisaged `_ITERATOR_DEBUG_LEVEL` to trade off debug depth vs speed. I am looking to debug some code without the abysmal increase in run time, for example, in order to *reach* the code I actually want to debug in the first place. – bers Aug 17 '22 at 12:33
  • 2
    *The following piece of code takes 700 ms to run* -- Unless you're the superhero "The Flash", 700 ms is nothing in human terms. – PaulMcKenzie Aug 17 '22 at 12:40
  • 3
    You could put the code you need to debug in it's own file and set that file not to be optimized while having the rest of your files optimized: https://stackoverflow.com/q/16515165/4342498 – NathanOliver Aug 17 '22 at 12:41
  • 1
    Another option is to build a [mre] around the code you want to debug so you are just working with the code you want to test. – NathanOliver Aug 17 '22 at 12:42
  • Make sure the release build is generating a PDB and debug the release build. – Richard Critten Aug 17 '22 at 12:51
  • @PaulMcKenzie that's funny (for some), but not helpful. Extrapolating the runtime of my code means it takes more than a minute to compare 1 GB worth of files. – bers Aug 17 '22 at 12:57
  • @bers -- 1) Why is your sample file when run under debug so large? Is there something buggy in processing large files? 2) You could always turn off the optimizations in release mode and debug the release build (assuming this is Visual C++), – PaulMcKenzie Aug 17 '22 at 13:00
  • @PaulMcKenzie 1) this is a unit test that happens to compare large files. When run in Debug mode, it simply shouldn't take ages (well, I'm not saying it should never, but I am looking for a way to avoid it). The size of the file has nothing to do with Debug/Release. – bers Aug 17 '22 at 13:07
  • @PaulMcKenzie 2) well, I can always do things differently, but suppose I still want my `assert`s active when debugging, etc. – bers Aug 17 '22 at 13:07
  • @bers -- [How to put assert in release builds](https://stackoverflow.com/questions/620892/how-to-put-assert-into-release-builds-in-c-c) – PaulMcKenzie Aug 17 '22 at 13:09
  • If you want to reach the code you want to debug isolate it into an object, inject mocks/stubs for its environment and unit test all the possible flows and different input scenarios for that function. Then if it passes all the tests call it from your loops, and start doing integration tests. Its called test driven development and it really helps. – Pepijn Kramer Aug 17 '22 at 13:44
  • @PaulMcKenzie I appreciate your efforts, but we are moving further and further from the original question. I am aware of the XY problem, but my main goal here - in the context of a big, multi-project, multi-solution code base - is to make a few local changes for a single function to run fast in debug mode. Enabling `assert`s in release builds is not an option unfortunately. – bers Aug 18 '22 at 07:22

2 Answers2

2

First of all, never put definitions of _HAS_ITERATOR_DEBUGGING or other standard library configuration macros in a source file. Put them in your project options. Otherwise you'll get into ODR errors (which MSVC is decently good at catching, but no point in tempting fate).

But to your main question, a lot of MSVC's standard library debug checks -- especially those related to iostreams -- are not controllable via macros, because they exist in pre-built libraries. It comes down to whether you're linking with the debug or release std library, rather than to what the header files see. The standard library LIBs/DLLs don't even know whether you had iterator debugging enabled, because they were compiled without the benefit of your #defines.

So if your slowdown is in the debug build of the standard library, there's nothing you can do about it except switch to the non-debug build of the standard library. (Which, BTW, will work even in debuggish configurations, with optimizations disabled and other debug-friendly compilation options set.)

Sneftel
  • 40,271
  • 12
  • 71
  • 104
  • I was inclined to agree with you, but I have found that I was measuring runtime incorrectly. Subsequently, I found the main contributors to performance are not linker options, but indeed compiler options - of which one can, and the other might, be controlled by `#pragma`s. Please see my updated question. – bers Aug 18 '22 at 07:23
1

"/Od versus /O2".

That's not one change, that's a handful of changes. In particular, I find that inlining /Ob, which is included in /O2 makes a big difference. But it does not affect linking - where a function is inlined, there's simply no work left for the linker.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • That's a useful finding. However, I have difficulties finding an appropriate `#pragma` for this in MSVC (such as `#pragma inline`). – bers Aug 18 '22 at 08:41
  • 1
    @bers: You can conditionally set `inline_depth` to 0 for the region you don't want inlined. – MSalters Aug 18 '22 at 10:26
  • Similar to the other options, however, I can only add inlining via `/Ob` and then disable it for everything else. It seems like there is no way to enable it for only a single region without `/Ob`, compare https://learn.microsoft.com/en-us/cpp/preprocessor/inline-depth?view=msvc-170 – bers Aug 18 '22 at 10:49