Приказивање постова са ознаком c++. Прикажи све постове
Приказивање постова са ознаком c++. Прикажи све постове

четвртак, 15. април 2010.

Занимљиво питање

Питање је стигло од корисника:

Зашто на powerpc платформи овај код завршава у бесконачној петљи:


int main()
{
   int bar = 0;
   char foo;

   for (foo = 7; foo >= 0; foo--) {
       bar++;
   }

   return bar;
}


Одговор:
зато што је на powerpc платформи char не-негативан целобројни тип (unsigned char)

субота, 28. новембар 2009.

elif евалуација

Интересантна промена се десила у gcc Ц препроцесору. Евалуација elif гране препроцесорске директиве "if" се извршава чак и ако је услов дат у if задовољен.

Конкретан пример:

#ifdef
#ifndef FOO_BAR
#define FOO_BAR
#elif FOO_BAR == 0
#endif

Ово ради без проблема на претходним верзијама, на пример 4.3.3 али не и на 4.4.1. Не знам у којој тачно верзији је дошло до промене, али ево извештаја проблема: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=36320

Исправна конструкција горе наведеног примера би била:


#ifdef
#ifndef FOO_BAR
#define FOO_BAR 1
#elif FOO_BAR == 0
#endif

петак, 27. новембар 2009.

Секвентна тачка

Односно "Sequence point".

Питање у вези секвентне тачке (ако сам превео добро) често долази на разговорима за посао. Многи нису свесни шта је то иако је у суштини проста ствар. Пре неки дан ме на то потсетио бивши колега, а и ја сам то питање добио на мом разговору.

Секвентна тачка је синтаксни елемент дефинисан језиком који гарантује да ће споредни ефекти евалуације израза са леве стране таквог елемента гарантовано бити завршени пре него што започне евалуација израза са десне стране.

На пример, у Цеу и Це++ то је оператор зарез ',' али не и аримтетички оператори. Осим зареза, има још секвентних тачака као што су '?' и логички оператори.

Кратак, али добар текст се може наћи на википедији.

уторак, 27. октобар 2009.

gcc и груписање функција

У неким ретким случајевима, потребно је оптимизовати програм тако да се смањи растојање између функција које се често позивају. Могући добитак од овакве оптимизације је смањење "TLB misses" односно потребе да процесор учитава податке за превођење виртуелне у физичку адресу за дати блок. Друга могућа добит је искоришћење различитих нивоа кеширања: уколико су функције физички близу, велика је вероватноћа да ће, док се прва извршава, друга бити учитана у кеш.

Конкретан пример који имам је оптимизација кода за "лењо разрешавање" адреса симбола у динамички повезаним програмима (lazy binding).

Наш оперативни систем (QNX Neutrino) има динамичко повезивање, али не "уме" да то уради "лењо" односно на захтев. Како сада ради, наш динамички повезивач разрешава све адресе симбола на самом почетку, пре него што програм стигне у "main". Последњих неколико дана сам радио на имплементацији која већ постоји на већини јуникс система, а која подразумева разрешавање симбола функција у тренутку првог позива.

Како између два разрешења може доста кода да се изврши, велика је вероватноћа да ће функције потребне за лењо разрешење већ бити избачене из кеша, а такође из процесорске табеле за превођење адреса (TLB) избачене ставке које се односе на физичке странице меморије у којима су те функције. Груписањем функција тако да буду близу једна другој, идеално у једној физичкој страници меморије, ће смањити број промашаја.

Приликом прве компилације установио сам да су функције на приличном растојању једна од друге. Конкретно, приликом дисасемблирања објектног фајла, добио сам ово:

bind_func     0x000000cc
 resolve_func  0x000029e0
 resolve_rels  0x00001010
 lookup_global 0x00000c68

Види се да би фунције биле распоређене у бар 3 меморијске странице (под условом да је страница 4KiB).

Да бих груписао исте, искористио сам атрибут фунције "hot" уведен са gcc верзијом 4.3. Горе наведеним функцијама сам у декларацију додао: __attribute__((hot)). Gcc тако означене функције оптимизује агресивније али и групише у посебну секцију ".text.hot". На крају процеса повезивања, функције су распоређене једна до друге:

bind_func    0x0003f3f8
 resolve_rels 0x0003f418
 resolve_func 0x0003f88c

Интересантно је да је lookup_global "нестала" односно апсорбована у функције које је позивају.

У сваком случају, уместо 4 физичке странице сада функције могу да стану у само једну и распоређене су једна уз другу чиме се повећава шанса да у тренутку позива већ буду у кешу.


Тек треба да упоредим да ли се овиме добија нешто што може да се измери. Вероватно ће доста зависити од процесора па и самог уређаја.

недеља, 22. март 2009.

конст коректност

Kључна реч "const" у језицима це и це плус плус.

У пројектима често срећем код у којима се ова кључна реч не користи или се не користи доследно. То је штета јер доприноси не само читљивости него и преношењу идеје о томе шта се дешава у одговарајућој функцији ономе који је чита, док компајлер обезбеђује да коректност буде примењена.

Уместо много речи, ево неколико примера. Рецимо да имамо функцију која треба да као аргумент прими показивач на низ карактера. За пример приказујем три функције од којих свака као аргумент има показивач на низ карактера:

/* Deklaracije. */
int f1(char *tekst);
int f2(const char *tekst);
int f3(char const *tekst);
int f4(const char *const tekst);
int f5(char *const tekst);

У чему је разлика између ових декларација? Наравно, у коришћењу речи конст.

Прва нам каже да ће низ на који показује формални параметар "tekst" можда бити мењан у функцији; друга функција нам каже да низ на који показује текст сигурно неће бити мењан унутар функције (али не говори ништа о томе да ли ће вредност аргумента "tekst" бити мењана, што са тачке гледишта корисника функције није ни битно).

Трећа функција има идентично значенје као и функција "f2". Неко више воли такав запис, неко као у другој функији - све једно је. 

Четврта функција поред тога што "обећава" да неће мењати низ на који показује формални аргумент, још казује да неће мењати ни вредност тог аргумента. Са тачке гледишта корисника функције, ово додатно конст је ирелевантно - позивној функцији је свеједно шта ће се са аргументом десити. Иако није неправилна, ова декларација можда мало непотребно оптерећује декларацију и ја је обично не користим. 

Пета функција је дата само ради комплетности, ни овакву декларацију функције не користим јер, као што сам раније рекао, говори о нечему што онога ко користи функцију не занима. Семантички, идентична је функцији "f1", дакле низ на који показује аргумент може да буде мењан унутар функције.

Слично је са декларацијама променљивих, само што овде често има смисла користити и варијанте "char *const tekst;" и "const char * const tekst" у зависности од контекста.

У це-плус-плусу постоји и коршћење речи конст у својству модификатора метода класе. Ево примера:

/* Jednostavan primer const kao
modifikatora metoda. */
#include <string>
#include <iostream>

using std::string;
using std::cout;
using std::endl;

class Zaposleni
{
public:
Zaposleni(const string &ime)
: _ime(ime) {}

virtual ~Zaposleni() {}

string ime()
{
cout << "Pozvana ne-konst metoda." << endl;
return _ime;
}

string ime() const
{
cout << "Pozvana konst metoda." << endl;
return _ime;
}

private:
string _ime;
};

void stampajZaposleni(const Zaposleni &zap)
{
cout << "U funkciji stampajZaposleni" << endl;
cout << zap.ime() << endl;
}

int main(int argc, char* argv[])
{
Zaposleni zap("Mika");
cout << "Poziv preko konst deklarisane promenljive." << endl;
stampajZaposleni(zap);
cout << "Sada poziv preko ne-konst deklarisane promenljive: " << endl;
cout << zap.ime() << endl;
return 0;
}

Пример је тривијалан, али има једну интересантну тачку: који од два метода за име ће бити позван у функцији stampajZaposleni? Наравно, биће позване методе које су означене са конст јер смо и формални аргумент "zap" у функцији stampajZaposleni означили са конст у декларацији функције. Уколико искоментаришемо конст декларације метода "ime", откирћемо да програм више не може да се искомпајлира јер не-конст метода "ime" не "обећава" да неће мењати унутрашње стање објекта.

За разлику од тога, у самој функцији "main" ће бити позвана не-конст метода јер је таква и декларација локалне променљиве "zap".



Закључак: конст коректност није претерано компликована за разумевање. Па ипак, уколико се добро не разуме и не користи правилно, брзо доводи до пројеката чија се конст коректност више не може поправити. Зато је важно од самог почетка пројекта правилно користити ову интересанту и веома корисну особину програмског језика.