Porting C++ project from VS 6.0 to VS 2010 brought to slower code

Discussion Porting C++ project from VS 6.0 to VS 2010 brought to slower code

  • samedi 3 mars 2012 06:48
     
      A du code

    I ported one project from Visual C++ 6.0 to VS 2010 and found that a critical part of the code (scripting engine) now runs in about three times slower than in was before.
    After some research I managed to extract code fragment which seems to cause the slowdown. I minimized it as much as possible, so it ill be easier to reproduce the problem.
    The problem is reproduced when assigning a complex class (Variant) which contains another class (String), and the union of several other fields of simple types.

    Playing with the example I discovered more "magic":
    1. If I comment one of unused (!) class members, the speed increases, and the code finally runs faster than those complied with VS 6.2
    2. The same is true if I remove the "union" wrapper"
    3. The same is true event if change the value of the filed from 1 to 0

    I have no idea what the hell is going on.
    I have checked all code generation and optimization switches, but without any success.

    The code sample is below:
    On my Intel 2.53 GHz CPU this test, compiled under VS 6.2 runs 1.0 second.
    Compiled under VS 2010 - 40 seconds
    Compiled under VS 2010 with "magic" lines commented - 0.3 seconds.

    The problem is reproduces with any optimization switch, but the "Whole program optimization" (/GL) should be disabled. Otherwise this too smart optimizer will know that out test actually does nothing, and the test will run 0 seconds.

    #include		<windows.h>
    #include		<stdio.h>
    #include		<stdlib.h>
    
    class String
    {
    public:
    	char	*ptr;
    	int		size;
    
    	String() : ptr(NULL), size( 0 ) {};
    	~String() {if ( ptr != NULL ) free( ptr );};
    	String& operator=( const String& str2 );
    };
    
    String& String::operator=( const String& string2 )
    {
    	if ( string2.ptr != NULL )
    	{
    		// This part is never called in our test:
    		ptr = (char *)realloc( ptr, string2.size + 1 );
    		size = string2.size;
    		memcpy( ptr, string2.ptr, size + 1 );
    	}
    	else if ( ptr != NULL )
    	{
    		// This part is never called in our test:
    		free( ptr );
    		ptr = NULL;
    		size = 0;
    	}
    
    	return *this;
    }
    
    
    struct Date
    {
    	unsigned short			year;
    	unsigned char			month;
    	unsigned char			day;
    	unsigned char			hour;
    	unsigned char			minute;
    	unsigned char			second;
    	unsigned char			dayOfWeek;
    };
    
    
    class Variant
    {
    public:
    	int				dataType;
    	String			valStr; // If we comment this string, the speed is OK!
    	
    	// if we drop the 'union' wrapper, the speed is OK!
    	union
    	{
    		__int64		valInteger;
    
    		// if we comment any of these fields, unused in out test, the speed is OK!
    		double		valReal;
    		bool		valBool;
    		Date		valDate;
    		void		*valObject;
    	};
    
    	Variant() : dataType( 0 ) {};
    };
    
    
    void TestSpeed()
    {
    	__int64				index;
    	Variant				tempVal, tempVal2;
    
    	tempVal.dataType = 3;
    	tempVal.valInteger = 1; // If we comment this string, the speed is OK!
    
    	for ( index = 0; index < 200000000; index++ )
    	{
    		tempVal2 = tempVal;
    	}
    }
    
    int main(int argc, char* argv[])
    {
    	int			ticks;
    	char		str[64];
    
    	ticks = GetTickCount();
    
    	TestSpeed();
    
    	sprintf( str, "%.*f", 1, (double)( GetTickCount() - ticks ) / 1000 );
    	
    	MessageBox( NULL, str, "", 0 );
    
    	return 0;
    }
    
    
    



    • Type modifié Helen ZhaoModerator mardi 6 mars 2012 06:21 for more ideas from more members
    •  

Toutes les réponses

  • samedi 3 mars 2012 18:21
     
     

    Consider these workarounds: move the union to a new structure and make it the base of Variant, or define a separate union and use it as a member of Variant.

    Check if it works better in new Visual Studio 11 Beta.


  • mardi 6 mars 2012 06:20
    Modérateur
     
     

    Hi Boris,

    I have been watching this issue for a while now. And I think there is no exact answer for it. For more ideas from more members, I'd like to change the thread type to "General Discussion".

    Thanks for your understanding.
    Helen Zhao


    Helen Zhao [MSFT]
    MSDN Community Support | Feedback to us

  • lundi 12 mars 2012 14:39
     
     
    The solution has been found.

    The problem was in default copy constructor generated by the compiler for "union" member.
    VC++ 6.0 performs byte-to-byte copying, which is logical, because union members cannot have constructors.
    VC++ 2010, seeing that the union contains float member, adds additional copying instruction for this float member (FLD, FSTP).

    Since in my example the union uses only __int64 member, the float member contains some denormalized junk value, which causes slow copying.
    This explains why setting __int64 member to zero speeds up execution.

    I have found that /fp:strict compiler options solves the problem. Writing own copy constructor which do byte-to-byte copying of the union, also solves the problem.

    This is similar to to this problem
    http://connect.microsoft.com/VisualStudio/feedback/details/238546/incorrect-code-generation-for-union-copy-assignment-in-visual-studio-2005-c-compiler
    but I am nor exactly sure.