Opened 15 years ago

Last modified 14 years ago

#1670 reopened Bugs

wave almost unusably slow on real-world input

Reported by: Stefan Seefeld Owned by: Hartmut Kaiser
Milestone: Boost 1.36.0 Component: wave
Version: Boost Development Trunk Severity: Problem
Keywords: Cc:

Description

I'm running a wave-based preprocessor on boost/python.hpp as part of document generation (Synopsis). The preprocessing alone takes >6 hours ! (The subsequent parsing with Synopsis' own C++ parser is then relatively quick, surprisingly.

I notice that wave seems to get increasingly slow, so I wonder whether the speed decrease is caused by some internal lookup on a growing dictionary (map) that has super-linear lookup time ?

Attachments (2)

wave.py (449 bytes ) - added by Stefan Seefeld 15 years ago.
Emulator.py (12.6 KB ) - added by Stefan Seefeld 15 years ago.

Download all attachments as: .zip

Change History (17)

comment:1 by Hartmut Kaiser, 15 years ago

This apparently is not a Wave problem. I tried to preprocess python.hpp using the Wave commandline tool and it needed about 35 seconds to fully preprocess this file (full optimization) and about 4 minutes with optimizations off.

comment:2 by Hartmut Kaiser, 15 years ago

Resolution: worksforme
Status: newclosed

comment:3 by Stefan Seefeld, 15 years ago

Thanks for the quick reply. I'll profile with my own trace policy to see where it spends all this time.

comment:4 by Stefan Seefeld, 15 years ago

Resolution: worksforme
Status: closedreopened

What were the flags you preprocessed this file with ? Which compiler were you emulating ? I noticed it makes quite a difference what macros are predefined, presumably because certain chunks of (boost) code are only enabled with the appropriate macro definitions. It seems entirely possible that large chunks of boost code are simply skipped as the presumed compiler represented by wave was not detected as 'supported'.

Please find attached two python scripts that help you run wave in 'compiler emulation' mode. (Please make some obvious adjustments to the code to make it work for you.)

I run it as 'python wave.py -S /usr/include/python2.5 -S boost boost/boost/python.hpp'.

calling the wave applet without any compiler emulation flags takes indeed only a couple of seconds. However, using flags to emulate GCC (on my Fedora 8 laptop) makes it run much longer (in fact, it is still running, so I can't tell you the total time.) (I notably see wave spend quite some time in boost's funny preprocessor loop construct, which is used in different places.)

I'd be happy to contribute this code (with some suitable changes) to the boost.wave project. I'm sure others may find it useful, too.

Here is how it works:

The wave.py file calls Emulator.get_compiler_info('C++', 'c++') to find compiler-specific (system) search paths as well as predefined macros. (The Emulator module will use some heuristics to query those flags for compilers it knows, such as GCC and cl. Those data then get cached under ~/.synopsis...)

by Stefan Seefeld, 15 years ago

Attachment: wave.py added

by Stefan Seefeld, 15 years ago

Attachment: Emulator.py added

comment:5 by Ainsley Pereira <boostbug@…>, 14 years ago

I did some profiling on the wave preprocessor in tools, and discovered that over 71% of its time was spent getting thread specific storage for code in spirit/phoenix/closures.hpp. (Another 4% was spent setting tss data.) It seems to be possible to define BOOST_WAVE_THREADING_SUPPORT to 0, but my bjam skills failed me and I was unable to build/link it with that to retest. I hope the information is useful to someone who knows more about the spirit closures stuff.

comment:6 by jordi@…, 14 years ago

Maybe our experience can bring some useful information to this bug, and it could locate some critical issue maybe in another boost library:

We met a similar problem: in our program we process around a 1000 files with Wave and custom callbacks and it was taking much more than expected. We profiled and found out that it spent 95% of time traversing the thread-specific pointer list, in find_tss_data.

A more restricted test, compiling 6 batches with 30 times the same file was surprisingly giving higher time on each successive batch. We checked that we were not leaking any resources, and we are not doing strange things. We disabled threading support in Wave and that fixed the problem, making it the same time for each batch as it should. The real world case moved from 40 minutes to 4 in some cases.

My conclusion is that something is not well managed with the thread specific pointers either in WAVE, or in SPIRIT or in BOOST::THREAD itself. This could be potentially a critical bug if that is the case, and should be scaled down to the suitable libraries.

By the way, to disable threading I added this at the top of wave_config.hpp, which is probably not a good idea:

#define BOOST_WAVE_SUPPORT_THREADING 0

Platform is WinXP. Verified with both 1.36.0 and 1.37.0 versions.

I hope this helps and thanks again for this great library.

jordi

comment:7 by Hartmut Kaiser, 14 years ago

Thanks for this information. The suspicion this has something to do with the threading support (in Phoenix and Spirit) is not new. The best way to handle this (for now) is apparently to disable threading support for Wave (as long as you don't really need it, and most users won't) the way you did: #define BOOST_WAVE_SUPPORT_THREADING 0.

Regards Hartmut

comment:8 by anonymous, 14 years ago

Is this a flag that needs to be set prior to compiling boost, or is it enough to issue it in user code (prior to including boost.wave headers) ?

Thanks,

Stefan

in reply to:  8 comment:9 by Hartmut Kaiser, 14 years ago

Replying to anonymous:

Is this a flag that needs to be set prior to compiling boost, or is it enough to issue it in user code (prior to including boost.wave headers) ?

Actually, both. You need to specify consistent setting while compiling the Wave libraries and the application using Wave. Either directly change the wave_config.hpp or specify the settings on the command line/bjam.

Regards Hartmut

comment:10 by anonymous, 14 years ago

Ah, that's too bad. While I can certainly do that for experimental purposes, I don't expect my (Synopsis') users to do it; they are simply expected to have boost installed (most likely as a system package, such as from Fedora or debian).

I don't know the boost.wave design well enough to be able to suggest anything at this point, but I certainly hope that there are (will be) ways for users only to 'pay what they need'.

Thanks,

Stefan

in reply to:  10 comment:11 by Hartmut Kaiser, 14 years ago

Replying to anonymous:

Ah, that's too bad. While I can certainly do that for experimental purposes, I don't expect my (Synopsis') users to do it; they are simply expected to have boost installed (most likely as a system package, such as from Fedora or debian).

I don't know the boost.wave design well enough to be able to suggest anything at this point, but I certainly hope that there are (will be) ways for users only to 'pay what they need'.

Actually, by default everything depends on your project settings. If you enable threading for your application, Wave picks that up too and will be built with threading enabled. If the application settings don't include threading, you'll get the Wave library with threading disabled. Really, I see no way to make that more flexible by default, do you?

Regards Hartmut

comment:12 by anonymous, 14 years ago

I'm not sure what you mean by 'project settings' (I can guess, though ;-) ).

When I'm distinguishing between library and application compile-time I mean this: When setting the above macro to a specific value, do I need to recompile boost.wave itself, or is it enough to compile my own code ? Are the libboost_wave libraries dependent on this setting ? (You seemed to suggest the answer to this question is 'yes', while I'm hoping for a 'no'.) Or may be libboost_wave_mt uses TLS, while libboost_wave does not ?

Thanks,

Stefan

in reply to:  12 comment:13 by Hartmut Kaiser, 14 years ago

Replying to anonymous:

I'm not sure what you mean by 'project settings' (I can guess, though ;-) ).

I meant the set of command line parameters used for compiling your application.

When I'm distinguishing between library and application compile-time I mean this: When setting the above macro to a specific value, do I need to recompile boost.wave itself, or is it enough to compile my own code ? Are the libboost_wave libraries dependent on this setting ? (You seemed to suggest the answer to this question is 'yes', while

The settings used for the application should match the settings used for compiling the libraries.

I'm hoping for a 'no'.) Or may be libboost_wave_mt uses TLS, while libboost_wave does not ?

Yes that describes it best.

Regards Hartmut

comment:14 by anonymous, 14 years ago

Does boost provide a facility to query those settings ? Are the relevant settings documented somewhere ?

Also, not all macros affect the compiled library itself, IIUC, so I believe it's important to make that clear. I'm for example thinking of BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS.

Thanks,

Stefan

in reply to:  14 comment:15 by Hartmut Kaiser, 14 years ago

Replying to anonymous:

Does boost provide a facility to query those settings ? Are the relevant settings documented somewhere ?

Which settings? Whether threading is enabled or not? That's BOOST_HAS_THREADS, IIRC.

Also, not all macros affect the compiled library itself, IIUC, so I believe it's important to make that clear. I'm for example thinking of BOOST_WAVE_USE_DEPRECIATED_PREPROCESSING_HOOKS.

Ok, will try to come up with a list of settings required to match.

On a second thought: you always can use the non-threaded Wave library as long as your application defines BOOST_WAVE_SUPPORT_THREADING=0. But you'll have to make sure not use Wave from different threads at the same time.

Regards Hartmut

Note: See TracTickets for help on using tickets.