in Computing and stuff

no except

Ето една интересна лекция на Meyers, в която той обяснява много добре какво е std::move, std::forward и noexcept 🙂

Към средата има една част, в която е показано колко по-бърз може да е кода, когато е с noexcept там, където е възможно. Освен това имаше показани примери, как чрез проста смяна на компилатора със C++11/14 enabled такъв, кода става по-бърз. Във връзка с noexcept се появи в един момент и тази графика.

Колко хубаво, помислих си .. Ще направим сега едно NOEXCEPT макро (което ще е #define NOEXCEPT noexcept когато компилираме със С++11/14 компилатор), ще прекомпилираме всичко което имаме (и вкъщи и в офиса) с него и ще получим ей така едни проценти забързване от нищото ..

Та изрових една моя стара (С++98) микро библиотека за математика (вектор, точка, матрица, quaternion, в общи линии това) и реших да завъртя един цикъл с тях (тя всъщност е толкова проста, че не очаквах каквито и да е разлики в резултатите).
Ползвам clang-600.0.51(llvm 3.5svn), компилирам с -Os (което е по дефаулт) (с -O3 резултатите са почти същите) и C++14 enabled.

Кода от теста се намира тук.

Интересно, че противно на приказките на Meyers, Chandler (from LLVM) и не знам си кой друг, на моята машина (I7-3615QM) кода върви по-бавно с между 2 и 5 процента, когато функциите имат noexcept.
Или аз го ползвам много не както трябва, или някой някъде малко послъгва …

Write a Comment

Comment


*

11 Comments

  1. Meyers говори за съвсем друго нещо, което в общи линии е следното: ако някой (като вектора в неговия пример) се чуди дали да те мести или копира, ще предпочете да те мести, *само* ако си безопасен за местене, което се обявява с noexcept move constructor/move assignment. Та забързването в таз графика идва от там, че не се копират стринговете по време на resize(), а просто се вика move. Примерът му е много специфичен и показва една от главните причини noexcept да се включи в стандарта, иначе STL-а напълно се счупва откъм exception safety (а в STL-a се държи на exception safety, че току-виж някоя сонда отказала на половината път до Марс) и всичките move еквилибристики стават безсмислени. А твоят тест е напълно различен – просто си измерил overhead-а от слагането на noexcept навсякъде. Имаш само копиране, нямаш movable типове, нямаш предпоставки за move, тъй че просто не си дал възможност на магическия noexcept да се прояви 🙂 На мен друго ми е интересно – каква ще е разликата между throw() и noexcept…

    Ако си готов да пожертваш 2000 неврона пък, може да разгледаш имплементацията на някой vector, ама не на майкрософтския…

  2. Благодаря ти за разяснението (което и аз, и всеки които е писал някога на С++ и е гледал лекцията, най-вероятно, е разбрал :)) …
    Както се вижда от текста на блог-поста ми, аз *не* питам това обаче … noexcept предполага повече възможности за оптимизиране *не само* заради move_if_noexcept (за което може да видиш на много места в bing). Тоест, такива забавяния, каквото и да става, *не са* очаквани ..
    Ако на някой му се занимава да разбере защо са се случили, вилкомен 🙂

  3. Напротив, напълно очаквани са забавянията (в случая) – очаква се от компилатора да сложи по някоя инструкция тук и там заради тоя noexcept. И да, noexcept наистина предполага повече възможности за оптимизиране, но спрямо throw(), а не спрямо exception-neutral код. А това move_if_noexcept може и да се окаже най-голямата печалба от цялата работа, не случайно Meyers точно това демонстрира…

  4. Ако питаш мен, по-скоро се очаква компилатора да махне една-две инструкции (знае повече за кода => може да си извади по-добри изводи) …

  5. Нали трябва да направи разни проверки дали все пак няма хвърлен exception и евентуално да извика terminate(). Един diff на асемблера ще реши мистерията 🙂

  6. Кода го има, аз желание (и нерви) за асемблер нямам ..
    За да има exception-и обаче, трябва да има други if-чета (или сходни екстри) .. тъй че все, си мисля, все тая.

  7. Интересно, GCC 4.7.0 генерира напълно еднакъв код (независимо дали е с -О3 или -Оs).

  8. А и като казва “пишете навсякъде, където може”, ако има дори най-малката вероятност да стане по-бавно поради някаква причина, следва да го спомене ..

  9. Аз си мисля, че причината е следната – с noexcept наистина даваш повече информация на компилатора, обаче може (несъзнателно) да го излъжеш и някой трябва в тоя случай да те удари през ръцете. За нещастие, в С++ това се прави от runtime-a, а не от компилатора. Ако се правеше от компилатора, това нямаше да минава:

    void foo() noexcept { throw new int(99); }

    Така че стойността на тоя noexcept извън контекста на move_if_noexcept и разни library optimizations е наистина много съмнителна…

    И слушай го внимателно какво казва: “my advise here is not “use noexcept everywhere” … use it whenever is possible, and it is possible if you’ve determined that the interface of the function should offer that guarantee”; сигурно затова е написал “whenever possible” с italic.

    След това казва, че от exception-neutral кода се очаква да остане такъв.

  10. Разликата в код е точно от извикването на някакви терминейт-и http://pastebin.com/seqnCtEG

    Тока обяснение мен ме обърква .. В моят код е възможно, моите функции предполагат да не се случват exceptions в тях .. Това че кода става по-бавен, лично мене ме изненадва 🙂

  11. Не е само тази разликата, цикъла е по-малко оптимизиран (редове 82-84 vs 29-30). Не бих очаквал 1 инструкция да дава толкова разлика, но какво да правиш – компютри 🙂