Re: 2 Andrew S: по поводу ebp
От: remark Россия http://www.1024cores.net/
Дата: 06.04.07 19:39
Оценка:
По поводу ebp я с одной стороны понял, а с другой не понял.

Понял, что при использовании исключений он может заниматься, и т.о. останется меньше регистров для вычислений.
Правда, имхо, такой эффект будет нетривиально отследить — надо сделать какой-то синтетический тест, которому требуется много регистров...


Не понял, когда и под что он занимается.
здесь
Автор: remark
Дата: 02.04.07
достаточно простенький пример и ebp _не_ занимается.
Почему и под что он будет заниматься в более сложных случаях?
Я бы понял, если бы наоборот — в простых он занимался, а в сложных освобождался каким-либо образом под другие нужны, а наоборот не понимаю...



Заодно по поводу thiscall. Для С случая нельзя использовать thiscall — его там нет. Поэтому всё же придётся мерить с cdecl.
Случай, когда в С++ используются возвращаемые значения, конечно, можно и надо мерить с thiscall.



По поводу сделать для случая С++ с возвращаемыми значениями вместо деструктора функцию deinit() — так не пойдёт. Даже если в С++ и используют функции типа init/create и т.д. вместо конструктора, то деструкторы используют даже самые вшивинькие библиотеки типа MFC/ACE. Поэтому я считаю, что случай с deinit() в С++ просто doesn't make any sense.



1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: 2 Andrew S: по поводу ebp
От: Andrew S Россия http://alchemy-lab.com
Дата: 06.04.07 20:55
Оценка:
R>По поводу ebp я с одной стороны понял, а с другой не понял.

R>Понял, что при использовании исключений он может заниматься, и т.о. останется меньше регистров для вычислений.

R>Правда, имхо, такой эффект будет нетривиально отследить — надо сделать какой-то синтетический тест, которому требуется много регистров...

Эффект проявляется уже в простейших нагруженых функциях. Как пример — недавно делал блиттер. Так вот, этот несчастный один дополнительный регистр во внутреннем цикле давал +20% производительности. Ну мало регистров у х86, никуда от этого не уйти... Идиотизм, конечно, но тут все вопросы к Интел.

R>Не понял, когда и под что он занимается.

R>здесь
Автор: remark
Дата: 02.04.07
достаточно простенький пример и ebp _не_ занимается.

R>Почему и под что он будет заниматься в более сложных случаях?

Я привел код. Посмотри, там явно видно, под что.

R>Я бы понял, если бы наоборот — в простых он занимался, а в сложных освобождался каким-либо образом под другие нужны, а наоборот не понимаю...


Это как это наоборот? В простейших случаях компилятор может организовать вычисления стек-фрейма на esp, а в сложных ему это сделать не судьба — когда стек фрейм может раскручиваться как локальным исключением (фрейм, установленный в самой функции), так и во фрейм, установленный по вызову выше. В общем, ничего удивительного.

R>Заодно по поводу thiscall. Для С случая нельзя использовать thiscall — его там нет. Поэтому всё же придётся мерить с cdecl.


Случай "С" меня как то не очень интересует, откровенно говоря. Надо сравнивать сравнимое.

R>Случай, когда в С++ используются возвращаемые значения, конечно, можно и надо мерить с thiscall.


R>По поводу сделать для случая С++ с возвращаемыми значениями вместо деструктора функцию deinit() — так не пойдёт. Даже если в С++ и используют функции типа init/create и т.д. вместо конструктора, то деструкторы используют даже самые вшивинькие библиотеки типа MFC/ACE. Поэтому я считаю, что случай с deinit() в С++ просто doesn't make any sense.


Нет. Обычно init\destroy используют, когда имеются тривиальные конструкторы\деструкторы (которые инлайнятся), и тяжелые функции первичной\вторичной инициализации. Либо вообще не имеют конструкторов\деструкторов — получаются обычные POD. Поэтому именно то, что я приводил. А назвать MFC или ACE вшивенькой библиотекой — ну это да. Вообще, MFC как раз очень вполне завязана на исключения — посмотреть те же самые CFile.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[9]: [Investigation] оверхед std::auto_ptr
От: gear nuke  
Дата: 06.04.07 21:03
Оценка:
Здравствуйте, remark, Вы писали:

R>В общем случае, если код делает одно и тоже, то и машинный код быдет таким же. Не виже никаких причин для обратного.


auto_ptr делает несколько больше. Я же не писал "всегда отличается"

R>Ты не поверишь, но компилятор сделал следующее:


R>
R>int main()
R>{
R>    auto_ptr();
R>0040109A  call        auto_ptr (401060h) 
R>    by_hand();
R>0040109F  call        auto_ptr (401060h) 
R>}
R>004010A4  xor         eax,eax 
R>004010A6  ret

R>Он даже не стал генерировать разных функций

Поверю, у меня тела функций тоже получились идентичными, но в разных местах, поэтому хотелось бы узнать компилятор и ключи

Немного модернизировал пример, что бы было лучше видно лишнюю работу:

__declspec(noinline) void auto_ptr()
{
    std::auto_ptr<int> i /*(new int(5))*/;
}
>__declspec(noinline) void by_hand()
{
    int* i /*= new int(5)*/;
    //delete i;
}

int main()
{
    auto_ptr();
    by_hand();
}

MSVC 2005 + Dinkumware STL:
; Function compile flags: /Ogspy

?by_hand@@YAXXZ PROC                    ; by_hand
    ret 0
?by_hand@@YAXXZ ENDP                    ; by_hand

?auto_ptr@@YAXXZ PROC                   ; auto_ptr
    push    0
    call    ??3@YAXPAX@Z                ; operator delete
    pop ecx
    ret 0
?auto_ptr@@YAXXZ ENDP                   ; auto_ptr

_main   PROC
    call    ?auto_ptr@@YAXXZ            ; auto_ptr
    xor eax, eax
    ret 0
_main   ENDP

Конечно вырожденный случай, вот более жизненный:
__declspec(noinline) void auto_ptr()
{
    std::auto_ptr<int> i;
    int* i2 = new int(5);
    i.reset(i2);
}

__declspec(noinline) void by_hand()
{
    int* i;
    int* i2 = new int(5);
    i = i2;
    delete i;
}

int main()
{
    auto_ptr();
    by_hand();
}

MSVC 2005 + Dinkumware STL:
; Function compile flags: /Ogspy

?by_hand@@YAXXZ PROC                    ; by_hand
    push    4
    call    ??2@YAPAXI@Z                ; operator new
    test    eax, eax
    pop ecx
    je  SHORT $LN3@by_hand
    mov DWORD PTR [eax], 5
    jmp SHORT $LN4@by_hand
$LN3@by_hand:
    xor eax, eax
$LN4@by_hand:
    push    eax
    call    ??3@YAXPAX@Z                ; operator delete
    pop ecx
    ret 0
?by_hand@@YAXXZ ENDP                    ; by_hand

?auto_ptr@@YAXXZ PROC                   ; auto_ptr
    push    esi
    push    4
    call    ??2@YAPAXI@Z                ; operator new
    test    eax, eax
    pop ecx
    je  SHORT $LN3@auto_ptr
    mov DWORD PTR [eax], 5
    mov esi, eax
    jmp SHORT $LN4@auto_ptr
$LN3@auto_ptr:
    xor esi, esi
$LN4@auto_ptr:
    test    esi, esi
    je  SHORT $LN7@auto_ptr
    push    0
    call    ??3@YAXPAX@Z                ; operator delete
    pop ecx
$LN7@auto_ptr:
    push    esi
    call    ??3@YAXPAX@Z                ; operator delete
    pop ecx
    pop esi
    ret 0
?auto_ptr@@YAXXZ ENDP                   ; auto_ptr


В STLPort должны получиться похожие результаты.


Cитуацию (точнее, auto_ptr) можно немного исправить.

первый вариант:
; Function compile flags: /Ogspy
?auto_ptr@@YGXXZ PROC                  ; auto_ptr, COMDAT
    ret 0
?auto_ptr2@@YGXXZ ENDP                  ; auto_ptr

второй:
; Function compile flags: /Ogspy
;   COMDAT ?by_hand@@YGXXZ
_TEXT   SEGMENT
?by_hand@@YGXXZ PROC                    ; by_hand, COMDAT
; заинлайнен new:
    push    esi
    mov esi, 1280593503             ; 4c544e5fH
    push    esi
    push    4
    push    1
    call    DWORD PTR __imp__ExAllocatePoolWithTag@12
    test    eax, eax
    je  SHORT $LN3@by_hand
    mov DWORD PTR [eax], 5
    jmp SHORT $LN4@by_hand
$LN3@by_hand:
    xor eax, eax
$LN4@by_hand:
    test    eax, eax
    je  SHORT $LN12@by_hand
; заинлайнен delete
    push    esi
    push    eax
    call    DWORD PTR __imp__ExFreePoolWithTag@8
$LN12@by_hand:
    pop esi
    ret 0
?by_hand@@YGXXZ ENDP                    ; by_hand

?auto_ptr@@YGXXZ PROC                  ; auto_ptr, COMDAT
; заинлайнен new:
    push    esi
    mov esi, 1280593503             ; 4c544e5fH
    push    esi
    push    4
    push    1
    call    DWORD PTR __imp__ExAllocatePoolWithTag@12
    test    eax, eax
    je  SHORT $LN3@auto_ptr
    mov DWORD PTR [eax], 5
    jmp SHORT $LN23@auto_ptr
$LN3@auto_ptr:
    xor eax, eax
$LN23@auto_ptr:
    test    eax, eax
    je  SHORT $LN37@auto_ptr
; заинлайнен delete
    push    esi
    push    eax
    call    DWORD PTR __imp__ExFreePoolWithTag@8
$LN37@auto_ptr:
    pop esi
    ret 0
?auto_ptr@@YGXXZ ENDP                  ; auto_ptr

Текст примеров не изменён, исходный текст auto_ptr приведу следующим постом, этот и так уже большой

R>Или вот ещё:


[]

Аналогично — просто повезло Я о непонятной мне штуке с названием scalar deleting destructor
Автор: gear nuke
Дата: 17.11.05


People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[10]: std::auto_ptr почти без оверхеда
От: gear nuke  
Дата: 06.04.07 21:09
Оценка:
template<class Y> struct auto_ptr_ref;

/// Class template auto_ptr [20.4.5 lib.auto.ptr]
template<class X>
class auto_ptr
{
  ///////////////////////////////////////////////////////////////////////////
  public:

    typedef X element_type;

    ///\name  construct/copy/destroy [20.4.5.1]

    explicit auto_ptr(X * p = 0)  throw() : ptr(p) {}

    auto_ptr(auto_ptr & a)        throw() : ptr(a.release()) {}

    template<class Y>
    auto_ptr(auto_ptr<Y> & a)     throw() : ptr(a.release()) {}

    __forceinline
    auto_ptr & operator=(auto_ptr & a) throw()
    {
      reset(a.release());
      return *this;
    }

    template<class Y>
    auto_ptr & operator=(auto_ptr<Y> & a) throw()
    {
      reset(a.release());
      return *this;
    }

    __forceinline
    ~auto_ptr() throw() { if ( get() ) delete get(); }

    ///\name  members [20.4.5.2]

    X & operator* ()  const throw() { return *get(); }
    X * operator->()  const throw() { return get(); }
    X * get()         const throw() { return ptr; }
    X * release()           throw() { X * tmp = get(); set(0); return tmp; }

    __forceinline
    void reset(X * p = 0)   throw()
    { 
      if ( get() && get() != p ) delete get();
      set(p);
    }

    ///\name  conversions [20.4.5.3]

    auto_ptr(auto_ptr_ref<X> r) throw() : ptr(r.ptr.release()) {}

    __forceinline
    auto_ptr & operator=(auto_ptr_ref<X> r) throw() { return *this = r.ptr; }

    template<class Y>
    operator auto_ptr_ref<Y>()  throw() { return auto_ptr_ref<Y>(*this); }

    template<class Y>
    operator auto_ptr<Y>()      throw() { return auto_ptr<Y>(release()); }

    ///@}

  ///////////////////////////////////////////////////////////////////////////
  private:
  
    X * ptr;
    void set(X * p) { ptr = p; }
};

template<class Y>
struct auto_ptr_ref
{
    auto_ptr_ref(auto_ptr<Y> & a) throw() : ptr(a) {}
    auto_ptr<Y> & ptr;
  private:
    auto_ptr_ref<Y> & operator=(const auto_ptr_ref<Y> &);
};
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[2]: 2 Andrew S: методика замера времени
От: Andrew S Россия http://alchemy-lab.com
Дата: 06.04.07 21:27
Оценка: -1
R>Речь идёт о замере интервалов порядка десятков тактов.

Десятков???? Десятков тысяч. Или ты про разницу?

R>Я предлагаю следующую методику:

R>Выполняем код много раз подряд.
R>Разы, в которые произошёл вызов планировщика, переключение контекста, страничное прерывание и т.д. отбрасываем, как выдающие заведомо оооочень большое неправильное время, никак не связанное с нашим измеряемым кодом.
R>Т.о. получаем "истинное" время выполнения целевого фрагмента кода.

Планировщик вызывается раз в ~15мс. Ты действительно думаешь, что он не влияет на результаты замеров? Еще раз — чтобы снизить влияние планировщика, надо поднять приоритеты и увеличить время замеров до нескольких (десятков) минут.

R>Ты предлагаешь (поправь, где не прав):

R>Выполняем код много раз подряд.

Много _тысяч_ раз подряд.

R>Разы, в которые произошёл вызов планировщика, переключение контекста, страничное прерывание и т.д. учитываем.


Мы их в любом случае учитываем. Ты в мультизадачной ОС. Прервать выполнение кода может не только планировщик — еще есть аппаратные прерывания, DMA, и многие другие вещи, неявно влияющие на производительность памяти, процессора и т.п.

R>Потом "размазываем" эти ооочень сильно большие времена, усредняя все значения.


Нет. Времена будут примерно одинаковые. Почитай статью по ссылке, там пояснено, как что и почему. Я уже устал говорить, что время выполнения в современных программно-аппаратных системах зависит не только от программной, но и той аппаратной части (юнита), на котором исполняется код. А с учетом всяких "прикольных" чипсетов типа KT133 с его закидонами с шиной — так и вообще от фазы луны. Поэтому надо использовать длительные измерения, а влияние перепланировки уменьшать путем повышения приоритета. Право — это же азы.

R>Т.о. получаем "истинное" время выполнения целевого фрагмента кода.


Не истинное, а реальное. Минимальное время выполнения на современном железе может быть вызвано совершенно безумным сочетанием невоспроизводимых в реальной ситуации факторов (ну или редко воспроизводимых).

R>Т.е. ты считаешь нормальным, если, например, при замере одного фрагмента кода переключения контекста произошли 1 раз, а во время замера второго фрагмента кода переключения контекста произошли 2 раза. Потом мы усредняем эти времена и сравниваем.


Нет. Я считаю нормальным, когда за время замера переключение контекста произошло 100000 или 100010 раз.

R>Т.е. ты считаешь нормальным, если произошёл вызов планировщика, потом одно переключение контекста, потом поработал другой поток, потом опять вызов планировщика, потом опять переключение контекста и потом продолжила работать наша функция. Это нормально, это время можно включить в замер функции в 20 тактов. А вот если ко всему этому прибавляется ещё и пенальти, вызванное переключением потока на другое ядро, то вот это ты считаешь не нормальным, и для борьбы с этим предлагаешь делать привязку потока к ядру.


Ты все в кашу мешаешь... Даже не буду комментировать, но право, рекомендую разобраться, что к чему и зачем. К одному ядру я рекомендую привязывать по совершенно другим причинам. И я о них уже говорил.
Ты, например, даже не учитываешь, что RDTSC замеряет совсем не время выполнения кода, а совершенно другое — пропускную способность декодера команд. Поэтому все твои измерения с точностью до 20 команд — пшик. Конвейер у того же П4 — 18-20 стадий, что ты меряешь? Почитай статью — там хороший пример. ПОэтому надо мерить не 20 тактов, а 200000 тактов. Тогда точность измерения будет приемлемой (инструмент измерений не будет влиять на измеряемый объект). Между вызовами точек отсчета должны проходить десятки, а лучше сотни тысяч команд.

R>Аналогичная ситуация с поднятием приоритета потока. Ты считаешь, что вызов планировщика совершенно нормально учитывать при замере куска кода.


Без этого — никак. Хочешь отключить планировщик? Работай в DOS

R>Ну уж извините, с таким подходом я в корне не согласен. Хоть убейте.


Ну, можно не соглашаться с теорией относительности, или с тем, что в одном рубле 100 копеек. Но от этого она (ТО) не станет более верной или не верной, а рубль не полегчает и тяжелее тоже не станет. Есть испытанные методики измерения времени выполнения кода. Про них тебе уже говорили несколько разных людей, не только я.
Все, эта тема для меня закрыта — нового я тебе ничего не скажу, ты мне — тоже . Право, ходим по кругу — я не вижу смысла продолжать.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[2]: [Investigation] Exceptions vs. Return Values
От: Константин Россия  
Дата: 31.01.08 10:22
Оценка:
Здравствуйте, Аноним, Вы писали:

А>То, что исключения позволяют сократить один if при вызове функции, по-моему, с лихвой компенсируется тем, что исключения вынуждают пользоваться обертками в виде умных указателей.


"Вы поставили знак минус вместо знака плюс"


Выделенное нужно заменить на и бесплатно получаем

Уже ради этих обеих вещей стоит использовать исключения
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.