[Megoldva] Valami hasznalhato compiler a loser g++ helyett linuxra?

Fórumok

Fejlesztgetek 1 programot, szem elott tartva hogy platformfuggetlen legyen. Tegnap az egyik modul stresztesztjet leforditottam linuxra is, es nehany overflow fixalasa utan (fokepp a CLOCKS_PER_SEC es a RAND_MAX nem kis kulonbsegebol adodtak) nagyon nem jo eredmenyek jottek ki gcc-vel forditva.

Optimalizacio nelkul jobb a gcc:
(mivel a tesztprogram random adatokkal tesztel tobb menetben igy a legjobb-legrosszabb futasi idoket adom meg)
MSVC: 4938-5219 ms (/O0)
gcc: 3510-2970 ms (-O0)

Optimalizacioval:
MSVC: 32-31 ms (/O2 /Ot)
gcc: 260-300 ms (-O3 -march=k8)

A kerdes gondolom adott:
En cseszek el valamit nagyon a gcc-s forditasnal, vagy a gcc optimalizacioja sikerult ennyire gyengere?
Amennyiben az elobbi, akkor mit rontok el?
Amennyiben az utobbi, akkor mit hasznaljak gcc helyett?

Meg annyi hogy a modul intenziven hasznal stl::list-et, szoval akar a linuxos (default) stl is lehet a hunyo.

Hozzászólások

Hát így elég nehéz bármit is mondani, de javasolnám a gprof használatát, azzal eléggé jól le lehet követni, hogy hol fáj a programnak. Én megnézném, hogy a gcc-s fordításnál az összes debug (pl. -g -ggdb, stb.) és profiling opció ki van-e kapcsolva. Az STL-nek is van egy előredefiniált preprocesszor makrója (amit fejből meg nem mondok, hogy mi a neve), ami megmondja, hogy kell-e ellenőrzéseket fordítani a template-ek kódjaiba vagy sem. Az is rengeteget számít.

Tapasztalataim szerint a gcc által produkált kód alapesetben nem sokkal (pár százalék) marad el az MS-es fordítóétól. Nagyobb ez a különbség, ha az msc-nél használsz profile-guided optimizationt (+10..20%). Ilyet a gcc is tud, csak én még nem láttam működni. :-/

-------------------------------------------------
" - Amerikanische Infanterie! Angriff! Angriff! "

A makefile igy nez ki:


test: gfx.o svgwriter.o rects.o
	g++ -o test gfx.o svgwriter.o rects.o

gfx.o: gfx.cpp
	g++ -c $(CFLAGS) gfx.cpp

svgwriter.o: svgwriter.cpp
	g++ -c $(CFLAGS) svgwriter.cpp

rects.o: rects.cpp
	g++ -c $(CFLAGS) rects.cpp

clean:
	rm *.o||true
	rm test||true

all: test

CFLAGS-et a make-nek adom at forditaskor:
make CFLAGS="-O3 -march=k8"

Az STL-nek is van egy előredefiniált preprocesszor makrója (amit fejből meg nem mondok, hogy mi a neve), ami megmondja, hogy kell-e ellenőrzéseket fordítani a template-ek kódjaiba vagy sem. Az is rengeteget számít.

http://gcc.gnu.org/onlinedocs/libstdc++/17_intro/howto.html

ennyit talaltam, ott van vagy 5 makro amivel be lehet kapcsolni bizonyos debug dolgokat. Alapbol mind undefined, en meg nem definialom oket.

gprof-ot meg nem probaltam, nem tudom mennyire segitene, MSVC-ben sem trukkoztem semmit, a kod megis majd' 10x gyorsabb, pedig ott meg architekturat se nagyon lehet megadni.

Eselteg az alábbi kapcsolókat próbáld meg, hátha valamelyik segít:

-fomit-frame-pointer
-fno-gcse
-fforce-mem
-malign-functions=4
-mpreferred-stack-boundary=4

Amúgy érdemes gprof-al megnézni, mert nem bonyolult, és cpu időigény szerint ad egy listát, amiből azonnal látod, hogy hol a gond. Nem kell több nap megérteni.

-------------------------------------------------
" - Amerikanische Infanterie! Angriff! Angriff! "

Gondolom az nem kérdés, hogy szükséged van-e egyáltalán az optimalizációból adódó sebességelőnyre?

icc talán.

Milyen gcc milyen OS mutat ilyet ?
Nem induláskori az az idővesztés ?

nem. a merest maga a szoftver vegzi ott, ahol az idoigenyes tevekenyseg indul. szoval nem time ./test, hanem a kodban van idolekeres a fo loop indulasa elott es utan. ott meg linuxon a kernel tick hatarozza meg a legkisebb merheto egyseget, ami 1/250-ed vagy 1/300-ad sec, mar nem emlekszem mire van allitva a kernelparameterekben a tick. Winen meg performance counterrel mer a program, ami orajel nagysagrend.
Magyaran a mert ertek nagyon messze esik a meresi hiba hataratol. Raadasul a gepek amiken probaltam 2 magos procival futnak, ugyhogy eleg jo esely van ra hogy a


#ifdef WIN32
	unsigned long long	starttime;
	QueryPerformanceCounter( (LARGE_INTEGER*)&starttime );
#else
	clock_t	starttime( clock() );
#endif

...

#ifdef WIN32
	unsigned long long	endtime, freq;
	QueryPerformanceCounter( (LARGE_INTEGER*)&endtime );
	QueryPerformanceFrequency( (LARGE_INTEGER*)&freq );
	DWORD	diff((DWORD)( (endtime - starttime) * 1000 / freq ) );
#else
	clock_t	endtime( clock() );
	clock_t	diff( ((long long)endtime - starttime) * 1000 / CLOCKS_PER_SEC );
#endif

Lebegő pontos a kód ?

fixpontos. a muveleteket egy 4 integer membert (left, top, rigt, bottom) tartalmazo "rect" nevu classon kell vegezni. ezek egy stl::list-ben vannak tarolva, mivel ugyis szekvencialisan kell tudni nagyon gyorsan vegigmenni az osszesen, ez ujfent gyorsan kell tudni az egyik containerbol egy masikba atdobni az elemeket.

Milyen függvényeket hivogat ?

OS szinten? kb annyit amennyit a stl::list hiv, azaz memoriamanagement uj elem letrehozasakokr.

Templateket használsz benne ?

Meg annyi hogy a modul intenziven hasznal stl::list-et, szoval akar a linuxos (default) stl is lehet a hunyo.

szeinted? :-P

Több szálas ?

nem.

Lehet, hogy msvc hatalmas kodreszleteket kiegyeszerűsített a programodból ?

szinte kizartnak tartom. eleg egyszeru a kod en meg messze nem vagyok mar nullkilometeres c++-ban.

maga a feladat, egy nem elforgatott, adott esetben egymassal fedesben levo teglalapokkal lefedett terulet, nem fedesben levo teglalapokkal lefedese. sajnos nem jutott eszembe hatekonyabb megoldas mint a teglalapokat egyenkent hozzaadni a keszlethez, es minden hozzaadaskor optimalizalni a hozzaadott teglalapot (bizonyos esetekben ez csak az uj teglalapo ketteosztasaval oldhato meg)

a ket teglalap iptimalizaciojat a rect::optimize oldja meg, ezt hivja eleg sokat a rectset::addRect, amit a teszter hiv meghatarozott szamu random teglelappal.

itt van a rect es a rectset deklaracioja:


//rects.h (C) 2007 Body Attila, all rights reserved
#ifndef __RECTS_H_INCLUDED
#define __RECTS_H_INCLUDED

#include <vector>
#include <list>

namespace gfx
{
	class point
	{
	public:
		inline point( const point &pt ) : m_x(pt.m_x), m_y(pt.m_y) {}
		inline point( int nx, int ny ) : m_x(nx), m_y(ny) {}
		int	m_x;
		int	m_y;
	};

	class rect
	{
	public:
		enum RELATION {
			notints = 0,
			conts,
			contd,
			modded,
			split
		};

		rect( const rect &base );
		rect();
		rect( int left, int top, int right, int bottom );
//		virtual ~rect();
		
		rect&		operator=( const rect &other );
		bool		join( const rect &other );
		inline bool	operator==( const rect &other ) const
		{
			return
				m_top == other.m_top &&
				m_left == other.m_left &&
				m_bottom == other.m_bottom &&
				m_right == other.m_right;
		}
		inline bool operator!=( const rect &other ) const { return ! operator==(other ); }
		inline bool	contains( rect &other) const {
			return m_top <= other.m_top &&
				m_bottom >= other.m_bottom &&
				m_left <= other.m_left &&
				m_right >= other.m_right;
		}
		inline bool contains( int x, int y ) const {
			return x >= m_left && x <= m_right && y >= m_top && y <= m_bottom;
		}
		inline bool contains( point &pt ) { return contains( pt.m_x, pt.m_y ); }

		inline int	width() const { return m_right - m_left; }
		inline int	height() const { return m_bottom - m_top; }
		inline bool	isEmpty() const { return width() <=0 || height() <= 0; }
		inline int	getTop() const { return m_top; }
		inline int	getLeft() const { return m_left; }
		inline int	getBottom() const { return m_bottom; }
		inline int	getRight() const { return m_right; }
		bool		hasIntersection( rect &insct, const rect &rct ) const;
		RELATION	optimize( rect &nr, rect &r );
		rect&		operator+=( const point &offset );
		rect&		operator*=( int mul );

	protected:
		enum EDGES {
			top = 1,
			left = 2,
			bottom = 4,
			right = 8
		};

		int	m_left, m_top, m_right, m_bottom;
		static const unsigned char	m_edgecnt[16];
	};	//	class rect

	class rectset
	{
	public:
		typedef std::list<rect>		RECTLIST;
		typedef RECTLIST::iterator	RLIT;

		rectset();
		size_t addRect( rect &r );
		inline std::list<rect>& getList() { return m_rects; }

	protected:
		RECTLIST	m_rects;
	};

	bool chklst( rectset::RLIT &begin, rectset::RLIT &end, rectset::RLIT &r1, rectset::RLIT &r2 );


}	//	namespace gfx
#endif /*__RECTS_H_INCLUDED*/

itt meg a kod:


//rects.cpp (C) 2007 Body Attila, all rights reserved
#include "stdafx.h"

#include "gfx/rects.h"
#include "svgwriter.h"
#include <algorithm>
#include <sstream>

using namespace gfx;
using namespace std;

#pragma warning( disable : 4996 )

//////////////////////////////////////////////////////////////////////////////
rect::rect( const rect &base ) :
	m_top( base.m_top ),
	m_left( base.m_left ),
	m_bottom( base.m_bottom ),
	m_right( base.m_right )
{
}

//////////////////////////////////////////////////////////////////////////////
rect::rect() :
	m_top( 0 ),
	m_left( 0 ),
	m_bottom( 0 ),
	m_right( 0 )
{
}

//////////////////////////////////////////////////////////////////////////////
rect::rect( int left, int top, int right, int bottom ) :
	m_top( top ),
	m_left( left ),
	m_bottom( bottom ),
	m_right( right )
{
}

//rect::~rect()
//{
//}

//////////////////////////////////////////////////////////////////////////////
bool rect::hasIntersection( rect &insct, const rect &rct ) const
{
	insct.m_top = max( m_top, rct.m_top );
	insct.m_bottom = min( m_bottom, rct.m_bottom );
	insct.m_left = max( m_left, rct.m_left );
	insct.m_right = min( m_right, rct.m_right );
	
	
	return ( !insct.isEmpty() );
}

//////////////////////////////////////////////////////////////////////////////
rect& rect::operator=( const rect &other )
{
	m_top = other.m_top;
	m_left = other.m_left;
	m_bottom = other.m_bottom;
	m_right = other.m_right;
	return *this;
}

//////////////////////////////////////////////////////////////////////////////
bool rect::join( const rect &other )
{
	if( m_top == other.m_bottom && m_left == other.m_left && m_right == other.m_right ) {
		m_top = other.m_top;
		return true;
	}
	if( m_bottom == other.m_top  && m_left == other.m_left && m_right == other.m_right ) {
		m_bottom = other.m_bottom;
		return true;
	}
	if( m_left == other.m_right && m_top == other.m_top && m_bottom == other.m_bottom ) {
		m_left = other.m_left;
		return true;
	}
	if( m_right == other.m_left && m_top == other.m_top && m_bottom == other.m_bottom ) {
		m_right = other.m_right;
		return true;
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////////
rect& rect::operator+=( const point &offset )
{
	m_left += offset.m_x;
	m_right += offset.m_x;
	m_top += offset.m_y;
	m_bottom += offset.m_y;
	return *this;
}

//////////////////////////////////////////////////////////////////////////////
rect& rect::operator*=( int mul )
{
	m_left *= mul;
	m_right *= mul;
	m_top *= mul;
	m_bottom *= mul;
	return *this;
}

//////////////////////////////////////////////////////////////////////////////
const unsigned char	rect::m_edgecnt[16] = {
//	0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
	0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4
};

//////////////////////////////////////////////////////////////////////////////
rect::RELATION rect::optimize( rect &nr, rect &r )
{
	if( !hasIntersection( nr, r ) )
		return notints;

	if( nr == r )
		return conts;

	if( nr == *this )
		return contd;

	unsigned char	edges(0), edges_wrk;

	//how many edges of the intersection rect equals to this rect
	if( nr.m_top == m_top )
		edges |= top;
	if( nr.m_left == m_left )
		edges |= left;
	if( nr.m_bottom == m_bottom )
		edges |= bottom;
	if( nr.m_right == m_right )
		edges |= right;

	if( edges ==( left|right ) ) {
		if( nr.m_top == r.m_top && nr.m_bottom == r.m_bottom && nr.m_left != r.m_left && nr.m_right != r.m_right ) {
			//see fig G. in spec.svg
			nr.m_right = r.m_right;
			r.m_right = nr.m_left;
			nr.m_left = m_right;
			return split;
		} else {
			//see fig A in spec.svg
			edges = nr.m_left == r.m_left ? right : left;
		}
	}
	else if( edges ==( top|bottom ) ) {
		if( nr.m_left == r.m_left && nr.m_right == r.m_right && nr.m_top != r.m_top && nr.m_bottom != r.m_bottom ) {
			//see fig. H in spec.svg
			nr.m_bottom = r.m_bottom;
			r.m_bottom = nr.m_top;
			nr.m_top = m_bottom;
			return split;
		} else {
			//see fig C in spec.svg
			edges = r.m_top == r.m_top ? bottom : top;
		}
	}

	rect	*rtm( NULL );
	switch( m_edgecnt[edges] )
	{
	case 3:	//3edgeb.svg
		rtm = this;
		edges_wrk = edges;
		break;
	case 1:	//3edgea.svg
		rtm =&r;
		edges_wrk = (~edges) & 0x0f;
		break;
	case 4:
		break;
	}

	if( rtm )
	{
		if( !(edges_wrk & top ) ) {
			//top is "free, so we modify the bottom (Fig. D on 3edges.svg)
			rtm->m_bottom = nr.m_top;
		} else if( !(edges_wrk & left) ) {
			//left is "free, so we modify the right (Fig. B on 3edges.svg)
			rtm->m_right = nr.m_left;
		} else if( !(edges_wrk & bottom ) ) {
			//bottom is "free, so we modify the top (Fig. C on 3edges.svg)
			rtm->m_top = nr.m_bottom;
		} else { // if( !(edges_wrk & right ) ) {
			//right is "free, so we modify the left (Fig. A on 3edges.svg)
			rtm->m_left = nr.m_right;
		}
		return modded;
	}
	else	//normal slice case: 2edge.svg
	{
		edges_wrk = (~edges) & 0x0f;
		if( edges_wrk & top ) {
			r.m_top = m_bottom;
		} else { //if( edges_wrk & bottom ) {
			r.m_bottom = m_top;
		}
		if( edges_wrk & left ) {
			nr.m_right = r.m_right;
			nr.m_left = m_right;
		} else { //if( edges_wrk & right ) {
			nr.m_left = r.m_left;
			nr.m_right = m_left;
		}
		return split;
	}
}

//////////////////////////////////////////////////////////////////////////////
rectset::rectset()
{
}

//////////////////////////////////////////////////////////////////////////////
size_t rectset::addRect( rect &r )
{
	RECTLIST	nrs;	//new rects
	RECTLIST	crs;	//rects in collision with the new rect;
	RECTLIST	ncrs;	//rect in not collision with the new rect;

	rect		tmprect;

	if( m_rects.empty() ) {
		m_rects.push_back( r );
		return 0;
	}

	while( m_rects.size() ) {
		RLIT	it( m_rects.begin() );
		if( it->hasIntersection( tmprect, r ) )
			crs.splice( crs.end(), m_rects, it );
		else
			ncrs.splice( ncrs.end(), m_rects, it );
	}

	size_t	retval( crs.size() );
	nrs.push_back( r );
	RLIT	crit( crs.begin() );

	rect::RELATION	lastrel( rect::notints );

	while( crit != crs.end() )
	{
		RLIT	nrit( nrs.begin() );
		while( nrit != nrs.end() )
		{
			RLIT	tmpit;
			lastrel = crit->optimize( tmprect, *nrit );
			switch( lastrel )
			{
			case rect::notints:
				//WTF?!
				break;

			case rect::conts:
				tmpit = nrit;
				++tmpit;
				nrs.erase( nrit );
				nrit = tmpit;
				break;

			case rect::contd:
				tmpit = crit;
				++tmpit;
				crs.erase( crit );
				crit = tmpit;
				break;

			case rect::split:
				//no need to check newly inserted rect against the same base rect
				nrs.insert( nrit, tmprect );
				break;

			case rect::modded:
				break;
			}
			if( lastrel != rect::conts )
				nrit++;
			if( lastrel == rect::contd )
				break;
		}	//	while( nrit != nrs.end() )
		if( lastrel != rect::contd )
			++crit;
	}	//	while( crit != crs.end() )
	m_rects.splice( m_rects.begin(), ncrs );
	m_rects.splice( m_rects.end(), crs );
	m_rects.splice( m_rects.end(), nrs );

	return retval;
}

Van meg kerdes?

Én a rects.cpp-t is .h-ba raknám, nem feltétlen a rects.h-ba, maradhat külön, csak az inline-osítás miatt. Azon elvileg sokat lehet nyerni, főleg ha rövid, és gyakran meghívott fv-ekről van szó...

Ez persze nem válasz az msvc-gcc különbségre...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Apróbb megjegyzés, hogy az osztály deklarációjakor definiált fv-ek elé nem kell "inline" kulcsszó, mert azok már azok.

Tehát elvileg a


class A
{
public:
   int func()
      {  return i; }
private:
   int i;
};

és a


class A
{
public:
   int func();
private:
   int i;
};

inline int A::func()
{  
    return i; 
}

ugyanazt eredményezi...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Azért az az optimize, meg a hasIntersection elég gyakran hívott fv-ek. Főleg ez utóbbi adbná magát inline-nak...

A kérdés egyébként azért is érdekes, mert pl nem tudom, hogy msvc mennyire agresszívan inline-osít kérés nélkül. Egy próbát talán megér.

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Újra csak nem a kérdésre a válasz :) :

Érdemes lenne megnézned gproof-fal vagy AMD Codeanalysttel, hogy mi tart sokáig, mert ha kiderül, hogy a listaműveletek (memóriafoglalgatás) akkor lehet, hogy nem helyben kéne a listát kezelni, hanem két vektorral az egyikből pakolni a másikba...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Ezzel mindossze az a bajom hogy jelen esetben nem alapvetoen a kod felgyorsitasa a kerdes, hanem az, hogy miert general majd 5-10x lassabb kodot a gcc, mint az MSVC. Ha a list rossz valasztas lene, akkor rossz valasztas lenne MSVC-hez is.

Elobb-utobb persze nem uszom meg hogy megismerkedjek a gprof-fal, de most nem aldoznek ra napokat.

Ha valami oknál fogva linux alatt lassabb a memóriafoglalás, az megmagyarázná a dolgot...

Egyébként elég furcsa, mert a gcc nem egy rossz fordító, ha megnézel benchmarkokat akkor kiderül, hogy az icc-hez és az msvc-hez képest legrosszabb esetben sem lassabb 10%-nál, és nagyritkán még nyer is...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

hat nem tudom. win alatt mingw-vel forditva is ugyan ez a helyzet.ezzel szerintem a platform kulonbsege nagyjabol ki is lett love, ami miatt esetleg a memoriafoglalas lassabb lehet az a gnu libc kontra MSVC libc, de ekkora kulonbseget ez sem lehet magyarazat szerintem.

Ami mint otlet felmerult az az hogy megprobalok megmerni kulonbozo stl es nem stl muveleteket MSVC/w32 kontra G++/Linux kozott

Meglepo eredmeny szuletett. Benchmark: hozzaadunk egy listahoz kuuuuvasok integert aztan vegigiteralunk rajra ugy hogy fel is hasznaljuk az erteket, aztan meg felhasznalas nelkul:


//////////////////////////////////////////////////////////////////////////////
void bm_list()
{
	timecounter	tc;
	list<int>	il;
	int			temp(0);

	tc.begin();
	for( int i=0; i<LISTITEMCOUNT; ++i )
	{
		il.push_back(i);
	}
	tc.end();
	cout << LISTITEMCOUNT << " integers added to the list in " << tc.getDiff() << " miliseconds" << endl;

	list<int>::iterator	it( il.begin() );
	tc.begin();
	while( it != il.end() )
		temp += *(it++);
	tc.end();
	cout << "iterating through the list with access took " << tc.getDiff() << " miliseconds" << endl;

	it = il.begin();
	tc.begin();
	while( it != il.end() )
		++it;
	tc.end();
	cout << "iterating through the list took " << tc.getDiff() << " miliseconds" << endl;
}

Eredmenyek
gcc:
8388608 integers added to the list in 670 miliseconds
iterating through the list with access took 50 miliseconds
iterating through the list took 50 miliseconds

msvc:
8388608 integers added to the list in 7723 miliseconds
iterating through the list with access took 131 miliseconds
iterating through the list took 131 miliseconds

Ezek szerint lassu gcc-s stl kilove. De akkor mi a franccal nem birkozik meg a gcc optimalizacioja? (jo, tudom, gprof, de meg soha nem hasznaltam es alapvetoen nem fogja megadni azt az infot hogy MSVC-hez kepest mit csinal lassabbra a gcc, mert ahoz az MSVC-vel forditott kod profilingja is kellene)

Esetleg egy próbát megér az idő ilyetén lekérdezése: getrusage(RUSAGE_SELF, &r_usage), r_usage.ru_utime, r_usage.ru_stime.

Egy próbát szintén megérhet az assembly vizsgálata (bár ehhez a gprof kellene, hogy mit érdemes nézni egyáltalán): g++ -g -O3 ..., objdump -S.

Esetleg mérd meg külön, mennyi időt töltesz az std::list metódusaiban.

Az std::list metódusai mennyire inline-olódnak helyből? A következő (mint tipp) csak vaktában lövöldözés, de ki tudja (-fvisibility=hidden, -fvisibility-inlines-hidden):

http://gcc.gnu.org/wiki/Visibility

gcc -O2 ?
Az -O3 ugyanis nem feltétlenül jobb mint az -O2. Egy próbát megér...

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

Hát igen. Akkor ez a baj. Ha i486-ra optimalizált gcc-vel optimalizálsz, ne várj optimális teljesítményt. Próbálj forrásból forgatni egy gcc-t, amit a processzorodnak megfelelőenj optimalizálsz ez alapján.

Még optimálisabb teljesítmény jöhet ki, ha esetleg valamilyen forrásalapú disztrót is kipróbálsz, például Gentoo-t, vagy GoboLinuxot.

gcc verzió?

"...handing C++ to the average programmer seems roughly comparable to handing a loaded .45 to a chimpanzee."
-- Ted Ts'o

próbáld meg csak assembly kódig fordítani, és nézd meg a két fordító esetén a fájlok méretét

---------
"Ha igazat mondasz azt végig unják, ha feldíszíted azt jól meg dugják"
szerény blogom -- új címen!

Na sikerult vegre kideriteni mi okozza a problemat: az elemeket egy vizsgalat utan 2 listaba szedem szet, megpedig splice-szal. Ez a muvelet egy while loopban van, ami addig megy amig van elem az eredeti listaban:


	while( m_rects.size() ) {
		RLIT	it( m_rects.begin() );
		if( it->hasIntersection( tmprect, r ) )
			crs.splice( crs.end(), m_rects, it );
		else
			ncrs.splice( ncrs.end(), m_rects, it );
	}

aminek eszement koltsege van a gcc stl implementaciojaban az a list::size(). Istenverte elmeroggyant gnu fele stl list::size() implementacio mit csinal? na mit? OMG:

return std::distance( begin(), end() );

Ami bizony-bizony szepen, komotosan egyesevel vegiggyalogol a lista elejetol a vegeig es megszamolja oket. Erre tenyleg csak annyit tudok mondani hogy attttyauuuuuristen.

ugyan ez a fuggveny M$ fele implementacioban ennyi:

return(_Mysize);

hat ezzel kb veget is ert az X akta, ha az empty()-t hasznalom a size() helyett akkor normalis sebesseggel fut a dolog.

Ezzel a modositassal 180-200 ms alatt vegez a gcc altal generalt kod ott, ahol az MSVC-vel forditott 357-342 ms alatt.

Szerintetek sirjak valami levlistan (es ha igen melyiken) hogy ez a splice() igy nem teljesen koser, vagy hagyjam a francba az egeszet?

mondjuk annyi kulonbseggel hogy eszembe nem jutna egy conatiner classban az ilyet nem letarolni es keres eseten a letarolt erteket visszaadni (ahogy az msvc stl implementacioja teszi) hanem minden egyes size() hivasnal vegigiteralni az osszes elemen (ahogy a gcc stl implementacioja teszi) egyszeruen nincs ra epeszu indok.

This choice is permitted by the C++ standard. The standard says that size() "should" be constant time, and "should" does not mean the same thing as "shall".
Komolyan gratulalok, politikusnak kellett volna mennie az ilyennek, nem fejlesztonek. Ott asztan el lehet vitatkozgatni mit jelentenek az egyes szavak valojaban, es maris nem kell a problema megoldasara koncentralni.

Es meg ha el is fogadom az indokaikat hogy a szabvany csak annyit mond hogy "kellene" es nem "kell", ezert ok ugy dontottek hogy a size() konstans sebesseget felaldozzak a splice() sebessegeert (a 3 splice() hivasi modbol 1 eseteben valoban nem tudhato hany elemet is mozgatunk at ha nem akarunk vegigiteralni az elemeken) akkor is van egy olyan megoldas hogy jelzem egy flaggel hogy az eltarolt ertrek invalid es a kovetkezo size() hivasnal aktualizalni kell.

Mondjuk ha a c++ szabvany valoban igy fogalmazza meg, akkor nekik is lo..sz a s...ukbe. Valahogy nekem a szbavany szo hallatan nem az olyan kifejezesek jutnak eszembe hogy "kellene", "lehet" meg "esetleg". Egy szabvanyositott nyelv szabvanyositott alapkonyvtaratol szerintem joggal lehetne elvarni hogy implementaciotol fuggetlen legyen a viselkedese barhol. Pont az az egyik hasfajasom az MSVC-vel hogy sok esetben tojik a szabvanyra. Pont a gcc fele stl implementaciotol nem varnam azt hogy a shall es a should jelentesen kezdjen el vitatkozni ahelyett hogy rendesen leimplementalna azt az istenverte osztalyt.

Karban tartni size valtozot koltseges, es legtobb esetben felesleges, neked sincs ra szukseged is_empty() pont eleg.
Ezrert nem szeretem ezt Template meg OO dolgot, mert az ember altalabna nem tudja mi zajlik a haterben, ill., ha valami hiba egy templaten belul csapodik le azt nehez debeuggolni.

Karban tartni size valtozot koltseges

Miertis? A ketiteratoros muiveleteken kivul (ilyen verzioja a splice-nak, assignnak, es az erase-nek van) van szerinted olyan fuggvenye a list()-nek amikor nem tudod minden varazslas nelkul hogy hany elemmel noveled/csokkented a listad elemszamat? Ha meg ilyet hivsz bebillented a _size_invalid flagedet az osztalyban es az elso size() hivaskor jobb hijjan vegigiteralsz az elemeken. Meg mindig jobban jossz ki mint ha minden egyes size() vegigmenne minden elemen...

Masreszrol ha en a standard alapjan irom a szoftvert, fel fogom tetelezni hogy a size() az nem O(n) futasi ideju. Persze ez a szabvany hibaja is, mert nem lenne szabadd ennyire sem megengedonek lennie ezzel kapcsolatban. A "kellene", "lehet", "talan", "esetleg" nem igazan szabvanyba valo szavak.

Gondolom nem arra van tervezve a list, hogy gyakran kérdezgesd a méretét (nem igazán lényeges a felhasználását illetően). Ha ilyet akarsz vele művelni, akkor valszeg rosszul választottál. Van más konténer, amit hatékonyan tudsz indexelni és a méretét kérdezgetni.

Az, hogy egy szabvány ad némi szabadságot az implementációban, vagy nem minden mozzanatot definiál pontosan, az az eddigi tapasztalataim alapján egyáltalán nem ritka (ebben a szakmában). :)

megprobaltam, megpedig ezen leiras alapjan, igy:

compi@arwen-d32:~$ g++ -o zizi -pg gfx.cpp rects.cpp svgwriter.cpp
compi@arwen-d32:~$ gprof zizi
gmon.out: No such file or directory

es tenyleg nincs, pedig a forditasnak a leirasok szerint a -pg hatasara letre kellett volna hoznia. Na itt adtam fel a gprof-ot es nyultam a probak es hibak modszerehez. Ha esetleg van tipped miert nem jon letre az a nyuves gmon.out, szivesen veszem a segitsegedet.

Azért nem elmeroggyanásból kifolyólag van így, a doksiban leírják, hogy miért van ez: egy döntés volt természetesen. Sokszor nincs szükség plusz belső méretadatra, ezt nem lehet kivenni, ha nem kell. Szerintem különben még a konzisztencia miatt is van: ne legyen kétszer bekódolva a listába a méret - egyszer a maga lista elemszáma, egyszer egy változóban. Ha meg kell, az ember beleteszi magának, vagy használ más struktúrát.

Persze ilyenkor dühítő, ismerem a szitut. :) Nameg fönti címen is emlegetik, hogy az ISO konstans idejű végrehajtást szorgalmaz, de nem feltétlenül írja elő.

Ezzel vitatkoznék... Az, hogy a redundancia jó, ha belefér, az igaz; de csak abban az esetben, ha az ember valóban ellenőrzi is, hogy a független forrásból kijövő adatok passzolnak-e. Ahogy látom, legtöbb helyen azt sem ellenőrzik, amit nagyon kéne, pl. egy malloc kapott-e elég helyet... meg egyéb rendszerhívásokat. Nemhogy még két helyről jövő ugyanolyan adat egyezik-e. A fontos viszont itt inkább az, hogy a konzisztencia nem egyenlő redundanciával.
Szerk: konzisztencia azért van, hogy könnyebb legyen hibamentes algoritmust csinálni. Redundancia nem igazán az algoritmus hibáinak kivédésére van, inkább hardvernyavalyák ellen, szerintem.

oke, pongyolan fogalmaztam. kb azt akartam mondani amit te irtal, bar szerintem nem szukseges a kozisztencia folytonos ellenorzese "eles" verzional, az ojjektum teszteleset viszont nagyban meg tudja konnyiteni, jelen esetben pedig az "eles" kornyezetben jelentos sebessegnovekedest lehetett volna vele elerni. de mind1, ebben az esetben szerencsere megkerulheto volt a size() hivasa.

Nem feltétlen, csak akkor, ha mind a két redundáns adathoz hozzá is férsz. Amúgy meg nem hiszem hogy user oldalról bármikor is valaki átszálazna egy listát, hogy télleg annyi elem van-e benne, mint a list::size() által adott eredmény. Itt meg pont arról volt szó, hogy a _valós_ eredményt kapd, ne egy "hát talán yó" eredményt. Valós méretet akkor ad, ha megszámolja, és az eredményt visszaküldi. Ha a méretváltozó bármely okból nem frissült, akkor te fals eredményeket kapsz.