Search
Write a publication
Pull to refresh
1
0
Калмыков Юрий @Videoman

Пользователь

Send message

Да, всё верно. Тип вызова pascal отличается от stdcall противоположным порядком проталкивания аргументов. Я не хотел вдаваться в дебри различных типов вызовов, тем более, что сейчас в x64 многие из них уже не актуальны и унифицированы. Я лишь хотел этим проиллюстрировать откуда могли взяться слухи, что Windows писался на Pascal-е и что разработчикам на конференции задавали уже этот вопрос. Ответ был - Windows писался на С.

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

Я за Open Source, но от выводов в статье остаётся ощущение недосказанности. Логика подсказывает, что коммерческая компания не просто так тратит такую уйму сил и не просто для того, что бы поделиться своими наработками с сообществом. Я хотел бы ошибаться, но мне кажется, что на самом деле здесь больше решаются юридические тонкости и основная цель - защитить те или иные российские разработки от нападок извне или наоборот, а Open Source это только форма. Опять же, не вижу в этом ничего плохого, думаю так многие компании делают, но если это действительно так, то всё встает на своё место.

Вроде фильтры совсем базовые и простые но есть одно замечание и один совет.
Замечание - gamma у вас работает неправильно. В нем граничные значения динамического диапазона [0, 255] (или [0.0, 1.0], если удобно) должны переходить сами в себя. Поэтому лучше сначала отнормировать значение яркости к [0.0, 1.0], а потом уже возводит в степень, затем обратно.
Совет - так как фильтры точечные и каждый канал обрабатывается независимо, лучше считать их в таблице для всех значений динамического диапазона, [0, 255] например, и уже потом брать оттуда. Во-первых, это сделает скорость любого фильтра независимой от размера изображения и от сложности самого фильтра. Во-вторых, можно применять сразу несколько фильтров к таблице, а потом обсчитывать уже каналы изображения по ней. Это еще увеличит скорость обработки.

Современные процессоры устроены совсем не так, как в 80-х. Здесь, мне кажется, нужны замеры - насколько это медленнее. Нельзя же производительность в этом случае линейно, по количеству команд, мерить.

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

В любом случае, если в архитектуре нет команды adc, то будут накладные расходы, но не большие.

constexpr bool addc(uint32_t& value1, uint32_t value2, bool carry) noexcept
{
    value1 += value2;
    bool carry_new = value1 < value2;
    value1 += carry;
    carry_new = carry_new || (value1 < uint32_t(carry));
    return carry_new;
}

Вот например выхлоп для RISC-V:

 add     a0, a0, a1
 sltu    a1, a0, a1
 add     a2, a2, a0
 sltu    a0, a2, a0
 or      a0, a0, a1
 add     a0, a0, a2

Выглядит очень не плохо, на мой взгляд.

Это будет в два раза медленнее, чем это возможно. Зачем так делать? Я с трудом представляю архитектуру, где не было бы adc, но даже, если это так, то перенос легко вычислить для полной суммы:

value1 += value2;
bool carry = value1 < value2;

Он и так это без костылей оптимизирует: adc, sbb всякие, simd где может использует. О каких костылях речь ? На 32-х битной архитектуре 64-х битные целочисленные операции (long int) уже через вызовы отдельных функции выполняются, а как иначе, это костыли?

Потому-что на практике это не сложно сделать в виде сторонней библиотеки, а это принцип С++ - не тащить в компилятор то, что не нужно.
Есть подход big integer - когда число не ограничено по длине и фактически его придется хранить в динамической памяти (привет Питону). В этом случае придется расплачиваться быстродействием.
Есть подход long integer - когда необходимо работать с числами большими, чем поддерживаются процессором, но при этом фиксированной длины. Такие числа можно хранить на стеке и работать с ними почти также быстро как со встроенными, с поправкой на длину. Например, данный подход используется в Simple long integer math library for C++. Такие числа почти взаимозаменяемые со стандартными, не считая некоторых нюансов в автоматических преобразованиях.

Древнее оборудование? Метод близнецов - это же, по моему, buddy-allocator, который используется во всех операционных системах для выделения памяти. За счет скорости, простоты и неподверженности фрагментации, он идеально подходил для ядра ОС, где все страницы по замеру равны степени двойки. Он используется в ядре Linux, Windows, RTOS. Да и во многих современных Си-шных быстрых аллокатарах он используется как нижележащий слой, под аренами и прочими структурами. Так что, списывать его со счетов я бы не стал.

Ладно порядок, даже размер слова и байта пропустили.

Странно, что ничего не сказано про выравнивание. х86, по умолчанию, позволяет доступ к не выровненным данным, тогда как многие другие платформы жестко требуют выравнивания.

Всё верно. Это обратное следствие. Как всегда дилемма: скорость-память.

Также ещё из минусов make_shared: custom deleter не задать, aliasing constructor не вызвать.

Дополнения к std::shared_ptr и std::make_shared:
1. Без объяснения как на практике устроен std::shared_ptr, невозможно понять что за сценой присутствует control block и без std::make_shared у нас появиться дополнительный уровень косвенности и следственно лишнее выделение памяти под него. std::make_shared объединяет эти два выделения, делая создание и удалении быстрее.
2. std::weak_ptr естественно делает увеличение счетчика ссылок, но это отдельный счетчик, который также находится в control block-е.

На БК0010(01)/11/11M - на базе PDP-11 - не было целочисленных mul и div. op-коды были, но аппаратно команды были не реализованы. Даже сдвиг битовый был только одинарным: asl r0 - сдвинуть на один бит влево и вся роскошь. Чего уж тут говорить о Floating Point.

Под независимыми, я имел в виду «независимые» по коду, в конкретном модуле. Но их может связать сторонний по отношению к модулю класс, динамически. Многие просто об это забывают и из-за этого представляют себе возможности RTTI уж слишком примитивно.
Ну и что с того, что с наследует a и b? От этого связывание не становится статическим, а всё-равно происходит динамически в рантайме, при работе программы. Пофантазируйте: определения с у вас вообще может не быть, если подлинкована либа или подгружена dll с неизвестным вам классом.
(вопросы совместимости RTTI и целесообразности такого подхода на практике уберем за скобки)
Ну например, можно рассмотреть простейшую реализацию COM на dynamic_cast<>:
#include <iostream>
#include <memory>
#include <string>

// a.h include

struct a_base {

    virtual std::string who() = 0;
    virtual ~a_base() = default;
};

struct a : a_base {

    std::string who() override { return "a class"; }
};

// b.h include

struct b_base {

    virtual std::string who() = 0;
    virtual ~b_base() = default;
};

struct b : b_base {

    std::string who() override { return "b class"; }
};

// c.h include

struct c : a, b 
{
};

// somewhere else

b_base* create_b()
{
    return new c;
}

// main

int main()
{
    b_base* b = create_b();
    std::cout << b->who() << "\n";

    a_base* a = dynamic_cast<a_base*>(b);

    if (a != nullptr) {

        std::cout << "receive a_base from pointer to b_base\n";
        std::cout << a->who() << "\n";

    } else {

        std::cout << "can't receive a_base from pointer to b_base\n";
    }

    delete b;
    b = nullptr;

    return 0;
}
Обратите внимание, что иерархия класса a и класса b могут друг о друге ничего не знать, а также оба вообще могут не знать, что в программе есть класс, который наследует их оба. Однако это не мешает dynamic_cast отслеживать эту связь в рантайме и получать через b_base указатель на a_base.
В С++ всё это называет cross cast. Идея думаю понятна.
Динамик каст делает больше чем просто енамчик, там сохраняется информация о иерархии. Например с вашей реализацией вы не сможете привести к другой базе, то есть это не точно тот тип, но находящийся в той иерархии
На самом деле мало кто задумывается как работает dynamic_cast. С его помощью можно привести не просто к другому типу, который является наследником, а к типу вообще находящемуся во вне иерархии того или иного класса.

Information

Rating
Does not participate
Location
Москва, Москва и Московская обл., Россия
Date of birth
Registered
Activity