Страницы

Поиск по вопросам

пятница, 29 ноября 2019 г.

Как измерить время выполнения js скрипта в микросекундах (не в миллисекундах)

#javascript #время


Как засечь время на js в микросекундах? В частности мне надо суммировать время выполнения
кусочка кода, который выполняется быстро (менее 1 миллисекунды), но часто.
    


Ответы

Ответ 1



Чтобы измерить время в микросекундах (не миллисекундах) надо воспользоваться стандартной функцией performance.now(). Она возвращает вещественное число (время от начала выполнения процесса) в милисекундах, а дробная часть есть соответсвенно микросекунды. var time = performance.now(); // некий код time = performance.now() - time; console.log('Время выполнения = ', time); Также можно использовать console.time('mark') и console.timeEnd('mark') - но тут вывод в консоль и просуммировать полученные отрезки времени не удастся. Подробнее тут.

Чем отличаются потоки от нитей?

#java #многопоточность


Вроде как должно быть одно и тоже, так как thread переводят и как "поток", и как
"нить". Вроде как, при создании потока (нити) и в послед запуске он может разбиваться
на нити?

Это поток порождает себе подобных (потоки такие же как и он) или же он порождает
нити (которые видимо являются неполноценными потоками)? 

А еще, являются ли потоки чтения (с консоли, файла) и вывода (в файл) такими же потоками? 

Почему тогда мы не вызываем у них методы start и пр., а только close? 

Или же можно запустить нить создав лишь объект их типа? 

Неужели это все одни и те же потоки? И еще, наличие 2-х нитей c thread и с runnable
обусловлено тем, что с 1-ым нельзя наследоваться, но легче запускать, а со 2м можно
наследоваться, но сложнее запускать?
    


Ответы

Ответ 1



Вроде как должно быть одно и тоже , тк thread переводят и так , и так. Одно и то же. Вот если встретите термин Fiber - там все немного интересней, взаимодействие с ним происходит как с потоком, но классическим ОС-потоком он не является. Вроде как , при создании потока(нити) и в послед запуске он может разбиваться на нити? Разбиваться не может, порождать - может. Порожденные потоки имеют ровно те же возможности. А еще , являются ли потоки чтения(с консоли,файла) и вывода(в файл) такими же потоками? Нет, мы говорим про потоки выполнения, а это - потоки данных, т.е. просто последовательная перекачка байтов из одного места в другое. И еще , наличие 2х нитей c thread и с runnable обусловлено тем , что с 1ым нельзя наследоваться , но легче запускать ,а со 2м можно наследоваться , но сложнее запускать? не берусь судить за архитекторов API, но наследоваться от класса потока вам не стоит вообще никогда. В большинстве случаев вам так вообще потребуется ExecutorService, который возьмет на себя управление потоками, а вы будете просто отдавать ему задачи на выполнение в виде Runnable или аналога.

Ответ 2



Трудности перевода. "Потоки" - это threads и streams, хотя в источнике это означает абсолютно разные вещи. В литературе чаще всего придерживаются перевода "потоки" в обоих случаях (где из контекста понятно, о каких потоках идет речь), но некоторые персоны предпочитают подчеркивать отличие называя treads нитями. PS Да и streams в той же Java бывают совершенно разными: I/O streams, Stream API.

Как проверить есть ли у класса Т метод foo?

#c++


Есть ли возможность в C++ написать что-то похожее на это:

template
void f(T a)  {
  if ( Существет метод a.foo ) {
    a.foo();
  } else {
    myfoo(a);
  }
}


Т.е. функция должна вести себя по разному в зависимости от того есть у класса T метод
foo или нет.
    


Ответы

Ответ 1



Это делается так: #include struct A{ void foo(){} }; struct B{}; template struct Test{ typedef void(T::*P)(void); template struct True{char dummy[2];}; typedef char False; static False detect(...); template static True detect(U*); static const bool exists = (sizeof(False) != sizeof(detect(static_cast(0)))); }; int main(){ std::cout << std::boolalpha << Test::exists << std::endl; //false std::cout << std::boolalpha << Test::exists << std::endl; //true } Теперь о том, что же здесь творится. В коде используется идиома под названием SFINAE. Аббревиатура SFINAE расшифровывается как substitution failure is not an error и означает следующее: при определении перегрузок функции ошибочные инстанциации шаблонов не вызывают ошибку компиляции, а отбрасываются из списка кандидатов на наиболее подходящую перегрузку. Теперь посмотрим как будет себя вести этот код. В классе Test определено два типа True и False. Их важным свойством является то, что sizeof(False) != sizeof(True). Так же в нашем распоряжении есть два метода detect. Первый принимает указатель на T, второй принимает произвольное количество аргументов(эллипсис). Эллипсис является всеядным, и в тоже время обладает самым низким приоритетом при выборе перегрузки. При вызове detect(static_cast(0) компилятор пробует подобрать подходящую перегрузку. Более высоким приоритетом обладает шаблонная версия detect. Но если у типа T не окажется метода void foo(), то инстанцирование шаблона провалится. Но как мы помним substitution failure is not an error. Компилятор отдаст предпочтение эллипсису(тореточие). Узнать какая версия detect была выбрана мы можем по типу возвращаемого значения. А отличить типы мы можем по их размеру. Обратите внимание, в коде нет реализации методов detect. Тут используется тот факт что sizeof не вычисляет значение выражения, сразу извлекает тип. Таким образом методы detect никогда не будут вызваны. В итоге в переменной exists окажется true если у типа T есть метод void foo(). Весь код выполняется на этапе компиляции, и не дает никаких накладных расходов. Так же этот код скомпилировался онлайн-компилятором в режиме совместимости с C++98 UPD: В комментариях мне намекнули, что это не решает вашей проблемы. В какой-то мере это так. Полученное значение нельзя просто использовать в if-е, так как это приведет к ошибке компиляции. Нам нужен аналог if, который бы отрабатывал на этапе компиляции. В роли такого if-а может выступить специализация шаблонов: template::exists> struct Foo; template struct Foo{ static void foo(const T &t){ t.foo(); } }; template struct Foo{ static void foo(const T &t){ std::cout << "foo" << std::endl; } }; Структура Foo имеет две специализации. Первая для случая Test::exists == true, вторая для false. Ну и небольшая вишенка на торте для удобства, не добавляющая ничего нового кроме автоматического вывода типа аргументов: template void foo(const T &t){ Foo::foo(t); } Теперь main примет такой вид: int main(){ A a; B b; foo(a); //A::foo foo(b); //foo } Полный пример

Ответ 2



Всё ниженаписанное будет работать для C++, версии 14-го года минимум. У Вас, по сути, две проблемы: первая, это определить есть ли у класса функция, и вторая вызвать функцию в зависимости от того, есть ли она. Первая проблема решается довольно просто: template struct HasFoo: std::false_type {}; template struct HasFoo().foo()), void>::value>>: std::true_type {}; template constexpr bool HasFoo_v = HasFoo::value; Используется это так: constexpr bool hasFoo = HasFoo_v; Давайте допустим, что у нас следующие входные данные: struct A { void foo() { std::cout << "From class foo!\n"; } }; struct B {}; void foo(B) { std::cout << "From standalone foo!\n"; } Теперь, нам надо как-то вызывать разные функции, в зависимости от наличия функции foo. Тут перед нами встаёт первая проблема: если бы на дворе был 17 год, то вполне вероятно, что мы могли бы написать как-то так: A a; if constexpr (HasFoo_v
) { a.foo(); } else { foo(a); } Но у нас всё ещё 2016, поэтому придётся пойти другой дорогой: напишем свой constexpr if(жалкое подобие, хотя бы). Вот к такой реализации я пришёл: template struct ConditioanlCall { ConditioanlCall(F function) {} }; template struct ConditioanlCall { ConditioanlCall(F function) { auto ignore = false; function(ignore); } }; template void callConditional(F&& function) { ConditioanlCall{std::forward(function)}; } #define CONSTEXPR_IF(condition, action) \ callConditional([&](auto) action); Используется это так: int main() { A a; B b; CONSTEXPR_IF(!HasFoo_v, {foo(b);}); CONSTEXPR_IF(HasFoo_v, {a.foo();}); return 0; } Полный код можно найти по ссылке.

Понять где undefined behavour в арифметических выражениях

#c++ #c #c++11 #неопределенное_поведение


Довольно таки часто обсуждаемая тема, но тем не менее хотелось бы конкретнее разобраться,
где есть UB, а где нет.

Ниже несколько примеров, и мои мысли по поводу что есть что:

int i = 0, x = 1;
int a[6] = {0, 0, 0, 0, 0, 0};

i = ++i + ++i; // UB
i = i++ + ++i; // UB
x = i++ + ++i; // ?? я думаю что UB 
x = i++ + i++; // OK
a[i] += i++;   // OK ??
a[++i] = i++;  // UB ??
a[++i] = ++i   // UB
i += ++i;      // UB
j += j;        // OK


За всё время я уяснил одно правило - если в одном выражении значение объекта меняется
более одного раза - UB чистой воды, то есть понятно, что на каких-то платформах может
и будет происходит то, что ожидается, но вопрос в том, что стандарт в таких случаях
ничего не гарантирует - то есть либо говорится, что это undefined behavour, unspecified
behavour либо implementation defined behavour. 

Вопрос - могут ли возникнуть в этих примерах случаи, кроме UB?
    


Ответы

Ответ 1



Языки С и С++ фундаментально отличаются в одной важной детали: язык С++ старается максимально тщательно сохранять lvalue-ность результата выражения (lvalue-preserving language), а язык С наоборот - в большинстве случаев сразу беззаботно теряет lvalue-ность результата выражения (lvalue-discarding language) int a = 0, b = 1; a = b; // lvalue в C++, rvalue в С ++b; // lvalue в C++, rvalue в С 1 ? a : b; // lvalue в C++, rvalue в С (a, b); // lvalue в C++, rvalue в С Эти свойства данных языков диктуют серьезные различия в их подходу к упорядочению (sequencing) операций в выражениях. Первый стандарт С++ (С++98) пытался игнорировать этот момент и придерживаться унаследованного напрямую из С подхода к упорядочению, но в конечном итоге эта модель была признана дефектной и существенно переработана. В процессе этой переработки в С++ появилась упорядоченность, которой ранее не было. Как следствие, некоторые выражения, которые формально порождали UB в C++98, получили вполне определенное поведение в C++11. А C++17 добавил еще больше отношений упорядочения в язык С++, тем самым еще более расширив круг выражений, поведение которых определено. Поэтому ответ на вопрос о наличии UB в выражении может существенно отличаться между С и С++. Например, выражение i = ++i; порождает UB в С, но имеет совершенно определенное поведение в С++. Различие возникает по той причине, что в языке С++ гарантируется, что модификация переменной i под действием преинкремента произойдет до того, как будет завершено вычисление результата преинкремента. В языке С ничего подобного не гарантируется. Правила "если в одном выражении значение объекта меняется более одного раза" не существовало никогда и нигде. Более-менее правильной формой этого правила будет "если между парой соседних точек следования значение объекта модифицируется более одного раза, то поведение не определено". Также не следует забывать вторую часть этого правила: "если между парой соседних точек следования значение объекта модифицируется, а также присутствует независимое чтение значения этого объекта, то поведение не определено". После переработки в С++11 эти правила, однако, стали применимы только к языку С, но не к языку С++ (см. пример выше). Также стоит заметить, эти правила основаны на концепции точки следования, в то время как современные спецификации этих языков (как С++, так и С) решили отказаться от этой концепции и заменить ее концепцией упорядочения (sequencing, sequenced before, sequenced after). Но, еще раз, в языке С эти правила по-прежнему достаточно точно отражают ситуацию с UB в выражениях. Новое же правило, применимое как в С, так и в С++, звучит так Если побочный эффект, воздействующий на скалярный объект, неупорядочен (unsequenced) по отношению к другому побочному эффекту, воздействующему на этот же скалярный объект, или по отношению к вычислению значения этого же скалярного объекта, то поведение не определено. Различия же между С и С++ сводятся к отличающимся гарантиям того, что является упорядоченным (sequenced), а что нет (unsequenced). Упорядоченность оговаривается в описаниях конкретных операторов языка. В ваших примерах i = ++i + ++i; // UB и в С, и в С++ i = i++ + ++i; // UB и в С, и в С++ x = i++ + ++i; // UB и в С, и в С++ x = i++ + i++; // UB и в С, и в С++ a[i] += i++; // UB и в С, и в С++11, все в порядке в С++17 a[++i] = i++; // UB и в С, и в С++11, все в порядке в С++17 a[++i] = ++i // UB и в С, и в С++11, все в порядке в С++17 i += ++i; // UB в С, все в порядке в С++11 (?), все в порядке в С++17 j += j; // OK (Я не уверен в своей трактовке i += ++i. A += B определено через A = A + B, но i = i + ++i - это UB и в С++. Но подозреваю, что ситуацию спасает то, что A в A += B вычисляется только один раз.)

Разница между catch, catch(Exception) и catch(Exception ex)

#c_sharp #исключения #try_catch #c_sharp_faq


Допустим, я не планирую использовать переменную ex и мне надо, чтобы обрабатывалась
любая ошибка.

try
{
    ...
}
catch(Exception ex)
{
    return;
}


Надо ли в таком случае объявлять переменную ex?

Или можно сделать так:

try
{
    ...
}
catch(Exception)
{
    return;
}


Или вообще вот так:

try
{
    ...
}
catch
{
    return;
}


В чем разница и как правильнее?
    


Ответы

Ответ 1



Переменную нужно объявлять, если в дальнейшем планируется её как-то использовать. Если важен только сам факт перехвата - достаточно указать всего лишь тип. Различие в catch(Exception) и пустом catch имеет место быть, если нужно перехватывать не CLS-совместимые исключения. Более подробнее об этом можно почитать на msdn. По ссылке как раз видно, что есть смысл и в таком коде: catch(Exception) { ... } catch { ... }

Ответ 2



Исключения также можно выстраивать одно за другим по мере увеличения. Если error не поппадает ни в одно исключений, то обезательно попадет в catch (Exception ex) как показанно на примере ниже. finally блок выполняется в при любых условиях. Console.WriteLine("Enter a number:"); while (true) { try { int yourNum = Convert.ToInt32(Console.ReadLine()); if (yourNum < 1 || yourNum > 10) { throw new Exception("Number must be between 1 and 10"); } Console.WriteLine("Your number is {0} !", yourNum); break; } // ловит ошибку неправильного формата. catch (FormatException) { Console.WriteLine("Not a number. Please try again"); } // ловит ошибку если число не попадает в допусимый ряд int catch (OverflowException) { Console.WriteLine("Not a valid integer. Pelase try again"); } // если ни один из предыдущих catch не скработал ловим изящно (gracefully) в ex переменную где ex.ToString() выводит нам на экран в string формате catch (Exception ex) { Console.WriteLine(ex.ToString()); } // код выполняется независимо есть ли ошибка или нет. finally { Console.WriteLine("Thanks for playing!"); } }

Ответ 3



Если вы хотите отловить любое исключение и контекст не важен, пишите: try { //... } catch { //... } Если важен тип исключения то: try { //... } catch (Exception) { //... } Но если логика завязана ещё и на данных в классе исключения то соответственно: try { //... } catch (Exception ex) { //... }

Ответ 4



1) Ловим конкретный тип исключения и заносим его в переменную. Переменную можно проанализировать в блоке и ,например, попробовать восстановить правильный ход программы 2) Ловим конкретный тип исключения, но само исключение не анализируем 3) Не уверен, но скорее всего ловятся абсолютно все исключения. P.S Так как все исключения наследуются от Exception, то 2 и 3 примеры аналогичны.

Как получить имя переменной внутри функции

#c_sharp


Как можно получить имя передаваемой переменной в вызываемом методе?
Только передавать дополнительным параметром?
Может есть какой аналог CallerMemberNameAttribute только для получения имени передаваемой
переменной?

На примере:

var variable = "some_value"; // имя переменной nameof(variable)
DoAction(variable);


public void DoAction(string value)
{
    var valueVariableName = ... // хочу получить имя переменной ("variable")
}


Буду благодарен за любые соображения.



Примечание: Изначально задумывалось сделать наподобие

public void DoAction(string value, [PreviousParameterName] string valueParameterName
= null)
{
    var valueParameterName = ... // имя переменной (== "variable")
}


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


Ответы

Ответ 1



В C# 8.0 завезут новый атрибут: CallerArgumentExpression С его помощью можно будет получить имя переменной, которое было передано. Цитирую ответ, который мне дали тут: Если метод объявлен так: public static class Debug { public static void Assert(bool condition, [CallerArgumentExpression("condition")] string message = null); } и вызывается так: Debug.Assert(someBoolean); Debug.Assert(array != null); Debug.Assert(array.Length == 1); , то компилятор подставит значение второго аргумента: Debug.Assert(someBoolean, "someBoolean"); Debug.Assert(array != null, "array != null"); Debug.Assert(array.Length == 1, "array.Length == 1"); Соответственно никаких просадок в производительности не будет, так как это будет делаться на этапе компиляции.

Ответ 2



Например, вот так: static void Main(string[] args) { var world = "Hello, {0}!"; DoAction(() => world); } static void DoAction(Expression> value) { var me = (MemberExpression)value.Body; var variableName = me.Member.Name; var variableValue = value.Compile()(); Console.WriteLine(variableValue, variableName); } Но это будет работать медленно. И точно так же будет медленно работать любая другая подобная "технология". Просто потому что уговорить компилятор сохранить имя переменной можно только превратив ее в поле или свойство - а способа быстро достать из объекта значение поля с неизвестным именем нет.

Ответ 3



Если вам имя переменной требуется не безусловно, а только в особых случаях с отладочными целями - то при наличии отладочных символов его можно попытаться достать из исходника. Например, как-то вот так: static void Main(string[] args) { string foobarbaz = null; Assert(foobarbaz != null); Assert(false); Assert(true && (false || new[] { true, false }[1])); Assert(false /* || true*/); Assert( false // || true ); Assert /*trap*/ (false); Assert($"{{" == null); } [Conditional("DEBUG"), MethodImpl(MethodImplOptions.NoInlining)] static void Assert(bool expression) { if (expression) return; string expression_raw; var frame = new StackTrace(fNeedFileInfo: true).GetFrame(1); try { using (var file = File.OpenText(frame.GetFileName())) { for (int k = frame.GetFileLineNumber() - 1; k > 0; k--) file.ReadLine(); for (int k = frame.GetFileColumnNumber() - 1; k > 0; k--) file.Read(); var parser = new Parser(file); parser.Ensure(nameof(Assert)); parser.ReadUntil('('); parser.Ensure("("); parser.BeginRecord(); parser.ReadUntil(')'); expression_raw = parser.EndRecord(); } } catch (IOException) { expression_raw = ""; } Console.WriteLine($"Assertion {expression_raw.Trim()} failed at {frame.ToString()}"); } private class Parser { private readonly TextReader input; private StringBuilder output; private char current, prev; public Parser(TextReader input) { this.input = input; } public void BeginRecord() { output = new StringBuilder(); } public string EndRecord() { var result = output.ToString(); output = null; return result; } private char Read() { var c = input.Read(); if (c == -1) throw new EndOfStreamException(); prev = current; current = (char)c; output?.Append(char.IsWhiteSpace(current) ? ' ' : current); return current; } public void Ensure(string v) { foreach (var c in v) { if (Read() != c) throw new IOException(); } } public void ReadUntil(char end) { while (input.Peek() != end) { switch (Read()) { case '(': ReadUntil(')'); Read(); break; case '[': ReadUntil(']'); Read(); break; case '{': ReadUntil('}'); Read(); break; case '\'': ReadCharacter(); if (Read() != '\'') throw new IOException(); break; case '"': ReadString(formattable: prev == '$'); Read(); break; case '/': if (prev == '/') { do { Read(); } while (current != '\n' && current != '\r'); } break; case '*': if (prev == '/') { current = '\0'; do { Read(); } while (prev != '*' || current != '/'); current = '\0'; } break; } } } private void ReadCharacter() { if (Read() == '\\') Read(); } private void ReadString(bool formattable) { while (input.Peek() != '"') { if (formattable && input.Peek() == '{') { Read(); if (input.Peek() != '{') { ReadUntil('}'); } Read(); continue; } ReadCharacter(); } } }

Почему в переопределяемом (@Override) методе нельзя пробросить Exception?

#java #исключения #override


Всем добрый вечер.

Столкнулся с интересной особенностью переопределяемых методов — исключения в них
нельзя пробросить, только обработка внутри метода. Но почему? Хочется получить исчерпывающий
ответ на этот вопрос.
    


Ответы

Ответ 1



Есть два вида исключений в Java — checked и unchecked, подробнее можно почитать, например, в этой статье. Checked исключения проверяются на этапе компиляции приложения, и должны где-то отлавливаться (catch), а методы, которые выбрасывают такие исключения, должны иметь в сигнатуре тип исключения, который может быть выброшен (напр. void method() throws Exception). При переопределении (@Override) метода, в сигнатуре которого не указан throws, нельзя бросить checked-исключение, потому что компилятор не сможет его отследить. Нерабочий пример: class A { void foo() {} } class B extends A { @Override void foo() throws Exception {} } A obj = new B(); obj.foo(); // Компилятор рассматривает obj как A, в котором нет throws, и не знает о том, что он бросает checked-исключение Вы можете бросить unchecked-исключение, на основе RuntimeException или Error, без добавления throws в сигнатуру перезагруженного метода. Конечно, если вам не нужно именно checked-исключение.

Ответ 2



Можно, если переопределяемый метод суперкласса объявлен, как выбрасывающий тот же тип исключений или базовый к нему. Это один из основных принципов ООП - LSP.