Быстрый вывод
#include <windows.h>
#include <cstring>
#include <stdio.h>
#include <conio.h>
int main()
{
const int T=100;
char *s="-1.2";
LARGE_INTEGER t1,t2,t3;
int f,f1,f3;
HANDLE j=GetStdHandle(STD_OUTPUT_HANDLE);
m:
QueryPerformanceCounter(&t1);
for(int i=0;i<T;i++){
WriteConsole(j,s,strlen(s),0,0);
}
QueryPerformanceCounter(&t2);
std::cout<<"\x20"<<t2.LowPart-t1.LowPart<<std::endl;
getch();
QueryPerformanceCounter(&t1);
for(int i=0;i<T;i++)std::cout<<-1.2;
QueryPerformanceCounter(&t2);
std::cout<<"\x20"<<t2.LowPart-t1.LowPart<<std::endl;
getch();
QueryPerformanceCounter(&t1);
for(int i=0;i<T;i++)printf("%f",-1.2);
QueryPerformanceCounter(&t2);
std::cout<<"\x20"<<t2.LowPart-t1.LowPart<<std::endl<<std::endl;
getch();
goto m;
return 0;
}
для 1 команды в среднем
принтф меньше 100 тактов
сиаут до 250
консол от 400 до пару тысяч
так же и при увеличении количества (10000)
3 милиона
полтора
пол
в чом принципиальные различия в функциях и что так сильно влияет на скорость? кто сталкивался или подскажыте статью какуюто где можно почитать
:eek: ужас, я вижу goto
:eek: ужас, я вижу goto
А что в этом такого ужасного?
А что в этом такого ужасного?
http://khpi-iip.mipk.kharkiv.edu/library/extent/dijkstra/pp/ewd215.html
Пользоваться надо с умом просто. Как и динамическими массивами. Сравните:
{
for (...)
{
for (...)
{
if (...) goto to_exit;
}
}
}
to_exit:
for (...)
{
for (...)
{
for (...)
{
if (...){ bExit = true; break;}
}
if (bExit) break;
}
if (bExit) break;
}
Это-то все понятно. Но все предостережения от использования goto в основном построены на вопросах стилистики. Да, goto делает код трудносопровождаемым, трудным в отладке и т.д. и т.п. И я отнюдь не призываю его использовать (и сам этого не делаю).
Но в данном случае автор написал маленькую тестовую программку и причем только для себя. Здесь думаю вполне можно позволить себе роскошь "нарушить" правила и использовать нерекомендуемые конструкции :)
И это не столь ужасно в данном конкретном случае. Я к этому.
Пользоваться надо с умом просто.
Абсолютно согласен. =)
Есть аргументы за и против goto. Изложенные Дейкстрой, Кнутом, Макконелом и др. И в общем случае их совет один - не надо пользовать goto.
Код, который Вы привели как пример - это код с "плохим запахом" (чит. Фаулера). Именно поэтому в данном примере без goto хуже чем с ним.
Хотите и с этим поспорить?
Нарушая правила в малом, невозможно придерживаться их в большом.
Более того код топикстартера элементарно переписывается с
while(TRUE) { } или for( ; ; ) { }.
...
if (Error2) goto label;
...
if (Error3) goto label;
...
label:
Поддерживаю hardcase :)
...
if (Error2) goto label;
...
if (Error3) goto label;
...
label:
Ну, это уже по МакКоннеллу :) Для случая обработки ошибок без использования исключений.
Точно такой -- вроде нет )). Но goto пользовался. Что-то вроде такого:
Если посмотреть на рекомендации М. Файлера в книжке Рефакторинг. Улучшение существующего кода, а именно "Замена кода ошибки исключительной ситуацией", то можно понять, что goto в Вашем втором примере тоже "попахивает"
Иди почитай подпись кота. Часть ближе к концу
Я так и думал, что Вы опуститесь до хамства, и ничего конструктивного, внятного и адекватного сказать не сможете.
Иди почитай подпись кота. Часть ближе к концу
Вам того же
Тебе код приводили и не раз. Что сейчас, что вчера с BroKer'ом. Что-то пытались тебе объяснять, доказывать.
От тебя же ничего, кроме каких-то запахов не слышно. Вчера было чуток кода - пурга полная. Сегодня вообще одни "запахи". Конструктифф, однако. Внятный, таки
Поддерживаю hardcase :)
Я тоже с ним согласен, просто меня немного поразило такое удивление
[QUOTE=H010d Freeze;]
ужас, я вижу goto
[/QUOTE]
не столь "плохим" в данном случае оператором :)
Сам избегаю goto, но вот не поверю, что вы всегда все пишете по правилам даже в таких тестовых прогрммках :)
Тебе код приводили и не раз
Применительно к текущему посту. Это Вы хотите сказать, что два Ваших куска кода - это то, что вообще можно кому-то показывать?
Да тем более как пример, да тем более как довод за goto?
Почитайте книжки и статьи авторов, которых я упомянул. Вот Вам конструктифф. А потом посмотрите на свой код...
Что-то пытались тебе объяснять, доказывать.
Вы спрашивали и говорили, что вам не понятна аргументация. Ничего объяснять и уж тем более доказывать конкретно Вы и не пытались.
Коллега, Вы сгущаете краски.
Я просто обратил внимание на goto. Без негатива. Надо было смайлик поставить :)
2Нездешний
Кто же знал, что тут есть сторонники goto, которых это заденет за живое
привычка от асма. в любом случае - кому что нравится, главное работало бы.
вы по теме по теме пишите...;)
...
if (Error2) goto label;
...
if (Error3) goto label;
...
label:
Ну это типичный пример обработки ошибок в C, а как насчет С++?
if (!Условие1)
{
MessageBox("Message1");
//пара специфических телодвижений именно для этой ошибки
goto label;
}
//математика
if (!Условие2)
{
MessageBox("Message2");
//пара специфических телодвижений именно для этой ошибки
goto label;
}
//математика
if (!Условие3)
{
MessageBox("Message3");
//пара специфических телодвижений именно для этой ошибки
goto label;
}
...
label:
//много общих действий
Я не сторонник и не противник goto, пользуюсь им крайне редко. Просто не нравятся категоричные безапеляционные заявления. И тыканье в книжки с тоном "да тут для вас, дураков, все написано, идите почитайте ХХХ". Не надо считать остальных нечитающими придурками. Опять же, пишут книги тоже люди, и если где-то что-то написано, не обязательно так оно всегда и есть. И не обязательно читающий правильно понял написанное.
??? А в чем собственно была бы разница даже с этим goto я не понял?
1. Не понял, почему должны возникнуть такие сложности с исключением в данном случае.
2. Не вижу проблемы приминения тех же исклюений в этом случае аналогично исспользованию goto, тоесть безо всяких сложностей.
if (!det1)
{
MessageBox("Message1");
//пара специфических телодвижений именно для этой ошибки
goto label;
}
//вычисление x, используя деление на det1
if (!x)
{
MessageBox("Message2");
//пара специфических телодвижений именно для этой ошибки
goto label;
}
//вычисление чего-то с делением на x и на det1
if (!Условие3)//невыполнение не вызывает стандартного исключения
{
MessageBox("Message3");
//пара специфических телодвижений именно для этой ошибки
goto label;
}
...
label:
//много общих действий
Если все заключить в один блок try-catch, то для выполнения специфических операций придется выяснять, где именно возникло исключение, т.е. это дополнительные проверки + switch в блоке catch.
Выгоды перед goto никакой + расходы на раскрутку.
Если попробовать несколько блоков try-catch последовательно, придется либо опять же использовать goto в блоках catch, либо вводить кучу флагов и их дополнительных проверок, чем дальше, тем больше.
Если все заключить в один блок try-catch, то для выполнения специфических операций придется выяснять, где именно возникло исключение,
А с goto не придется? Еще раз спрашиваю, в чем по вашему принципиальная разница в этих подходах в данной задаче? =)
x2 = ...; //вычисление x2
x3 = ...; //вычисление x3
...
xN = ...; //вычисление xN
y = 1 / x1 / x2 / ... / xN;
Любой из x может оказаться нулем.
Для каждого из х есть некоторые свои действия, которые необходимо выполнить, если он окажется нулем.
Для некоторых х есть некорректные значения, которые не вызовут стандартного исключения (т.е. придется проверять и возбуждать исключение самостоятельно).
В процессе вычисления некоторых х может использоваться деление на одно из предыдущих х
if (!x1 || x1 == some_value1)
{
MessageBox("Message 1");
goto label;
}
x2 = ...; //вычисление x2
if (!x2)
{
MessageBox("Message 2");
goto label;
}
...
xN = ...; //вычисление xN
if (!xN || xN == some_valueN1 || xN == some_valueN2)
{
MessageBox("Message N");
goto label;
}
y = 1 / x1 / x2 / ... / xN;
label:
Сразу скажу, я не хочу поднимать холивар "коды ошибок против исключений". Просто хочу отметить, что в последнем примере исключения не внесли бы никакой дополнительной сложности, а напротив, отделили бы код уведомления об ошибки от всего остального.
Зачем? Например, чтобы можно было выполнять автоматические тесты для кода, выполняющего процессинг.
{
public:
std::string msg; // я знаю, что public-поля - это плохо, это для простоты
EProcessingError(const std::string &amsg) msg(amsg) {}
};
void process()
{
x1 = ...; //вычисление x1
if (!x1 || x1 == some_value1)
{
//пара специфических телодвижений именно для этой ошибки
throw EProcessingError("Message 1");
}
x2 = ...; //вычисление x2
if (!x2)
{
//пара специфических телодвижений именно для этой ошибки
throw EProcessingError("Message 2");
}
...
xN = ...; //вычисление xN
if (!xN || xN == some_valueN1 || xN == some_valueN2)
{
//пара специфических телодвижений именно для этой ошибки
throw EProcessingError("Message N");
}
y = 1 / x1 / x2 / ... / xN;
}
try
{
process();
}
catch (EProcessingError &e)
{
MessageBox(e.msg);
}
Если стоит задача не использовать исключения вовсе (Гугл-стайл кодинг), то все замечательно.
Если вся логика выполняется в серьезном "тяжелом" цикле и обработка ошибок - это некая перенастройка цикла, то тоже все ок.
Но в случае когда операция выполняется относительно нечасто, а ошибки все-таки нужно обрабатывать корректно и, более того, требуется внимание пользователя, можно обойтись всеголишь двумя блоками Try.
1) Создаем базовый класс исключений, что-то типа EMathError.
2) Для каждого варианта ошибки создаем свой класс исключений, наследованный от EMathError.
В каждой точке программы, где требутся проверка на ошибку, точно также вычисляем предикат, но вместо безусловного перехода генерируем соответствующие ситуации исключение.
try {
...
if(!p1) {
throw EError1();
}
...
if(!p2) {
throw EError2();
}
...
if(!pN) {
throw EErrorN();
}
} catch(EError1 e1) {
// действия специфичные для ошибки 1
throw e1;
} catch(EError2 e2) {
// действия специфичные для ошибки 2
throw e2;
} catch(EErrorN eN) {
//действия специфичные для ошибки N
throw eN;
}
} catch(EMathError e) {
// общие действия.
}
Плюсы:
+ нету безусловного перехода.
+ логика программы не прерывается кодом обработки ошибок
+ обработка ошибок собрана в одном месте
+ общая часть обработки ошибок явно выделена в тексте
Минусы:
- требуется поддерживать иерархию классов исключений.
- накладные расходы на раскрутку стека
// больше пока не вижу
З.Ы. за синтаксические ошибки не пинайте больно :)
принтф меньше 100 тактов
сиаут до 250
консол от 400 до пару тысяч
так же и при увеличении количества (10000)
3 милиона
полтора
пол
в чом принципиальные различия в функциях и что так сильно влияет на скорость? кто сталкивался или подскажыте статью какуюто где можно почитать
Во многом влияет внутренний буфер команд. АПИ WriteConsole честно пишет (через WriteFile) в StdOut-поток вывода, создаваемый операционкой. С/С++ способы вывода в конечном счете (в Windows) операются на эту же самую функцию (WriteFile). Помимо этого они иногда имеют внутренний буфер (у cout он есть), а писать в буфер быстро, также время тратится на разбор символов и раскрутку стека (printf) (но это тоже достаточно быстро).
По поводу cout. После вывода, если в конце строки не ставится endl, нужно вызывать flush для сброса буфера, иначе данные просто не попадут в StdOut.
Это спорный плюс. )) Обусловленный личным отношением к goto
С помощью комментариев многого можно добиться.
С остальным согласен. Рассматривал как раз такой вариант тогда в качестве альтернативы. Но решил "не множить сущностей без надобности".
2 H010d Freeze
Один из худших возможных вариантов с использованием исключений. Смотрите плюсы у hardcase. У вас из них присутствуют только
+ общая часть обработки ошибок явно выделена в тексте
т.е. самые несерьезные.
Я не претендовал на лучшее решение по обработке ошибок.
Я показал, что использование исключение вместо goto не доставило бы неудобств.
Разумеется, без goto можно было обойтись. Можно было бы воспользоваться исключениями, но это привело бы к куче вложенных блоков try (ну, или последовательных + проверка флагов). Применение исключений было в этом случае довольно неудобно.
Доставило
- требуется поддерживать иерархию классов исключений.
- накладные расходы на раскрутку стека
Это спорный плюс. )) Обусловленный личным отношением к goto
Нормально я к нему отношусь: пользуюсь крайне редко, но метко :)
Я этого не указал, но имел в виду то, что инструментам рефакторинга будет совсем ясно где что варится - комментариев на программёрском они покуда не понимають.
Да, по поводу раскрутки стека. Это нужно специяльно измерять. Сдается мне, что если исключение ловится в том же стековом фрейме, где и генерируется, то возможно оно эквивалентно безусловному переходу.
Вообще-то да. Согласен. Читал у нескольких авторов, что исключения потому так и назвали, что они должны возникать в действительно исключительных ситуациях. Использовать же их для обычного программного управления, не есть хорошо, так как создатели компиляторов C++ при реализации обработки исключений не заботились особо об эффективности. По крайней мере это относится к C++.
Скажем, это вырожденный случай использования try-catch/throw конструкций. А раскрутка стека и в правду, не является проблемой когда мы используем исключения по назаначению.