Как стать автором
Обновить

Комментарии 226

>Так вот, а в C передать массив в функцию нельзя. Точнее, можно, не нет специального синтаксиса, чтобы показать компилятору, что в функцию передаётся именно массив. Если написать

void f (int a[]) — это разве не говорит компилятору, что будет именно массив? То что туда можно пихнуть не массив, а в принципе что угодно — это скорее дополнительная фича языка.

>А значит, если мы передаём в функцию «массив» a и «массив» b, то передаваться будут указатели. А значит, у компилятора нет никакой информации о том, собираемся ли мы, используя один указатель, читать/писать память, доступную из другого, или нет

Какая связь массивов и указателей с тем, что две перемененные не зависят друг от друга внутри функции? Если бы компилятор знал что то это массивы, то я бы не смог один в другой записывать?

Смысл в том, что массивы A и B, определенные в Fortran, однозначно не пересекаются.


Аналог в C++ -


Type * __restrict A, Type * __restrict B;

Этот хинт объясняет компилятору, что объём памяти, на который указывает A, в пределах функции точно не пересекается с объёмом памяти, на который указывает B. Это позволяет оптимизировать многие операции, например, такие, как копирование данных.


Подробнее https://ru.wikipedia.org/wiki/Restrict


P.S.: Нотация [] в C/C++ идентична нотации *. По крайней мере на практике.

void f (int a[]) — это разве не говорит компилятору, что будет именно массив?

Нет. Эта конструкция вообще ничем не отличается от void f (int *a). Да, это совершенно нелогично, но это так. Можете проверить, что в любом коде замена одной конструкции на другую ничего не меняет

Есть только один случай, когда int[] и int* не эквивалентны — перегрузка вызовов в C++:

void f1(int *a);
void f2(int a[]);
void f3(int a[100]);
void f4(int (&a)[100]);


Здесь f1, f2, f3 — идентичные функции, но функция f4 может принимать на вход исключительно ссылки на массивы из 100 интов.

С одной стороны, я не совсем понимаю, какое отношение вариант f4 имеет к вопросу эквивалентности (или неэквивалентности) int [] и int *. В этом варианте параметр имеет тип int (&)[100], а это не int [] и не int *. Поэтому как это может быть "одним случаем, когда int[] и int * не эквивалентны" — в упор не ясно.


Если говорить именно об int [], то никакой неэквивалентности быть не может.


С другой стороны, если говорить об общем случае типа T [N] в списке параметров функции, то определенные отличия c T * есть. Например, в С требуется, чтобы T был полным (complete) типом (требование снято в С++). Также в обоих языках требуется положительность N, если оно указано. Эти требования, понятное дело, не распространяются на вариант T *.

С одной стороны, я не совсем понимаю, какое отношение вариант f4 имеет к вопросу эквивалентности (или неэквивалентности) int [] и int *. В этом варианте параметр имеет тип int (&)[100], а это не int [] и не int *. Поэтому как это может быть «одним случаем, когда int[] и int * не эквивалентны» — в упор не ясно.

Это имеет практическое значение, т.к. позволяет сделать так, чтобы для T* вызывалась одна функция, а для T[N] другая.

Лучше дайте ответ на вопрос: верно ли, что если типы T1 и T2 эквивалентны, то и типы T1* и T2* будут эквиваленты?

void f(int *a);
void f(int a[]);
void f(int a[100]);

void g(int **a);
void g(int (*a)[]);
void g(int (*a)[100]);

void h(int *(&a));
void h(int (&a)[]);
void h(int (&a)[100]);


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

А самое смешное, что во всех случаях typeid будет возвращать одинаковый результат, т.е. typeid(int[100]) == typeid(int[]) == typeid(int*).

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


Поэтому еще раз: тип int (&)[100] не имет ничего общего с типами int * и int [100], поэтому ваш пример с f4 никакого отношения к вопросу эквивалентности int * и int [100] не имеет. Т.е. написана ерунда.


Далее: да, в языке С и С++, если типы T1 И T2 эквивалентны, то и типы T1 * И T2 * тоже эквивалентны.


Но вы, очевидно, запутались в природе "эквивалентностей" наблюдаемых в списках параметров функций. На самом деле в языках С и С++ типы T [N] и T * эквивалентными не являются и никогда не являлись. "Эквивалентность" о которой идет речь в данном случае — не более чем следствие неявной замены типа T [N] на тип T * в процессе интерпретации объявления функции языком. Эта подмена — четко выделенный и оговоренный в спецификациях этих языков отдельный шаг процесса такой итерпретации, а не следствие какой-то врождленной натуральной "эквивалентности" T [N] и T *.


Еще раз напомню, кстати, что даже в параметрах функций коррекность обявления типа T [N] проверяется еще до выполнения вышеупомянутой подмены, то есть при неполном типе T и/или неположительном значении N вы получите ошибку в языке С, в то время как проблем с T * не было бы. Т.е никакой полной "эквивалентности" тут нет.


А уж откуда вы взяли бред про typeid(int[100]) == typeid(int[]) == typeid(int*) — ума не приложу. В языке С++ все эти typeid дают попарно неравные результаты


#include <typeinfo>
#include <iostream>

int main()
{
    std::cout << (typeid(int[100]) == typeid(int[])) << std::endl;
    std::cout << (typeid(int[100]) == typeid(int *)) << std::endl;
    std::cout << (typeid(int[]) == typeid(int *)) << std::endl;
}

Вывод


0
0
0

http://coliru.stacked-crooked.com/a/84c8488ed029d2e0

Т.е. написана ерунда.

… которая вполне успешно используется в STL в функции begin, например.

А уж откуда вы взяли бред про typeid(int[100]) == typeid(int[]) == typeid(int*) — ума не приложу. В языке С++ все эти typeid дают попарно неравные результаты

Наверное потому, что мой компилятор (Intel C++ Compiler) здесь выдаёт true, а не false.
… которая вполне успешно используется в STL в функции begin, например.

Где и как именно в функции begin используются "исключения из "эквивалентности" int * и int []?


Наверное потому, что мой компилятор (Intel C++ Compiler) здесь выдаёт true, а не false.

Ну уж я не знаю, как мог Intel C++ Compiler так опростоволоситься. У меня нет под руками живого Intel C++ Compiler, но разглядывание ассемблера от Intel C++ Compiler на godbolt показывает, что для этих типов он генерирует раздельные type_info объекты...

«Эквивалентность» о которой идет речь в данном случае — не более чем следствие неявной замены типа T [N] на тип T * в процессе интерпретации объявления функции языком.

Да, именно этот момент я и упустил.
T[N] меняется на T*, но (T*)[N] на T** — нет.

Ну уж я не знаю, как мог Intel C++ Compiler так опростоволоситься.

Возможно, баг. У меня при компиляции в x86 вывод 1, 1, 1, а при компиляции в x64 вывод 1, 0, 0. Должно быть 0, 0, 0, понятное дело.
> Вот только есть один нюанс. Массив a — это массив указателей. А не единый двумерный массив, одним куском размещённый в памяти.

Это ОШИБОБЧНАЯ логическая установка, которая ложна для массивов произвольного размера. Для маленьких массивов, действительно, будет наблюдаться различие в скорости доступа. При этом, с действительно большими матрицами работать просто будет невозможно, потому что ОС не сможет выделить непрерывный блок памяти достаточного объёма. Особенно важно это для 32 битных программ. На 64 битных системах у меня случался bad_alloc для матриц буквально в десяток гигабайт объёмом. Вообще, если размер матрицы превышает размер страницы памяти, то нет разницы, линейный блок памяти или из нескольких кусков — накладные расходы по доступу к памяти превысят время одного лишнего умножения при вычислении индекса.

А если идёт обработка изображений, то тут для ускорения нужно уже делать квадратную матрицу из квадратных матриц. А значит без класса транслирующего адреса всё равно не обойтись.

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

Это если массив указателей вообще влезет в кеш...

НЛО прилетело и опубликовало эту надпись здесь
Во времена Вин98 можно было точно, потому что у меня была какая-то программа для дефрагментации оперативной памяти. Не уверен, что она действительно работала, но я был молод и доверчив…
С практической точки зрения ответ, скорее, нет.
Поясните, пожалуйста, вопрос. Что такое «дефрагментация» RAM и для чего она нужна?

1. На уровне системы проблем нет благодаря механизму виртуальной памяти (за исключением всяких контроллеров, где нет защищённого режима, но там динамическую память не используют обычно). Память, с которым работает приложение в пользовательском режиме, может быть постранично отмаплена куда угодно.

2. А вот на уровне приложения проблема есть в 32-битном режиме за счёт ограниченности адресного пространства. Если вы выделили кусок памяти, то, вообще говоря, не можете его подвинуть, потому что вам придётся изменить значения всех указателей, которые на эту область памяти ссылаются. Неприятности вызывают и отображаемые в юзерспейс динамические библиотеки.

Чтобы иметь возможность двигать объекты в динамической памяти, консолидируя блоки, вы можете реализовать на C++ механизмы, используемые языкам со сборщиком мусора, за счёт небольшого падения в производительности и десятикратного геморроя из-за неправильно выбранного языка программирования.

P.S. То, что далёкие от программирования пользователи называют «дефрагментацией RAM» — это просто принудительный сброс данных приложений в своп и очистка буферов/кэшей.

Доступ к одному линейному куску a[x+y*sy+z*sz] действительно быстрее a[z][y][x] из-за меньшего числа доступов в память. Ещё в тестах a[x+y*sy+z*sz] работало заметно быстрее, чем a[(z*sz+y)*sy+x], из-за суперскалярности процессора.
Проверено на 3D массивах размером сотни мегабайт. Конечно, ещё всё зависит от паттерна доступа. Не буду спорить, что на бОльших объёмах может быть bad_alloc.Но на "средних" объёмах разница в пользу линейной схемы хранения была заметна.

> Ещё в тестах a[x+y*sy+z*sz] работало заметно быстрее, чем a[(z*sz+y)*sy+x]
Может потому, что эти уравнения не равнозначны? Вот видите, даже в таком простом примере по памяти с ошибками реализовали. А это минимум пять минут на компиляцию и отладку.)
Я же не спорю, что линейный блок памяти быстрее «рваного». Я утверждаю другое, что нет смысла биться за непрерывный кусок, если матрица очень большая. Шансы на bad_alloc резко возрастают, а падение производительности ещё нужно доказать (в зависимости от паттерна доступа падения производительности может и не быть вообще).

Имелось в виду a[x+y*strideY+z*strideZ] и a[(z*sizeY+y)*sizeX+x]. (strideX=1, strideY=sizeX, strideZ = sizeX*sizeY)
Мне показалось нужным показать только дерево выражения, поэтому stride и size я назвал одинаково (s). И немного напутал.


Я утверждаю другое, что нет смысла биться за непрерывный кусок, если матрица очень большая.

Как раз таки это имеет смысл (но ровно до тех пор, пока на bad_alloc не нарвётесь).

Проблема в Си в том, что A[N][M] это может быть как массив указателей на одномерные массивы, так и двухмерный массив. А причина этого в глупом (именно так) решении о том, что имя массива эквивалентно указателю на первый элемент массива. Написать символ взятия адреса для получения этого самого указателя не так уж и сложно. А теперь в результате этой «синтаксической оптимизации» мы получили то что массивы не являются «объектами первого класса». Их нельзя передавать в функции именно как массивы; нельзя возвращать из функций именно как массивы.

В c++ можно через шаблоны это делать спокойно

Причина в том, что для того, чтобы всего лишь из имени массива можно было волшебным образом получать информацию о его размерности (чтобы иметь возможность использовать/передавать/возвращать массив как объект первого класса) необходима ещё служебная структура данных хранящая информацию об этом. Си — это достаточно низкоуровневый язык и типы данных, которые несут с собой такие такие скрытые структуры в нем отсутствуют как класс by design. К «проблеме» «глупой синтаксической оптимизации» это не имеет совершенно никакого отношения.

"… имя массива эквивалентно указателю на первый элемент массива..." — поле такой белиберды дальше можно не читать. Написана ерунда.


Автоматическое приведение объекта типа "массив" к значению типа "указатель" делается в С не безусловно, а лишь в наборе четко оговоренных контекстов. Например, в контекстах операторов & и sizeof такого приведения не делается.


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

Да, на самом деле это так — «ни у кого не хватает смелости расширить список контекстов» и, действительно, «нет никаких физических преград включить копирующие контексты в список исключений». Как хорошо бы иметь возможность просто написать: a = b или a = f(), где a и b — массивы одинаковой статической размерности (не нужно заморачиваться с memcpy() — разве не круто), а функция f() — просто супер — возвращает массив, копируя его поэлементно наружу.

Ни у кого не хватает смелости сделать массивы копируемыми в языке Си, да и соображения обратной совместимости, понятное дело, мешают сильно. :(
double a[n*n]
a[i*n+j]
устроит?
Нет, конечно. Потому что хоть сколько-нибудь сложная формула превращается в ад.
Можно привести пример сложной формулы? Чтобы было видно, в каком месте возникает сложность.
Например, a[complicated_formula(asdf,zxcv)*n+more_complicated_formula(more,arguments)].
Имелось ввиду, что есть ли разница между a[i][j] и a[i*n+j], даст ли вторая форма заметную дополнительную сложность.

и зачем так писать? не проще выделить переменную под индекс?
или, снова, преждевременная оптимизация?

И как это упростит и починит формулу?

Никак, но вопрос же про индексы и удобство.
Вы замеряли или чисто теоретизируете?

А теперь смотрите:
1. Для повышения производительности часто используется выравнивание строк. Получаем ещё один параметр — Stride.
2. Вы не можете вставить код для проверки выхода индексов за границы.

Единственное разумное решение — обернуть всё это дело в класс.
> 1. Для повышения производительности часто используется выравнивание строк.
Или не используется, если некоторые алгоритмы можно преобразовать так, чтобы вместо перебора двух индексов перебирать один, проходя по всей матрице последовательно (фактически построчно).

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

Это если вам ужасно повезло, и выполняемая операция является поэлементной.

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

И назвать его cv::Mat…
Ну зачем же сразу так? Лично мне база OpenCV очень не нравится: зачем указывать тип пикселя при каждом обращении к нему, когда можно один раз указать тип изображения?
статью не читай@коммент пиши?

Я уже писал. В C89 и C++ это не сработает, т. к. n не известен на этапе компиляции. В C99 скомпилируется, там VLA есть, но упадёт в случае сколько-нибудь большой матрицы, т. к. размещается на стеке

Мм… То есть вот серьёзно? Целая статья, чтобы заявить «С — слоупок потому, что там нет стандартных 2Д-массивов»? Без особых идей решения проблемы, без нормального анализа ситуации, без тайм-тестов?

Заявка — двухмерные массивы нужно создавать вот так: «double *a = new double[n * n]»? А почему? Где объяснение? «Память выделяется не последовательно»?.. И что? Чем это плохо? Где объяснение? Загуглить про кеш-промахи? Если загуглить — зачем нужна данная статья?

Информация о restrict? Что это такое? «Адреса могут пересекаться»? Хм… Как-то непонятно. Можно подробнее?.. А подробнее нету. Да, можно поискать и почитать: раз, два. Но если нужно что-то искать — снова-таки, зачем данная статья?

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

З.Ы.: Звиняюсь если кого обидел. Таковой цели не было. Просто хочется читать интересный материал.
Нету — и не надо.

В С/С++ по большому счёту нет массивов вообще, есть
С — участок памяти индексируемый от его начала с шагом sizeof(T)
С++ — С или идиома «последовательные элементы списка» если мы вспоминаем про перегрузки

Это не массив в понимании «тип данных», точно так же нет и строк.

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

Пользователь хочет простую идиому вида " C = A * B; C[3,4] = -1; " — он её может получить. Не нужно реализовывать умножения матриц самостоятельно, не нужно реализовывать доступ к членам матрицы самостоятельно, всё работает и работает быстро.
С++ справляется с этой задачей. Да, через библиотеку, но он на то и универсальный язык, что на нём можно решить любую задачу написав подходящую абстракцию, а не жаться в рамки предоставляемые языком.

Пользователь хочет простую идиому вида " C = A * B; C[3,4] = -1; " — он её может получить.

С++ разве поддерживает мультииндексы?

Да, вы правы, сделать в C++ что то вроде A[x, y] не получится, но можно перегрузить оператор (), или реализовать метод at(x, y), и вобщем-то получить то-же самое.
Получится, но это будет равнозначно А[y]. Можно замарочится и c [x][y].
В C++ можно перегрузить оператор ()
int& operator ()(int x, int y);
int operator ()(int x, int y) const;

Ну, это уже совсем за гранью добра и зла :-) Лучше уж тогда явно вызвать метод.

Не, за гранью добра и зла — это возвращать что-то типа ElementWrapper с перегруженным оператором присваивания.
Вы правы — это Ада.
Там массивы индексируются круглыми скобочками.
Хотя и в фортране всё не так однозначно…



do k=1,10
do j=1,20
do i=1,100
arr(i,j,k)=25! правильно
brr(k,j,i)=0! работоспособно, но медленнее в несколько раз

end do; end do; end do


(цитата из Вики)

В фортране сперва в памяти лежит arr(1,1,1), потом arr(2, 1, 1), arr(3, 1, 1), ..., arr(1, 2, 1) и так далее.
Поэтому arr(i,j,k) будет быстрее всего, там во внутреннем цикле происходит обращение к данным, лежащим в памяти подряд (строчки кеша и так далее). Ну необходимость учитывать кеш относится вообще ко всем языкам программирования

буст поддерживает

Эффективные многомерные массивы есть через библиотеки.
Например в BOOST: http://www.boost.org/doc/libs/1_62_0/libs/multi_array/doc/reference.html

Кстати, CBLAS как раз матрицы хранит в виде непрерывного массива.

Так вот, а в C передать массив в функцию нельзя. Точнее, можно, не нет специального синтаксиса, чтобы показать компилятору, что в функцию передаётся именно массив.

Раз уже пишите про C/C++, то дополню, что в С++ это можно.


#include <iostream>

typedef int array[13];

void f1(array &a)
{
    std::cout << "sizof(a) = " << sizeof(a) << std::endl;
}

template <typename T>
void f2(T &a)
{
    std::cout << "sizof(a) = " << sizeof(a) << std::endl;
}

int main()
{
    int a[13];
    f1(a);
    f2(a);
    return 0;
}

Вывод

sizof(a) = 52
sizof(a) = 52

Начну с многомерных массивов. Допустим, вам нужно максимально эффективно работать с большими квадратными матрицами в C++

Только в контексте фортрана говорить об «максимально эффективно» не имеет смысла.

А как сделать так, чтобы синтаксис был нормальным (a[i][j]), но чтоб было эффективно?

А с чего это нормальный синтаксис? Кто определил тот «факт», что синтаксис через n не нормальный?

При этом читаем ниже:
И обращение к i-му j-му элементу происходит так: A(I, J)

Надо уже определиться с тем — какой собственно синтаксис является нормальным.

А по поводу «не может» — у меня почему-то может:

#include <stdio.h>
#include <stdint.h>

void f(uint64_t n, char p[n][n]) {
  fprintf(stderr, "%lu\n", &p[10][0] - (char *)p);
}

int main(void) {
  f(1024, (void *)(char[123456]){0});
}


Однако Fortran 90 не такой уж и новый. 26 лет прошло. 26 лет эта фича есть в Fortran'е. А в C++ её нет до сих пор.

А кому она нужна? Никому — кому нужно красиво — пишет на крестах и делает себе какой угодно синтаксис.

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

А теперь «фичу» чтобы получить обратный случай? Что? Её нету? Да вы что. Оказывается это никакая не фича, а просто иное поведение по умолчанию, а си может и так и так? Ну ничего — бывает.

Но Fortran имеет огромную историю использования для высокопроизводительных математических вычислений. Не исключено, что в Fortran есть ещё несколько фич, которые ещё ждут своего часа: включения в C. И которые, возможно, всё ещё делают Fortran быстрее C.

Такой истории нет. Производительность есть у компилятора( в случае фортрана). Уже наверное лет 20 как фортран никого не интересует и интересовать не может.

Точнее, можно, не нет специального синтаксиса, чтобы показать компилятору, что в функцию передаётся именно массив.

В тех компиляторах, в которых фотран быстрее жаваскрипта никакие рестрикты компилятору не упали. Это хинты для экспортируемых функций. Если «высокопроизводительных вычислений» собирать как в 95-м в разных единицах трансляции, то тут конечно всякое бывает. Но таким профессионалам дали lto.

А вот как там устроены правила алиасинга в Fortran'е, я не знаю, просто где-то в интернете я как-то прочитал, что появление restrict позволило C наконец приблизиться Fortran.

Как там в параллельной вселенной? Сишка на связи.
А с чего это нормальный синтаксис?

Он удобнее. Сложнее ошибиться. Почитайте другие комменты, там плюются на a[i * n + j]

o_O, вы меня убедили, в C действительно можно. Переименовал статью и добавил UPD. :)

Вроде бы как C++ включает в себе C89, а не c99.

char (*p)[12];
p = new char[8][12]();

https://ideone.com/99ixtJ

Разные стандарты C++ ссылаются на разные стандарты C. Однако VLA всё равно ни один из стандартов C++ не поддерживает.


p = new char[8][12]();

Мне было очень сложно понять, что это значит. Пишите лучше просто p = new char[8][12]


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

Стандарт C++11 включает в себя C99 by reference. Т.е. как бы целиком.

Нет. C++11 действительно ссылается на C99 (т. е. C++11 references C99), но C++11 не содержит всех фич C99. C++11 не поддерживает VLA из C99. И вообще, C++11 и C99 имеют огромный список несовместимостей (да и вообще, C++ и C всех версий очень плохо между собой совместимы). Начиная с того, что в C символьные константы (например, 'a') имеют тип int, а в C++ — char. Недавно читал огромную статью со списоком несовместимостей, если надо — могу дать ссылку

Всё таки не просто ссылается как на левый документ. Многое оттуда берётся и в самом C++ уже не описывается.
C++ is a general purpose programming language based on the C programming language as specified in
ISO/IEC 9899:1999 (hereinafter referred to as the C standard). In addition to
the facilities provided by C, C++ provides additional data types, classes, templates, exceptions, namespaces,
operator overloading, function name overloading, references, free store management operators, and additional
library facilities.

Да и Майкрософт, например, всегда говорит о поддержке C99, как это требуется (они заявляют, что реализовали C99 на 99.9% кроме tgmath.h, так как это к чистому C относится, для C++ есть ctgmath, который у них реализован).
Да и вообще, тот же restrict по этим причинам в C++11 не описан, но ровно один раз упомянут:
17.2 The C standard library [library.c]
1 The C++ standard library also makes available the facilities of the C standard library, suitably adjusted to
ensure static type safety.
2 The descriptions of many library functions rely on the C standard library for the signatures and semantics
of those functions. In all such cases, any use of the restrict qualifier shall be omitted.

Нет, ссылается. Это подтверждается большим списоком несовместимостей и фич C, отсутствующих в C++ (VLA, _Bool [в C++ такого ключевого слова нет] и т. д.). Нашёл всё-таки ту ссылку со списоком несовместимостей: http://david.tribble.com/text/cdiffs.htm .


C++ is a general purpose programming language based on the C programming language as specified in ISO/IEC 9899:1999

Эта фраза лишь сообщает всем известную мысль о том, что C++ основан на C, т. е. что главная модель, которую держали перед глазами авторы C++ — это C.


In addition to the facilities provided by C, C++ provides additional data types, classes...

Опять-таки, эта фраза лишь неформально нам сообщает о том, что кроме основных фич, которые есть в C, в C++ также есть классы и т. д.


Стандарт C++ self-contained, т. е. все фичи C++ должны быть упомянуты в стандарте C++. Они не появляются в C++ автоматически на том основании, что они есть в C.


Вот когда стандарт C++ говорит о сишных хедерах, он говорит, "<string.h> переносится в со следующими исключениями". Вот тут уже C++ явно говорит, что такая-то фича переносится, значит, так и есть.

Да, возможно, процитированный абзац неудачно сформулирован. Но вряд ли они его исправят, если, допустим, я зарепорчу им баг. Т. к. так принято, что стандарты и не предназначены для того, чтобы быть понятными вообще всем без подготовки, особенно в базовых вещах. Я помню, как-то зарепортил баг в POSIX по поводу непонятных, на мой взгляд, формулировок о функциях printf, scanf и т. д. Мне ответили (могу достать ссылку, здесь цитирую по памяти), "you should invest enough time in learning POSIX". Т. е. ты уже должен знать базовое.


Стандарт C++ не пишет, что в C++ есть restrict, а значит, его там и нет. А упомянут он там просто, чтобы дать понять, что декларации функций из стандарта C не нужно воспринимать как есть, нужно их воспринимать без слова "restrict". Потому что restrict в C++ как раз нет.


P. S. Решил проверить, если ли restrict в C++. И что бы вы думали? Его там действительно нет. Ни в C++11, ни даже в текущем черновике C++17. То есть C++ медленнее Fortran. Сейчас. Facepalm

Мне стало интересно, есть ли хоть какой-нибудь способ обойти отсутствие в C++ restrict. Если отсутствие многомерных массивов можно обойти с помощью умных классов, то что тут?!

__restrict?
__declspec(restrict)?
Я про стандартное решение

"Слышал звон да не знал где он". Ничего подобного в С++11 нет и никогда не было. С и С++ настолько фундаментально различные языки, что никакого "включения" между ними нет и быть не может.


By reference в С++ включается только интерфейсная спецификация стандартной билиотеки, да и то с массой оговорок.

Насчёт «интересовать не может» — наука (по меньшей мере, в России) очень много пишет на Фортране. То есть, вот тот редкий случай, когда язык используется именно и только для того, для чего создавался.
Кроме того, переход с Фортрана во многих случаях — это планы на полста лет вперёд. Чисто как пример: у атомщиков переписанные коды потребуют пересертификации, а это дикое бабло и отвественность.
А можете привести пример? В моём научном окружении используют MalLab, Python, C#, C++, но никак не Fortran. Потому что Fortran — это шаг на 30 лет назад.
Теория поля, HEP, MD, не буду обобщать, но знаю, что люди пишут.

Процитирую Википедию:


Фортран широко используется в первую очередь для научных и инженерных вычислений. Одно из преимуществ современного Фортрана — большое количество написанных на нём программ и библиотек подпрограмм.

Имеется большое количество написанных на Фортране (в большей части на старых версиях языка) различных математических библиотек для матричной алгебры и решения систем линейных уравнений, библиотеки для решения дифференциальных уравнений. <...> Ряд таких пакетов создавался на протяжении десятилетий и популярен в научной среде по сей день, например — IMSL.

Большинство таких библиотек является фактически достоянием человечества: они доступны в исходных кодах, хорошо документированы, отлажены и весьма эффективны.

В общем есть куча кода, переписывать его никто не собирается. Можно автоматически сконвертировать в C или C++. Но мы в результате получим машиночитаемый, а не человекочитаемый код. Плюс, возможно, полученный код будет работать медленнее, т. к. никто не гарантирует, что Fortran -> asm не быстрее Fortran -> C -> asm

Контекст статьи «использования для высокопроизводительных математических вычислений». И именно в этом контексте фортран никого интересовать не может.

А то, что кто-то пишет на фортране по каким-то другим причинам — это не важно т.к. эти причины не есть «мы пишем на фортране, ибо нам надо максимально быстро».
А зачем тогда Intel выпускает и поддерживает свой Intel Fortran Compiler?
> Контекст статьи «использования для высокопроизводительных математических вычислений». И именно в этом контексте фортран никого интересовать не может.
Вот именно в этой области фортран дико популярен и конкурирует с C&C++ нередко выигрывая у них.

А приведенные выше MatLab, Python, та же Mathematica — это чаще всего лишь инструменты прототипирования или текущих расчетов над небольшими моделями.

Справедливости ради MatLab поддерживает параллельные вычисления на суперкомпьютерах, но на практике я не видел чтобы это активно применялось, хотя раз поддерживает, то наверняка не просто так…
Вот именно в этой области фортран дико популярен и конкурирует с C&C++ нередко выигрывая у них.

Где? В параллельной вселенной? В реальном мире в этих областях он на помойке. А то, что всякие пхп-эксперты мне ретранслирует поверья из-за своей запарты — из этого ничего не следует.

В реальном мире даже дефолтные диалекты сишки на помойке. Для этого достаточно взглянуть на любые блобы интела( выше эксперт рассказывает куллстори про штеудский компилятор фотрана), да и любые конкурентоспособные реализации. Фортран там невидно, но он 100% там есть. Единственное что есть на фортране — это протухшее дерьмо из 70х, которое не сливает в хлам только жаваскрипту. И то если повезёт.

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

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

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

А приведенные выше MatLab, Python, та же Mathematica — это чаще всего лишь инструменты прототипирования или текущих расчетов над небольшими моделями.

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

Справедливости ради MatLab поддерживает параллельные вычисления на суперкомпьютерах

Это на уровне заспавнить на mpi тонну дерьма? Не велика заслуга. Слово «супер» не делает код под этот «супер» супер. Никто до сих в параллельность на уровне суперскалярности и симдов не может, кроме как в подвалах интела в обнимку с ассемблером, либо каким-нибудь дсл.

Чтоа? Вот только не надо хаскелль и лисп складывать в одну кучу к коболам, на которых нормальные люди не пишут. Хаскелль и лисп — отличные языки со своей нишей, как и многие другие. Да хотя б даже зайдите на оф. сайт хаскелля https://www.haskell.org/ и посмотрите, сколько там пакетов в менеджере пакетов (да, кстати, у хаскелля есть менеджер пакетов, в отличие от c++, я помню, кто-то в комментах тут жаловался об отсутствии менеджера пакетов у c++)

Вот только не надо хаскелль и лисп складывать в одну кучу к коболам

Я складываю в одну кучу не с т.з. языка, синтаксиса, его идей и прочего, а именно с ТЗ комьюнити — тех, кто их используют и возможностей самого языка.

Да и в целом я всё правильно сказал. Вся это это кобоальгольная семья. Которая существовала до того как появился си. А после си захватил как мир, так и все эти язычки.

Хаскелль и лисп — отличные языки со своей нишей, как и многие другие.

У них нет никакой ниши, кроме школьников и хипстеров. Их творения абсолютно бесполезные и неконкурентоспособны. Они несостоятельны в любой области.

Да и само понятие «ниши» для языка — это уже определения его как убогого. У языка не может быть «ниши» — у него может быть только порог вхождения.

Да хотя б даже зайдите на оф. сайт хаскелля https://www.haskell.org/ и посмотрите, сколько там пакетов в менеджере пакетов

И всё это мусор никому не интересный и никому не нужный, ну кроме адептов, хипстеров и прочих школьников.

Единственная причина почему хацкель не на помойке как остальные ФП-языки — это его мультипарадигмальность. Хотя он называется «функциональный» — он полностью дискредитировал ФП, как и коммонлисп. Ничего без циклов и классиков на этом позере не написать.

Всякие школьники с их кложурами и прочим маразмом мало кого за пределами говносайтиков интересуют.

да, кстати, у хаскелля есть менеджер пакетов, в отличие от c++, я помню, кто-то в комментах тут жаловался об отсутствии менеджера пакетов у c++

Зачем сравнивать жопу с пальцем? Хацкель псевдояп для школьников. Одно дело собирать хелворды школьников из шаблончиков на недоязычке, а другое дело интегрировать в себя всю широту того же С++ мира и системщины с тоннами легаси. Что там будет делать хацкель-адепт для получения своего гхц, пакетного манагера и прочего бинарного рантайма?

Да и что за враньё. У меня есть «менеджер пакетов» портеж называется. Ведь с C++ я получаю системный мир, а так же си, а с си — весь остальной(основной) системный мир.

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

Ваше мнение было очень ценным для нас. Только, кажется, вы забыли его аргументировать.

Да и само понятие «ниши» для языка — это уже определения его как убогого

Чтоа? То есть по-вашему существует лишь одна ниша и все в этой нише друг другу конкуренты, и, как следствие, среди языков существует один-единственный лучший? Нет. Есть несколько разных ниш. Есть ниша максимально близких к железу языков. В ней есть C, C++, Fortran и, возможно, Rust. Далее идут всё более медленные языки. Ещё есть специализированные ниши вплоть до одинэсов. Вы щас уподобляетесь тому чуваку с хабра, который написал большую серию статей про то, что якобы nim — самый лучший язык (могу найти, если надо)

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

Вас сказать нечего. Если я определил то, что само понятие «ниши» для языка множит его на ноль и определяет говно. Каким образом и с чего вы взяли, что вы можете мне нести херню про какие-то «ниши»? Ниши для школьников. Всё просто.

У вменяемого языка нет ниши — у него есть только порог вхождения. Выше я это написал.

Есть несколько разных ниш.

Не верно. Очередная подмена понятий от школьника. Чем определяются эти ниши? Правильно — эти ниши есть разный уровень требований к качеству конечного результата.

Есть броузер — к нему есть высокие требования к качеству кода и качеству результата. Есть веб-школьник. У веб-школьника нет никаких критерием качества — они не нужны.

Качество кода, как и любого другого продукта определяет его цену. Всё просто. Это и есть ваши «ниши», но ниши-то эти для продукта, а не для языка.

Есть ниша максимально близких к железу языков. В ней есть C, C++, Fortran и, возможно, Rust.

Какое ещё железо. Уровень понимания в районе помойки. Есть управляемые языки, а есть не управляемый. К железу ничего из этого отношения не имеет. Язык — это не про железо.

Фортран вообще не является языком — это кусок дерьма. Он ничем не отличается от жаваскрипта.

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

С++ — это надстройка пары костылей над си. В целом язык остался тем же.

Но опять же, как я уже говорил — сам язык никому в мире «высокопроизводительных вычислений» не упал — им можно только подтереться, если он управляемый.

Далее идут всё более медленные языки.

А почему они более медленные? Правильно — они менее управляемые. А почему они менее управляемый? Правильно — для управления нужна квалификация, а откуда она у школьников?

И вот мы и вышли на то, о чём я говорил — ниши существуют только в параллельном мирке адептов. Кто же сознается с тем, что он малоразвитая обезьяна, которая не осилила нормальный язык( вернее даже дело не в языке — дело в управляемости. Отвечать за всё сложно — вот школьник пытается сбежать от ответственности)?

Опять же — пример попроще. Литейное производство. Является ли кустарное литьё из какого-нибудь силумина «нишей»? Есть ли какая-нибудь ниша у силумина? Нет — её нет. Конечно, адепты будут орать, что ниша есть и прочее. Но на самом деле это применяется только потому, что у адептов нет ни бабок, ни квалификации на нормальный техпроцесс. И существует эта ниша только поэтому.

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

Естественно есть «ниши», где дерьмо делать нельзя. Там и есть ниша С/С++. И без разницы какая это «ниша» в понятии школьников. Вебчик, либо не вебчик. Это ничего не определяет.

Ещё есть специализированные ниши вплоть до одинэсов.

Ну да. С чего вдруг какая-то секретарша/бухгалтерша сможет осилить вменяемый язык? Да и и это скриптуха убогая.

Вы щас уподобляетесь тому чуваку с хабра, который написал большую серию статей про то, что якобы nim — самый лучший язык (могу найти, если надо)


Ну смотрите. Вы написали, что у хацкеля есть ниша — я вам её определил. Школьники убогие. Вы можете назвать какую-то другую? Нет.

А высеры убогого ламерка про nim, io, haskell, rust и прочий мусор — ничего не стоят. Когда он высер на своём дерьме что-то кроме лабы, либо что-то конкурентоспособное с тем, что есть на вменяемом языке — тогда его высер будет иметь смысл.

Предлагаю вам написать сайт на с++, запрос в бд на хаскелле и программу на микроконтроллер на js
п.с.
уважаемый, вы не так делаете. Правильный троллинг — это когда вы пишете меньше, чем «жертва». Плюс, слишком явно
https://github.com/cesanta/v7 — интерпретатор js для микроконтроллеров, кстати.

И еще такая штука есть: http://www.espruino.com/
Предлагаю вам написать сайт на с++

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

Да и собственно весь вебстек на С/С++ и написан.

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

запрос в бд на хаскелле

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

программу на микроконтроллер на js

Это такой же не язык.

В целом ваша потуга убога и глупа. Вам говорили не о том, что языков нет «ниш» в смысле, что они все «универсальны», а о том, что у языков у которых есть «ниша» — это уже не язык, а дерьмо.

И примеры я приводил, но вы слишком тупы, чтобы их понять и не нести мне херню. Не можете думать на уровня языков и прочих абстракций — думайте на бытовом языке. Это просто — надо только чуть подумать.

Специально для вас повторю ещё раз. «ниши» есть не у языков — ниши есть у продуктов, вернее ниши как уровни качестве — высота критериев оценки конечного продукта. В конечном итоге это определяется только дешевизной — это вообще свойство этого мира. Если можно впарить дерьмо — зачем делать не дерьмо? Если можно нанять обезьяну — зачем нанимать не обезьяну? Проблем больше. Затрат больше.

Вот так и получается, что в этом мире качество появляется только в ответственных проектах. Это и есть «ниша» вменяемых технологий и материалов. Корпус мобилки можно сделать из дерьма, а можно из стекла и алюминия. И у дерьма нет ниши — он ни по каким критериям не является конкурентом. Он просто дешевле и технология его применения проще и дешевле.

Точно так же и с языками.
Кажется, Вам немедленно нужно обратиться в компанию ВСМПО-АВИСМА для закупки титановых защитных щитков во избежание катастрофы.

А аргументация, базирующаяся на никнейме собеседника — это пожалуй даже оригинальная вещь. +1 Вам за изобретательность.

Резюмируя вышесказанное я вынужден процитировать господина mayorovp:
> Ваше мнение было очень ценным для нас. Только, кажется, вы забыли его аргументировать.

P.S. В тред призывается к.ф.-м.н. kbtsiberkin. Может быть он, как практикующий теорфизик, скажет по существу что-нибудь еще.
Я не понимаю зачем я спорю с вами, если это бесполезно. Вы не понимаете ни предмета, ни обладаете желанием чего-то добиться, кроме как выпячивать своё бессмысленное мнение, которое является убогое ретрансляцией мифов и легенд.

А аргументация, базирующаяся на никнейме собеседника — это пожалуй даже оригинальная вещь. +1 Вам за изобретательность.

Канонический пример балабола. Ему написали целую портянку — он ничего не осилил ответить — слился и высрал какую-то херню. Ну спрошу я вас — «какая именно моя аргументация основывалась на вашем никнейме?» и вы обделаетесь. Зачем это пишите? На что рассчитываете.

Резюмируя вышесказанное я вынужден процитировать господина mayorovp:

Ну это не имеет смысла. Хотя в вашей тусовке балаболов можно обвинять кого-то в отсутствии аргументации с отсутствующей аргументацией.

Я уже аргументировал своё мнение. Аргументация была с 2-х сторона. Со стороны «если он лучше, то почему его нет нигде?», а так же со стороны — почему собственно его нигде и нет. Если бы вы что-то могли — вы бы развивали тему, а раз нет — вы ничего не понимаете. А раз так — распинаться среди вас я не буду. Аргумент «первой стороны» никуда не пропал. Покажите мне фортран.

P.S. В тред призывается к.ф.-м.н. kbtsiberkin. Может быть он, как практикующий теорфизик, скажет по существу что-нибудь еще.

Ваш адепт ниже обосрался. Бывает.

Аргументация уровня «кукареку все думают, что фортран в дерьме, но это не так — можете мне поверить», а так же высеры уровня «выходят новые „версии“ фортрана». Просто смешно. Выходят новые версии паскаля, делфятинки, бейсика и прочего дерьма. Никого это не волнует.
Fortran — один из немногих языков, который действительно работает на скорости C. Собственно, сегодня есть лишь 3 языка, которые кроссплатформены и при этом обеспечивают максимальную производительность. Это C, Fortran и C++. Причём C++ лишь в случае, если не используются STL, мощный ООП и так далее.

«мы пишем на фортране, ибо нам надо максимально быстро» — да, вряд ли кто-то станет так делать. Новые проекты в этой сфере, видимо, начинают на C и C++.

Но если есть уже написанный на Fortran код, то никакого смысла переписывать его на C нет, т. к. производительность от этого не изменится.
Причём C++ лишь в случае, если не используются STL, мощный ООП и так далее.

STL и мощный ООП никак не мешают производительности при правильном использовании.

Я сейчас говорю про по-настоящему адский быстрый код. Необходимость написания которого в реальных задачах не возникает. Но если вам действительно нужен быстрый код, то каждый раз, когда вы будете писать слово "class" или "vector", вам нужно будет думать, "а как в результате будет выглядеть результирующий ассемблерный код?" В результате от использования STL и ООП придётся отказаться, т. к. они попросту проигрывают хорошему вручную написанному коду.


Вот скажем, поищите в интернете compiler benchmark game. Сможет ли STL/ООП решение быть на одном уровне с решениями, написанными вручную на C? Вряд ли

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


Вряд ли

Не знаете — не пишите.

Вот здесь человек рассказывает, как простейший код с вектором и 4-мя push_back'ами компилится в огромную простыню ассемблерного кода: https://www.youtube.com/watch?v=s4wnuiCwTGU

Вывод простой: если вам нужно работать с вектором, гарантировано имеющем длину не больше N (N — небольшое, до 8-16), и именно работа с вектором отжирает до 90% времени выполнения кода, то имеет смысл подумать об использовании собственной реализации вектора под конкретные условия.

Всё остальное — преждевременные оптимизации, усложяющие написание кода.

Именно поэтому я написал про "правильное использование". Использование вектора тут не нужно, достаточно std::array:


int f2() {
  std::array<int, 4> v = {1, 2, 3, 4};

  int sum = 0;
  for ( auto x : v) {
    sum += x;
  }
  return sum;
}

компилируется в 2 инструкции:


f2():                                 # @f2()
        movl    $10, %eax
        retq

(godbolt)

Мысль правильная, но пример, скажем прямо, не показательный и к производительности std::array прямого отношения не имеющий вообще. В реальном коде подобные optimization opportunities, понятное дело, не встречаются. Если, конечно, вы их нарочно не создаете.

Т.к. разные инструкции процессора восполняются с разной скоростью, то простыня кода мало о чем говорит. Если перепишем std::vector на Си как АТД, пометим все как inline, то получим простыню когда. Естественно если будем компилировать код компилятором Си, то простыня получится поменьше из-за отсутствия кода, для работы с исключениями.

Очень многое зависит от самого компилятора, точнее, от его способности оптимизировать код.
Тесты проводились только для g++, но g++ — не единственный в мире компилятор, да и не самый быстрый, помимо него ещё существуют msvc, llvm, icc и т.д.

До того, как я познакомился с ICC, я пытался переписывать узкие места кода (обработка изображений) на ассмеблере и получал двукратное ускорение по сравнению с msvc. Но когда я воспользовался ICC, оказалось, что он генерирует код такой же по скорости, а иногда и быстрее, чем вручную написанный на ассемлере.
Вот смотрите как интересно получается. Как вы ко мне — так и к вам. Показательно.

Помогу вам.

Как я уже выше писал — производительность — это контроль.
class

Дак вот — классы(обычные) абсолютно предсказуемые и никак никому не мешают. Поэтому выкатывать это как аргумент не имеет смысла.

Проблема в классами именно в связки их с ООП, а оно предполагает aos, а им можно только подтереться.

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

vector

Опять же в контексте «ассемблерного кода» нету смысла говорить о векторах и прочих крестовых изваяний по причине того, что в конечном итоге крестобалабол сольётся на то, что для сравнения с вектором вам нужно реализовать такой же вектор, а иначе сравнение некорректно. Но это не имеет смысла, ибо вектор ничего не отличается от сишной лапши — он она и есть завёрнутая в Оинтерфейсик на шаблончиках.

И для того, чтобы смешать с дерьмом вектор, как и убогий фортран — надо перейти в реальный мир. Собственно в реальном мире вся производительность и существует.

И тут начинается разрыв шаблона у крестового балабола. Оказывается в реальном мире динамическая память есть на уровне памяти и вектор нахрен не нужен уже 10лет. Далее оказывается, что вектор( как и кресты) не умеют в реаллок. И тут опять разрыв шаблона — реаллок в любую часть памяти не требует копирования( как учили крестовых адептов).

А дальше идёт разрыв шаблона от ленивого связывания памяти. И оказывается reserve() в векторе нихрена не даёт памяти( как и маллок). И мы получаем память по мере обхода обработкой пейджфолта. Далее мы понимаем, что дефолтными сишными и крестовыми аллокатарами можно только подтрееться. И нам нужен префолт.

А потом когда мы узнём что такое память, что такое тлб и почему это плохо при RA, то мы желаем его «выпилить» нахрен. Опять же крестами можно только подтереться.

Ну и даже такой банальности как «выравнивание» нету. Приходится прикостыливать левый аллокатор. Были когда-то потуги с валараями, но нахрен они крестовикам?

т. к. они попросту проигрывают хорошему вручную написанному коду.

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

Сможет ли STL/ООП решение быть на одном уровне с решениями, написанными вручную на C?

Что-то я там не вижу фортрана. Фанатизм — такой фанатизм.

Что самое интересное там фортран-днище написано ручным c2f, а компилятор его собирается встроенным f2c. Полезность зашкаливает.
НЛО прилетело и опубликовало эту надпись здесь
Дело не в том, кто я — это никого не волнует, точно так и «кто вы».

Опять же — вы пытаетесь врать и себе и остальным. Пытаетесь выставить меня в свете будто-бы я дартаньян и шизофреник. Зачем?

Вы явно врёте про то, что я называю кого-то школьником. Дело не в этом. Школьник и быдлокодер — это не плохо и из этого ничего не следует. Ни у всех есть время, желание и способности. Да и не выгодно это особо.

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

Никто не обвиняет человека в том, что они сидел и писал хервордики на фортране — для него язык — это калькулятор. Зачем мы обращаемся к его авторитету, зачем он что-то утверждает?

Зачем тому, кто не знает ни языка, ни матчасти рассуждать о том почему, где и как кто кого обгонял. Это же глупо?

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

Почему же мы не называем это нишей? Дело не в их квалификации — это просто ниша. Ведь так?

Почему вы такие непоследовательные? Говно вы называете говном. Чем говнокод отличается от вашего кода? Он ведь то же работает. Чем нормальная машина отличается от дерьма? Ведь она то же едет?

И посмотрите на рекламу ширпотреба, на защитников ширпотреба — что они говорят? Правильно — он как раз-таки говоря о «нише». Не говно дороже, сложнее и прочее. И подавляющее большинство тех, кто орёт мне про «нишу» — считают их идиотами. А в чём отличии? Правильно — его нет.

Самообман штука такая. Свой «труд» сложно назвать говном. Себя вообще сложно оценить объективно. И те, кто драят сортиры думают точно так же как и вы. Это просто ниша. И отношение у них к вам такое же, как и у вас ко мне.

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

Но ведь никаких отличий нет? Каждый видит ниже себя. Что там вася делает не так. Но ведь я не вася. Я 100% думаю не так как этот вася. Это в мечтах адепта. А реально он ничего от васи не отличается. Поводки всё те же, только несколько в другом виде.

НЛО прилетело и опубликовало эту надпись здесь
Разумеется, вы сейчас снова будете психовать

Если вы думаете, что я психую — это неверно. Меня это не колышет.

и оскорблять меня и всех окружающих, но чем именно вы лучше всех остальных?

Я никого не оскорблял. Это ваше восприятие. Если вы не позволяете себе говорить и утверждать на те темы в которых не разбираетесь — вы не имеете отношения к тем о ком говорил я. А если это так, то проблема явно не во мне.

Да и я нигде не говорил что я лучше — это очередная невменяемость вашего восприятия. Мне не важно кто вы. Я не оцениваю вас. Я оцениваю то, что вы пишите. Оцениваю ваши слова и суждения. Я оцениваю их не противопоставляя себе, а отдельно. Зачем вы приплетаете меня? Лишь потому, что это известный вам шаблон ответа? Вы не уникальны.

Тем, что знаете C++?

Язык тут не причём. Вы же пастили мои пасты из другой темы — там я говорил, что С++ говно. А в другой говорил что си говно. Я не являюсь адептом какого-то языка и прочее. Меня это мало волнует.

Так всегда. Когда я не соответствую каким-то общепринятым верованиям — меня начинают обвинять в чём-то. Говорю плохо про С++ — не осилил. Про сишку — не осилил. Это восприятия мира сектантами — всё что не по их — этого не может существовать. Это говорит еретик, урод и прочее. Сжечь его — забанить, заминусовать, заспамить.

Так было везде. В прошлый раз я пытался писать вменяемо, но меня опять довели. Я кидал уже пруфец — https://habrahabr.ru/users/firsttimecxx/comments.

Я люблю эльбрус — я пытаюсь объяснять людям в чём проблема и как сделать лучше — ты хейтер. Я люблю си и даже С++ и пытаюсь объяснить людям в чём проблема С++, чтобы сделать его лучше — я хейтер.

Всё что я прошу — понимание. Надо понимать, а не верить в какую-то херню которую кто-то сказал. Адепты го верят, что их не развели с их горутинами. Они верят, что ОС не может в 100потоков. Им показываешь обратное — бесполезно.

В целом меня это достало + в теме про С++ мне попались совсем невменяемые люди, которые просто врали, врали и врали. А т.к. у меня ни времени, ни кармы, чтобы им отвечать — я уже перестал воспринимать их как людей.

Вы не хуже других. Я не лучше вас. У всех разные способности, желания, обстоятельства и текущие условия. Я не против вас. Я не понимаю просто — зачем рассуждать, утверждать, называть других дебилами и не воспринимать альтернативной точки зрения, если вы даже в том о чём рассуждаете не разбираетесь?

НЛО прилетело и опубликовало эту надпись здесь
Тогда почему вы называете других дебилами

Не было такого. А почему я отвечаю жестко? Я не вижу желания от людей вести какую-то осмысленную дискуссию, а зачем мне тогда делать то не же самое?

и не воспринимаете альтернативных точек зрения?

Потому что это не точки зрения, не? Это глупая ретрансляция легенд, мифов, «одна бабка сказала», либо «я не понимаю того, о чём я говорю». Это никак не котируется за мнение.

Да и ладно — это не аргумент. Просто эти мнение, даже предположим, что это мнение — не являются аргументированными. Люди начинают утверждать «фортран жив и может» — их спрашиваю — «где?», а в ответ тишина, либо не ссылка на такие же пустые утверждения, либо совершенно на левые темы.

Допустим «на фортране просто писать хелворды» и с этим никто не спорит, что на фортране удобнее писать тому, кто на нём/паскалике пишет всю свою жизнь. Правда эти рассуждения не имеют отношения к теме. Напомню — «высокопроизводительные вычисления» и «быстрее си».

Ну и да, в моём понимание «альтернативных точек зрения» быть не может — это невозможно. Оно может быть так, либо иначе. Ни никак не так и не иначе одновременно. Точки зрения и разговоры про них любите вы(общественность тутошняя) — поэтому я взываю вас соответствовать. При этом моё несоответствие этому никак не позволяет вам делать то же самое. Ведь себя же «вы» не обвиняете в том, в чём обвиняете меня?

Ну а в целом я уважаю любые мнения, если они осмысленное — т.е. человек может объяснить причины и вселенную в которой это может работать, даже если это не эта.

Если (допустим) вы действительно не тролль, как минимум не ругайтесь (ну там "обосрался", "говно" и так далее). Ну и тон другой сделайте. И не придётся постоянно новые аккаунты создавать. Не надо фраз вроде "Кто же сознается с тем, что он малоразвитая обезьяна, которая не осилила нормальный язык". Так, может, в личных блогах пишут. Может, это ваш стиль, саркастический, но тут так не принято. Нормально пишите, аргументированно

Эх, автор ещё явно такого не видел:
//Допустим, есть строка «abcd»,
//Как получить значение третьего элемента?
//Можно, например, так:
char c = «abcd»[3];
//а можно еще и так:)
c = 3[«abcd»];

Я видел

Ничто не мешает перекастовать указатель из маллока к указателю на VLA нужной формы. Массив будет получаться сплошным и любой размерности. Единственная проблема — вылет за границу, но если ты взялся за C, надо уметь это отлавливать.

int (*array)[N] = calloc(N*M, sizeof(int));
for(int i = 0; i < M; i++)
    for(int j = 0; j < N; j++)
         // Обработка A[i][j]

Да, вы правы. selgjos вас немного опередил, статью поправил

В Fortran'е вообще много удобств для работы с массивами. Операция поэлементного сложения или вмножения в сях близко нет. Приходится циклы городить.
А как обстоит со скоростью у APL?
(поясню о чём речь: APL имеет встроенные средства работы с матрицами)
Для передачи в аргументах именно массивов есть static:
void foo(restrict int a[static 5]);
Оно говорит компилятору — «a это массив int-ов с не менее, чем пятью элементами». Компилятор никак не проверяет истинность этого утверждения, и если передать не то, или меньше элементов, будет UB.

Преимущество С++ — в расширяемости (в разумных пределах).


Не знаю как в фортране, но в C++ можно легко написать требуемый в данной ситуации (например, который может работать на GPU и на CPU) класс для "многомерного массива". Или взять готовый из boost, blitz++, или другой библиотеки (тысячи их).
Поэтому все double a[n][n] — в топку, на C++ так не пишут. А пишут наподобие


array2D<double> a(n,n); 
a.setDim(m,m);
a(x,y) = 2 * a(y,x);

И в фортране вы вряд ли (возможно, я ошибаюсь) напишете


GPUArray2D<double> b(n,n); 
b.copyFrom(a);
gpuProcess(b);

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


И зачем впихивать матрицы в и без того раздутый стандарт? Они прекрасно реализуются в библиотеках.

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

Меня больше беспокоит, что долго не было стандартного API файловой системы (в С++17 — уже есть). Его, в отличие от матриц, просто средствами языка не реализуешь.

А почему «были»? И были, и есть, компилятор Fortran поддерживается фирмой Intel и оптимизирован под процессоры этой фирмы для наилучшего быстродействия. Последняя версия языка, если не ошибаюсь, от 2013 года. Какой-то странный спор, что лучше, отвёртки плоские или отвёртки крестовые. Под каждую задачу свой инструмент, и каждый пользуется тем инструментом, который ему удобнее. Зачем об этом спорить-то? В фортране ещё много чего есть, и комплексные типы переменных и перемножение матриц одним оператором, и библиотеки IMSL. Скорость вычислений определяется тестами, а не бла-бла-бла на форумах.
Boost не является частью C++17, а реализовать функционал сторонними библиотеками проблем не вызывает.

Вообще, правильное утверждение должно звучать так: «Синтаксисом и стандартной библиотекой C++17 не предусмотрены многомерные массивы».

А зачем они вообще нужны в стандартной библиотеке?


Вот чего действительно нет, так это нормальной системы управления зависимостями

А зачем они вообще нужны в стандартной библиотеке?

Не нужны. Потому что их желаемое представление в памяти, в зависимости от задачи, может быть различным. А C/C++ слишком низкоуровневые, чтобы насаждать такие вещи.

Вот чего действительно нет, так это нормальной системы управления зависимостями

Это потребует радикального пересмотра стандартов и приведёт к потере обратной совместимости, а на такое никто не пойдёт. Проще сделать форк от C++ (=новый язык).

Что вы имеете здесь в виду под управлением зависимостями? Менеджер пакетов для C++? Скажем, как npm для nodejs? Если да, то для создания такого не нужно ничего менять в языке. И даже в стандарт такой менеджер пакетов добавлять не нужно. Его нужно просто сделать. Я слышал, что есть даже несколько конкурирующих решений

Что вы имеете здесь в виду под управлением зависимостями?

Простоту подключения стороннего кода. Как сборки C# или Java.
И зачем для этого самого подключения нужно пересматривать стандарт? Просто берём менеджер пакетов, устанавливаем через него нужную вам либу, добавляем в используемую вами систему сборки опции для сборки с этой либой, добавляем нужные инклуды и готовы. Или вам нужно, чтобы ещё и в систему сборки опции сами прописывались? Или может, чтобы ещё и инклуды кто-то за вас дописывал? Опять-таки, всё это в принципе это осуществимо. То есть можно написать соответствующие инструменты. К стандарту это вообще не относится.

Под какой ОС пишите? Какая у вас система сборки? Мейкфайлы или что? Я просто совершенно не могу понять, зачем для этого «подключения стороннего кода» нужно переделывать стандарт.

Или может, вы хотите отказаться от препроцессорных инклудов и сделать вместо этого другое решение, как в других языках? Ну так на это есть Modules TS, такой proposal для стандарта C++, ищите в интернете. И я не думаю, что этот proposal так уж сильно переделывает весь стандарт. Что аж язык проще форкнуть
Или вам нужно, чтобы ещё и в систему сборки опции сами прописывались? Или может, чтобы ещё и инклуды кто-то за вас дописывал?

Конечно! Я хочу написать всего одну строчку: #import <...>, а всё остальное должен сделать компилятор-линковщик, без прописывания дополнительных опций в виде магических путей и магических дефайнов.

C++ плох также тем, что в нём нет стандартного описания проекта. Системы сборки (Makefile) предназначены, как это ни странно, именно для сборки, а не для описания проекта, которое вообще не должно содержать никаких команд. Как следствие — зоопарк всяких automake-ов.

Я просто совершенно не могу понять, зачем для этого «подключения стороннего кода» нужно переделывать стандарт.

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

Или может, вы хотите отказаться от препроцессорных инклудов и сделать вместо этого другое решение, как в других языках?

В точку! Препроцессор — зло. Представьте себе, сколько надо будет переписать кода, чтобы не осталось ни одного #include?

Под какой ОС пишите? Какая у вас система сборки? Мейкфайлы или что?

Под разные. Использую vcxproj и голый makefile, причём оба редактирую вручную, проекты у меня небольшие (до 50 файлов).

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


А что касается конкретно отказа от препроцессорных инклудов, ещё раз скажу: ищите по словам modules ts, работа ведётся. И для введения этих modules стандарт придётся поменять лишь немного. А сами либы не придётся разом все переписать, работа будет вестись постепенно. Почитайте про эти modules, там скорее всего объясняется как они собираются постепенно внедрять свои модули

Ну да. Надеюсь. Жду. Всех хотелок оно не решит, но удобства добавит.
К стандарту это не имеет отношения.

С одной стороны, да. С другой, тот же filesystem, казалось бы, тоже доступен в виде библиотеки, причём кроссплатформенной — бери да пользуйся. Но некоторые вещи всё-таки удобнее иметь в стандарте. Заодно это вынудит основных вендоров договориться между собой. Иначе могут наделать разных, каждый из которых придётся допиливать.

Затем, что многомерные массивы имеют весьма широкое применение в различных областях. Это не какая-то "высшая математика". А синтаксису бы не помешала поддержка мультииндексов и срезов.

К сожалению, в C++ есть одна неприятная штука — comma operator, будет мешать при определении многомерных индексов.

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

А я вот уверен, что кто-нибудь да использовал его в своем однострочнике. И по закону подлости, существует какая-нибудь популярная библиотека с этим однострочником внутрях...

Ну, если она достаточно популярна, то её и достаточно быстро починят ;-)

Каким образом фича языка может "мешать" — ума не приложу...

Очень просто. Раньше выражение int x[2,3] означало массив из трех элементов, а x[1,0] — обращение к нулевому.


Если разрешить мультииндексы — то старый код может сломаться.

Первая часть — неверно. Это никогда не "означало массив из трех элементов", как вы ошибочно полагаете.


В С89/90 и С++ размер массива в объявлении массива должен задаваться константным выражением. Но константное выражение в этих языках грамматически не может содержать оператора "запятая" на верхнем уровне. Поэтому последовательность int x[2, 3] является грамматически неверной, т.е. не вообще в принципе никак не распарсиваемой в этих языках.


Единственным грамматически верным способом протащить оператор "запятая" в константное выражение является использование дополнительных круглых скобок: int x[(2, 3)]. Однако в С и pre-C++11 С++ тут дополнительно вступают в силу уже явные (неграмматические) ограничения, дополнительно оговоренные в тексте стандарта: константным выражениям открытым текстом запрещено использовать оператор "запятая".


В post-C99 С размер локального массива уже не обязан быть константным выражением, но грамматика по прежнему сформулирована так, что int x[2, 3] не является распарсиваемой последовательностью. Поэтому в С99 разрешается только int x[(2, 3)].


В post-C++11 C++ разрешается использование оператора "запятая" в константных выражениях, но структура грамматики оставлена прежней, то есть в С++11 int x[2, 3] по прежнему не является распарсиваемой последовательностью. Поэтому в С++11 разрешается только int x[(2, 3)].


Вторая часть — верно. Действительно x[1,0] — это обращение к нулевому элементу и да, тут вы правы: оператор "запятая" мешает в контексте доступа. Тут бы потребовалось волевое решение: модификация грамматики по образу и подобию грамматики объявления (как я описал выше): грамматически запретить использование оператора "запятая" на верхнем уровне выражения. В принципе, возможно это стоило бы сделать вообще везде, т.е. разрешить использование оператора "запятая" только внутри скобок.

Ну, второй части достаточно чтобы создать проблемы с обратной совместимостью.

Это так, но последнее время и С и С++ ведут себя довольно смело с депрекацией фич, которые могут создать проблемы с обратной совместимостью. В С++ чего стоят одни только депрекации dynamic exception specification и неявного объявления функций копирования в классах.

Да

Но простите, STL тоже к языку относится скорее формально, а не как часть самого языка. Никто не мешает использовать любые другие библиотеки без какого либо STL вообще. И, кстати, много чего из буста постепенно мигрирует в этот самый STL.
STL — гарантированный поддерживаемый минимум любым современным компилятором C++. Вы можете быть уверены, что STL есть всегда.

Если же STL вдруг нет (ну бывает — опять же микроконтроллеры), то у вас уже не C++, а C с классами.

Автор статьи не увидел леса за деревьями.


Первый способ организации многомерного массива через индивидуальное выделение памяти для каждой строки массива — это то, как учат создавать run-time-sized двухмерные массивы студентов-первокурсников. И именно и только студенты-первокурсники так и делают. Уже ко второму курсу студент внезапно понимает, что когда речь идет об ручном выделении памяти для обычной матрицы, нет никаких причин выделять память для каждой строки индивидуально. Поэтому при "ручном" выделении памяти никто не поступает так, как показали вы, а делают несколько по-иному


double **a = new double *[n];
double *data  = new double[n * n];

double *row = data;
for (int i = 0; i != n; ++i, row += n)
  a[i] = row;

А теперь достаточно просто внимательно взглянуть на код выше, чтобы увидеть, что разница между этим вариантом, и вашим вариантом с пересчетом индексов заключается только в том, что вы настаиваете на постоянном пересчете индексов "на лету", то есть на явном выполнении умножения i * n при каждом доступе к элементу [i][j]. А этот вариант просто-напросто вычисляет произведение i * n и запоминает адрес a[i] для каждой строки i заранее, сохраняя его в отдельном массиве.


С концептуальной точки зрения реализацию с запоминанием адресов строк можно считать классической оптимизацией через препроцессинг: "выполним препроцессинг, потратим немного больше памяти для сохранения результатов этого препроцессинга, зато потом будем брать готовенькое, вместо того, чтобы каждый раз перевычислять заново".


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


P.S. Индивидуальное выделение памяти для строк матрицы, разумеется, не является безусловно бессмысленным. Оно может обладать ценностью в разнообразных jagged-array применениях, когда необходимо представлять "рваные"/разреженные матрицы и/или заниматься индивидуальным memory management для каждой строки. Но ваша-то статья говорит совсем не об этом…

Тут можно поспорить.
Во-первых, a[i*n+j] часто выполняется быстрее, чем 2 разыменования указателя a[i][j] (доступ к памяти — медленный); во-вторых, компиляторы выносят за пределы цикла подобные лишние вычисления.
Так что оптимальный вариант зависит от конкретной ситуации.


И ещё

немного странно видеть рассуждения о студентах вместе с кодом new double[n * n]. Есть же механизм RAII, разумно будет его использовать. Иначе сложно корректно освободить память.

Не совсем понятно, о каком "споре" идет речь, если вы фактически повторяете именно то, что я сказал во второй части своего комментария.


Замечание жа про RAII в контексте данного обсуждения неуместно — речь идет совсем не об этом.

Рассмотрим данный вами фрагмент кода. Причём предположим, что обращаться к i-му j-му элементу мы будем как a[i][j]. Тогда в вашем варианте выделение памяти будет происходить быстрее, чем в варианте с выделением памяти для каждой строчки. А время обращения к i-му j-му элементу будет тем же. Но я в своей статье обращал внимание на обращение, а не на выделение. Поэтому я не стал усложнять статью приведением вашего варианта. И конечно, я о нём знал.

Теперь по поводу того, что быстрее, умножение или обращение к вот этим вот вашим «закешированным» результатам умножения. Конечно, первое. Т. к. для обращения к вот этому отдельному массиву нужно обращаться к памяти. Пускай даже это будет кеш первого уровня, но это всё равно обращение куда-то. А умножение можно сделать в одних только регистрах.

Допустим, есть матрица и нам нужно её удвоить. Причём допустим, что нужно это сделать, идя по столбцам, т. е. идя сперва по второму индексу, а потом по первому. Да, постановка задачи искусственна, т. к. по строчкам было бы быстрее. Но всё же предположим, что нам это зачем-то нужно. Например, потому что это упрощённый вариант некой реальной задачи, в которой действительно нужно идти именно по столбцам, и по строчкам никак не получится.

Так вот, допустим мы выделили память одним куском (double *a = new double[n * n]). Тогда код будет выглядеть так:
for (int j = 0; j != n; ++j) // j идёт по столбцам, т. е. по второму индексу
  {
    for (int i = 0; i != n; ++i) // i идёт по строчкам, т. е. по первому индексу
      {
        a[i * n + j] *= 2;
      }
  }


А теперь допустим, что у нас есть этот ваш вспомагательный массив. А основная память выделена по строкам (как у меня в статье) или одним куском (как у вас), это не так важно. Тогда код будет выглядеть так:
for (int j = 0; j != n; ++j) // j идёт по столбцам, т. е. по второму индексу
  {
    for (int i = 0; i != n; ++i) // i идёт по строчкам, т. е. по первому индексу
      {
        a[i][j] *= 2;
      }
  }


Так вот, разумеется, первый код гораздо быстрее второго. В первом коде внутренний цикл будет оптимизирован так:
double *b = a + j;
for (int i = 0; i != n; ++i)
  b[i * n] *= 2;

или так:
double *end = a + n * n + j;
for (double *b = a + j; b != end; b += n)
  *b *= 2;

То есть фактически во внутреннем цикле процессор просто пройдётся по ряду ячеек в памяти, которые расположены с шагом n и удвоит их. Во втором же случае так соптимизировать будет невозможно. Процессору нужно будет пройтись по массиву a, разыменовать каждый элемент, и на основании его посчитать ещё один адрес и в нём уже удвоить значение. С учётом того, что обращение к памяти гораздо медленнее арифметических операций, это будет очень медленно.
Нужно пробовать и смотреть на скорость. У меня где-то валялись несколько классов для перформанс-тестов (когда-то алгоритмы сортировок сравнивал с их помощью). Могу завтра бросить ссылку на проект (только вычищу его в начале). В понедельник, если до того времени никто не озаботится раньше, могу заделать, собственно, тесты для проверки всяких тезисов по поводу быстродействия работы с матрицами. Всяко осмысленнее разговора на уровне общих понятий будет.

Лично мне это не надо

Добавил небольшой UPD в статью, чтобы не было таких вопросов

Я не понимаю, к чему это.


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


Речь идет совсем не о скорости.


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

что физичекская организация хранения собственно полезных данных матрицы в обоих случаях

В каких обоих случаях? Я тут вижу уже 3 способа. Один new, два new и мой "частокол".


Отличие, на которое вы напирали с самого начала статьи

Опять-таки, отличие между чем и чем?


В общем, поясните последний абзац

Это всё STL. Ад полнейший. Лучше бы убрали и взяли какие-нибудь корпоративные контейнеры гугла или ЕА. И модули добавили.
Кроме c++ и fortran, в каких других языках нет многомерных массивов, а в каких есть?
К слову — Delphi давно работает с многомерными массивами на уровне компилятора. Как известного, так и неизвестного (динамические) размера. И в параметрах можно их передавать, позже узнавая и устанавливая размерность. И обращение идёт как Arr[i][j]. И в памяти подряд данные лежат. В общем — все плюшки.
Паскаль изначально различал массивы и указатели «по дизайну».
Правда, там, в результате — были сложности с передачей массивов произвольного размера в процедуру.
Больше всего, мне нравятся комментарии! :)
Вспомнился обширный интересный комментарий kraidiky о невероятной разумности компилятора фортран. Правда, он не стал, как автор, раздувать из комментария статью (может, всё таки, зря).
В C++ много чего нет, зато есть много ненужного мусора.

Я был ярым фанатом C++ как ребенок-максималист. Но сейчас повзрослел, остепенился, добавил пару плюсов в язык, и теперь смотрю в стабильное будущее.
По поводу массива с данными, а не с указателями, тут есть одна очень веселая штука, на которую я в свое время наступил. Времена WinXP и Server2003. Мне необходимо разместить в памяти изображение размером всего-то 300 метров (битмап-карта). памяти на машинке 1,5 гига (XP ела если мне не изменяет память метров 300-500 всего) На серваке больше 4, но программа на 32 бита. И в какой-то момент у меня программа начинает вываливаться с ошибкой — не могу выделить память… (malloc, кажется, возвращал ошибку). После перезагрузки — все ок! Начинаю разбираться и оказывается, что памяти то много, но одного большого куска куда бы влезло 300 метров нет… Выход — свой мэнеджер памяти %) приехали, называется…
В 7 и 10 вроде как подход к выделению поменялся, но, если честно, не следил. так что аккуратнее с памятью и стеком, даже в винде до сих пор себе можно вполне спокойно отстрелить ногу…
Так вот, а в C передать массив в функцию нельзя. Точнее, можно, не нет специального синтаксиса, чтобы показать компилятору, что в функцию передаётся именно массив. Если написать:

void f (int a[2])

ну или:

void f (int a[])

То это будет эквивалентно такому коду:

void f (int *a)


Таки int * const a, что даёт некоторые нюансы.
Вообще, что характерно:



FORTRAN 77 (1980)[править | править вики-текст]
[...]
Увеличена максимальная размерность массива с 3 до 7. Сняты ограничения на индексы массива.
Усовершенствованы и расширены возможности работы с процедурами.


Fortran 2008 (2010)[править | править вики-текст]
Стандартом предполагается поддержка средствами языка параллельных вычислений (Co-Arrays Fortran)[5][11]. Также предполагается увеличить максимальную размерность массивов до 15,


(Вики, наше всё)

Т.е. ценой внесения в язык высокоуровневых абстракций (в отличие от Си/С++) — становится ограничение на предельную размерность массива, которую необходимо изменять на уровне стандарта…
А где может понадобиться 15-тимерный массив? Ограничение вполне уместно, т.к. в физических приложениях редко больше 4х нужно бывает (разные там струнные теории и иже с ними — особый случай, но не уверен, что народ там как раз на фортране считает — больше аналитики в этих областях).
А где может понадобиться 15-тимерный массив?


Везде.

Ограничение вполне уместно,


Ой, шо?
Здесь только что били пяткой в грудь, шо Фортран — круть немерянная по сравнению с Си — а теперь «ограничение вполне уместно»?

т.к. в физических приложениях редко больше 4х нужно бывает


Фкаких «физических»?

(разные там струнные теории и иже с ними — особый случай,


Откройте для себя понятие фазового пространства.
Для материальной точки — это будет шесть измерений, для протяжённого тела (с вращениями) — уже девять.
Массив таких тел… ой.
Обсчёт электромагнитного поля — две векторные величины в каждой точке, шесть отсчётов на точку трёхмерного пространства.
Приращения полей — по три отсчёта на каждую ось для обоих полей.
Т.е. это всё влёгкую улетает далеко и за три, и даже за пятнадцать измерений.
С фазовыми пространствами и нелинейной динамикой вполне знаком. Всегда было проще делать кучку маломерных массивов, пусть даже их и 15-20 штук оказывалось. Впрочем, это вопросы технические. Предельная вложенность циклов также может ограничивать предел размерности.
Ну вот о том и речь, что громогласные рассуждения про «замечательную способность по работе с матрицами» как-то незаметно переводятся на кучу несвязных маломерных массивов — от чего код начинает выглядеть просто чудовищно.
Возможно, возможно. Вы ещё количество GOTO в некоторых программках наших не видели. По старинке, старшее поколение продолжает писать де-факто на fortran-77. Но научный расчёт — штука странная. И здесь уж точно позволительно компилятору быть умнее программиста, который и не программист по сути, а просто имеет за плечами базовый курс программирования, численных методов и некоторый опыт в весьма узкой, надо сказать, области.

Что замечено, опять же, на личном опыте:
1. Редкая программа используется более чем одним человеком. Есть всего пара примеров в поле зрения, где программы написаны давно, и потихоньку модифицируются. Но за перфекционизмом в коде не гонятся, благо программа простая и позволяет легко понять, что в ней есть — простое решение конечно-разностной системы уравнений. Остальные пишут код под себя. Да, это не оптимально с точки зрения затрат времени. Но зато позволяет разобраться в специфических методах и глубже понять особенности физики задачи, что на первых порах очень важно. В дальнейшем, при развитии темы, программа может потихоньку модифицироваться, что обычно сводится к добавлению в уравнения новых слагаемых и/или изменению граничных условий. Конечно, есть некоторый костяк алгоритмов, текст которых, будучи единожды написанным, в дальнейшем не меняются годами у одного и того же автора.
2. Редкая программа используется для решения более чем одной задачи. Не считая доработок в виде добавки новых слагаемых в уравнения — это правда. Тем более что на каждое подробное(!) исследование уходит не один год, а на написание оптимальной универсальной программы может понадобиться столько же времени. Разве что человек, стремящийся к ускорению счёта, проведёт оптимизацию кода или добавит в него директивы OpenMP. И то таких случаев тоже очень немного.

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

Хорошо хоть не Фортран-66.

Что замечено, опять же, на личном опыте:
1. Редкая программа используется более чем одним человеком. Есть всего пара примеров в поле зрения, где программы написаны давно, и потихоньку модифицируются.


Ну тогда вообще непонятен предмет разговора.
Люди не умеют программировать ни на Си/С++, на на чём другом.
Уйдут эти люди — придут другие, которые будут юзать Си, питон или Матлаб с Октавой.
Для МКЭ — Ансис или Саломе.
Ну и т.д.
А все разговоры о «преимуществах Фортрана» — это так, рассуждения для бедных.
О чём и речь. Математики-физики редко являются хорошими программистами, поэтому пишут как могут. А программисты не могут написать эффективную программу, потому что не хватает способностей понять то, что придумал математик-физик, и применить оптимизации.

Поэтому часто (но не всегда!) бывает, что математик дико неэффективно использует суперкомпьютер для тех задач, где достаточно использования обычного компьютера с GPGPU. Ничего плохого здесь нет: для суперкомпьютера код можно написать на коленке, в отличие от GPU.
О чём и речь. Математики-физики редко являются хорошими программистами, поэтому пишут как могут.


Странно.
А ведь программирование и ЭВМ выросли из математики — и длительное время рассматривались едва ли не как часть математики.

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


Мне, честно говоря, не совсем понятно — чего может не хватать матфизику для более-менее нормального понимания Си/С++. Не на уровне senior'а — но хотя-бы начального middl'а.
Не в смысле написания библиотек — а в смысле понимания и использования самого языка.

Поэтому часто (но не всегда!) бывает, что математик дико неэффективно использует суперкомпьютер для тех задач, где достаточно использования обычного компьютера с GPGPU.


Страшно слушать такое про математиков.
Сегодня, кажется, в среде математиков даже общепринятыми становятся обозначения из Вольфрамовской Математики ихних «закорючек».
Какой там «фортран», боже мой…
Странно.
А ведь программирование и ЭВМ выросли из математики — и длительное время рассматривались едва ли не как часть математики.

Ага, только вот программирование начиналось с алгоритмов и вычислительной математики. Сейчас же можно быть хорошим программистом, не понимая в математике вообще ничего.

Мне, честно говоря, не совсем понятно — чего может не хватать матфизику для более-менее нормального понимания Си/С++

Опыта и времени. С тем же успехом матфизик может вести бухучёт и класть плитку.

Навыки более-менее адекватного программирования на C++ появляются только через пару лет опыта программирования как основного рода деятельности. Если вы считаете, что это не так, значит именно место эффект Даннинга — Крюгера.
Не будут. Мы их фортрану учим. Современному. Радиофизики, будущие железячники и софтовики, на нашем физфаке учат Си. Фундаментальные физики учат Фортран.

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

Питон — он же интерпретируемый. Откуда там скорость счёта? Си — да, есть люди, которые периодически начинают писать на нём. Не справляются, быстро вязнут в указателях, итераторах и правильном выделении памяти. К вопросу о скорости выполнения подойти не успевают за время обучения или сроки выполнения проекта.

Неужели нет более простых языков программирования, чем современный Фортран?

У части студентов база в виде чего-нибудь, обычно паскаля, имеется. Таких у меня на потоке было процентов 60, сейчас статистику не наводил. Но вроде учат и сдают успешно. Другое дело, что сдают успешно, а программировать всё равно не умеют, когда на третьем курсе вдруг дело доходит до расчётных исследовательских работ. Или до моего спецкурса со странным названием «Решение задач на ЭВМ», который в английских резюме предпочитаю писать как «Advanced numerical methods».

По фортрану же — современный куда проще старого, 77-го. Just for fun переписывал несколько своих программ с f90 на f77, используя все его ограничения. Жутковато, честно говоря. А f90 изучал, проводя аналогии с паскалем, который до этого был в 10-11 классах. C начал осваивать так же на этапе старшей школы, в параллель с паскалем, но тоже чисто для себя. Правда, так его и не знаю, хотя разобраться в вычислительных программах и допилить их для своих нужд, в принципе, могу. Но не хочу.
Не будут. Мы их фортрану учим. Современному. Радиофизики, будущие железячники и софтовики, на нашем физфаке учат Си. Фундаментальные физики учат Фортран.

А потом мы спрашиваем: почему наши ученые не конкурентоспособные?
Слава не_знаю_даже_кому, что такое не во всех университетах. Вот в ННГУ на ФизФаке 15 лет назад обучали на C++/C#/Mathematica.
Не беспокойтесь, Mathematica и Maple (и MATLAB на радиофизическом профиле) у нас тоже есть в программе обучения. А если у человека с головой всё в порядке — он с любым языком освоится. Глобальная проблема не в языке, а в нынешней системе образования и финансирования науки.

Учёные не конкурентоспособные? Наукометрический холивар что ли развести ещё. Вокруг есть масса людей, прекрасно известных на мировом уровне, с прекрасными индексами цитирования. Навскидку — опять же наш город Пермь; провинция глубокая, да. Навскидку по ближайшим кабинетам: кфмн, 34 года, h=8; наш экс-завкаф, дфмн, Заслуженный деятель науки РФ, h=14; его супруга, дфмн, Заслуженный деятель науки РФ, h=16; профессор, дфмн, h=8, маловато вроде бы, но — adjunct professor в университете в США; профессор, дфмн, h=18; профессор, дфмн, завлаб, h=28. И утечки мозгов хватило в 90-е гг. Некоторые вернулись, правда. Но с некоторыми не вернувшимися удаётся держать связь — и таким образом уже не один десяток лет жива большая российско-французская коллаборация (к сожалению, совсем недавно прекратилась двойная аспирантура), российско-бельгийская коллаборация с участием ESA, и разные прочие приятные сердцу мелочи.

Везде есть люди, способные научить. И везде есть люди, способные научиться. Даже в глубине африканских саванн или амазонских джунглей, где племена до сих пор за выживание борются.

Но не везде есть условия. На грант РФФИ много не разработаешься при нынешних суммах и типичных коллективах в 8-10 участников. Или гранты поддержки Ведущих научных школ РФ, в коллективах которых по 80 человек, а сумма ещё ниже среднего РФФИ. С РНФ люди зашевелились, что характерно. И с «УМНИК»-ом, хотя это уже более специфическая область. Но всё потому, что возможности появились, и нормальная поддержка.

И наука наша адекватно себя на мировом уровне держит. Другое дело, что об этом не говорят массы, кому куда интереснее обсасывать очередной разбившийся «Прогресс» или искать вину Роскосмоса в неудаче марсианской миссии, несмотря на то, что посадочный модуль полностью принадлежал ESA.
Ну ОК, наши пермские физики-фортранщики самые физически-фортранутые физики-фортранщики в мире, убедили. :)

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

Но многие, когда видят утверждения, подобные тому, что я взял в цитату, не испытывают гордость за российскую науку, и в принципе я их понимаю, помню, как лет 13 назад плакали бывшие одноклассники, которых в НГТУ заставляли изучать фортран, и когда они слушали от меня про C#
И ещё двоих забыл указать, из лаборатории «за стенкой».
Два дфмн: постарше (выпуск 1974 г.), h=20, активно работает с европейскими астрофизиками, и помладше (год рождения — 1974), h=16, специализируется в той же области. Оба в сущности занимаются магнитной гидродинамикой и математикой (вейвлет-анализом).
В западной науке эмпирическое правило: h ~ количество лет работы в науке (т.е после получения PhD).
h ёще по-разному считают, google scholar завышает довольно сильно по сравнению сo scopus, например.

Здесь все данные из scopus, уж подписку университет обеспечивает.

Эмпирика эта примерно верна для физиков, но уже давно известно, что h=20 — это отличный показатель для активного физика и очень рядовой для активного химика или биолога. Для математиков h ниже.
Оба в сущности занимаются магнитной гидродинамикой и математикой (вейвлет-анализом).


И как рисовать вейвлеты на фортране?
Рисуя тонны малоразмерных матриц? 0_0
а как можно вообще доверить человеку писать код на любом яп, если он «вязнет в указателях, итераторах и правильном выделении памяти»? Это же база. Он потом так и будет писать код, который переписать быстрее, чем выполнить

Просто нужны языки где не требуются указатели, итераторы и правильное выделение памяти. И они даже есть.


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


Во-вторых, можно использовать языки со сборщиком мусора. Те же C# или Java.

да, но это будет как бегун со сломанной в детстве ногой. И когда такому программисту скажут «че-то вот здесь оно долго работает, можешь сделать чтобы быстрее считалось?» он не просто не будет знать как ускорить, но даже не будет знать, из-за чего программа работает медленно.

Да кто скажет-то, если все программы "для себя" пишутся?


Обратите внимание, там учат физиков, а не программистов!

Просто нужны языки где не требуются указатели, итераторы и правильное выделение памяти. И они даже есть.


Остаётся непонятным — зачем в Си/С++ активно использовать указатели и динамическое выделение памяти — ежели в нём не разбираешься?
Си достаточно хорош и без этого.
Да блин, мне кажется позиция kbtsiberkin давно уже всем понятна. Fortran позволяет сразу научиться писать код. Такой, какой нужен в высокопроизводительных вычислениях. Без необходимости разбираться с этими указателями. Поэтому его и любят в academia. И на удивление всякие умножения матриц на фортране работают так же быстро, как и на си. А сегфолт получить сложнее.

Да, си мощнее. Но физикам фортран лучше по указанным выше причинам. Физикам компьютер нужен лишь как инструмент. Им не нужно уметь по-настоящему хорошо прогать.

Но сразу хочу сказать. Я только что лишь попытался пояснить позицию kbtsiberkin. Сам я не фанат фортрана. Вся эта статья написана просто чтобы рассказать хабру интересный факт о фортране. Не более. Сам я прогаю на C++.
Да блин, мне кажется позиция kbtsiberkin давно уже всем понятна. Fortran позволяет сразу научиться писать код.


Только это будет говнокод.
Нечитаемый, немодифицируемый и требующий крайне высоких трудозатрат для своей поддержки.
А фортран плох, прежде всего — отсутствие ясной и чёткой концепции — «что это» и «для чего оно».
Языки «сверхвысокого уровня», типа Матлаба — имеют развитые средства вывода — графики, мультипликацию, симуляцию.
Си/С++ — близок к аппаратуре, и позволяет целенаправленно оптимизировать программы под неё.
А фортран?
«Напиши код абы как — а транслятор откомпилирует в идеальную программу»?
А вывод будет в виде таблиц?
В научной среде программный код является не конечным продуктом, а инструментом. Написал код на коленке — получил результат. Если результат не понравится, код успешно хоронится. А если результат хороший, тогда можно заморочиться и написать нормально.
Насколько я понимаю научную среду, если результат хороший, переписывать тоже незачем. Каждый код пишется один раз для одной статьи, и для большинства статей даже из областей, напрямую связанных с программированием (например, в теории оптимизации) кода алгоритма найти совершенно невозможно. Каким образом производится проверка экспериментальных данных — даже думать страшно.

Проверка экспериментальных данных обычно производится независимыми экспериментами.

Не будут.


Человек предполагает — а бог располагает. :)

Мы их фортрану учим. Современному.


Вас бог покарает за развращение малолетних!!! ;)

Фундаментальные физики учат Фортран.


Ну т.е. всевозможные тензоры — в программах этих фундаментальных физиков — не реализуются. Ручками, всё ручками…
Чудесно.
Традиции — стррашшная сила…

Питон — он же интерпретируемый. Откуда там скорость счёта?


Не знаю — но люди что-то на нём кропают, есть NumPy, SciPy.
Есть (был?) какой-то проект на Питоне по сбору и расшифровке генетической информации — читал о нём лет пять — семь назад.

Не справляются, быстро вязнут в указателях, итераторах и правильном выделении памяти.


Гхм.
Во-первых — «прикладные» программисты должны пользоваться библиотеками.
Ну в во-вторых — паходу они таки не знают и не понимают Си — в заглавной статье тоже написано множество глупостей, к примеру.
Каких глупостей? Мне интересно
Например, глупостью является фраза, что double a[5][10] есть «массив указателей», «не размещаемый в памяти единым куском».
И где я такое говорил? Цитату дайте

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

вы прицепились к отсутствию в старых версиях си и с++ слова restrict, которое в компиляторах появилось очень давно. Игнорируя оптимизации, которые есть (или возможны) в си/с++, и которых нет и не будет в фортране
а за счет чего «Fortran быстрее C»?

?!?!?!? Дык про это вся моя статья была. Во-первых, restrict. До недавнего времени за счёт него Fortran был быстрее C. Далее, в настоящее время, видимо, они сравнялись по скорости. Но, скажем, лет 10-20-30 назад Fortran был действительно намного быстрее C. Тут в этих комментах ссылались на рассказ про супербыстрый оптимизатор фортрана, могу найти

restrict делается одним условным define'ом, благо все основные компиляторы такую фичу поддерживают. А еще? Я даже не говорю о том, что стандарт языка и реализация его в компиляторе — чуть чуть разные вещи.

Если бы в языке была возможность делать матрицы с доступом через [i][j], то ею бы всё равно никто не пользовался. Для максимальной эффективности всё равно пришлось бы делать специальный класс. Ведь при работе с матрицами чуть менее, чем всегда, нужны не столько операции доступа к произвольному элементу, сколько операции перехода с следующему элементу по вертикали или по горизонтали. И соответствующие итераторы можно реализовать вообще без умножения. Более того, переходы к следующему элементу нужны не сами по себе, а как часть более сложных операций, в которых участвуют весь i-й столбец и вся j-я строка. При помощи итераторов доступ к столбцам и строкам изнутри любых функций можно организовать, не вынимая самих элементов из матрицы.

Не согласен. Если в языке есть вот эти массивы, которые мне нужны ([i][j], но чтоб в памяти подряд), то никакие итераторы не нужны. Просто пишем тупой низкоуровневый код в стиле C. А оптимизатор сам уберёт эти умножения там, где надо

Давайте проясним, что именно вы предлагаете. Нужно зафиксировать в стандарте языка С++ конкретный способ хранения двумерного массива в памяти, удобный лишь для работы с матрицами. Причём при помощи кода, написанного в стиле языка С. Кроме того, ещё нужно зафиксировать требование к оптимизатору убирать умножение там, где надо. Это, кстати, где конкретно? Видимо, в стандарте должен быть прописан некий эвристический алгоритм, позволяющий оптимизатору определить, когда программист использует двумерные массивы для хранения матриц, а когда нет.


Ну, что ж! Обязательно напишите пропозал и держите нас в курсе. Очень интересны комментарии членов комитета.

Да не предлагаю я добавить в C++ многомерные массивы в стиле Fortran или C. Вся моя статья нужна просто, чтобы рассказать про этот курьёзный, так сказать, факт. Что в Fortran есть «нормальные» многомерные массивы, а в C++ — нет. Этот факт был удивительным для меня в своё время.

Кроме того, ещё нужно зафиксировать требование к оптимизатору убирать умножение там, где надо


Поясню, что я тут имел в виду. Рассмотрим такой код на C++:
double *a = new double[n * n];
for (int i = 0; i != n; ++i)
  for (int j = 0; j != n; ++j)
    ++a[i * n + j];

Здесь во внутреннем цикле есть умножение. Но если скомпилить это с оптимизацией, это умножение будет вынесено за пределы внутреннего цикла. Ну или убрано вовсе. Я имел в виду только это. То есть вот эту оптимизацию, которая уже есть в компиляторах.

Есть ещё один курьёзный факт. В С++ вы можете переопределить оператор [] у собственного класса матрицы и использовать его точно так же, как двумерный массив. И память для элементов сможете выделять так, как пожелаете. Зачем при наличии таких возможностей ещё и дополнительно перегружать обычные массивы лишними ограничениями на то, каким образом они должны храниться в памяти в многомерном случае?

Если я правильно помню, нельзя перегрузить оператор [] так, чтобы он принимал два индекса.

вы можете перегрузить operator [] так, чтобы он возвращал reference wrapper, который в свою очередь перегрузит operator [] для другой размерности. И будет вам array[i][j][k];
Сразу вспомнилась старая статья на Хабре, где чувак делал оператор --> (длинная стрелка)…

Кстати, у нас на проекте почему-то били по пальцам за использование операторов. Типа, код не очевидным делается от этого и можно лишние операции получить случайно. Слышал, это типа распространённое мнение. Но сам считаю что иногда можно — и использую.
А ещё операторы «головастик вперёд» и «головастик назад»: ~- и -~.
Сам считаю допустимым перегрузку операторов только для чисел, иногда — для векторов.
Кстати, у нас на проекте почему-то били по пальцам за использование операторов

Многие начинающие программисты, только познакомившись с переопределением операторов, очень любят определять операторы (всякие << >> ^) там, где простые функции очевиднее. А вот случаи где операторы действительно нужны и удобны встречаются намного реже и, как правило, требуют более опытного программиста (например, написание собственного контейнера с итераторами). Поэтому проще запретить
… и можно лишние операции получить случайно

Оператор отличается от явного вызова функции только семантически.
Оператор отличается от явного вызова функции только семантически


На проекте это объясняли так, что, мол, выражение "vec = ((a+b)/r)*A" визуально выглядит невинно и не предвещает "vec.set(a.sum(b).divScalar( r ).mulMatrix(A))" со всеми вытекающими из этого временными объектами и умножениями. Я немного пробовал спорить — мне ответили, что был печальный опыт использования операторов. Я поверил и старался ими не усердствовать.
В книге «Шаблоны С++. Справочник разработчика», есть целый раздел, посвященный оптимизации таких выражений, для исключения создания «лишних» временных объектов. Описанный подход немного потерял актуальность с выходом новых стандартов, но для понимания сути очень полезно почитать, я думаю. Чтобы не слепо верить и не никогда использовать, а разобраться и делать осознанный выбор.
В моём проекте по обработке изображений операция (a + b) / r представляет собой создание временного объекта, который только накапливает операции. При каждом обращении к пикселю (x, y) производится вычисление (a(x,y) + b(x, y)) / r(x, y).

Создание объекта происходит только при явном вызове:
((a + b) / r * A).instance();

На С++ с шаблонами это делается на раз-два. А вот чтобы добиться того же эффекта на C#, чтобы не было виртуальных вызовов, дженериков, да ещё чтоб все опрации были параллельными, пришлось позаморачиваться с кодогенерацией на лету.

С другой стороны, в C# очень простая кодогенерация на лету, в отличие от C++...

Либо можно перегрузить оператор ,(запятая) для int'ов, чтобы он возвращал некий объект типа matrix_position_t, и уже для него перегрузить [].

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

Честно говоря, впервые об этом слышу. Но выглядит весьма правдоподобно.
Ну, тогда можно перегрузить не [], а ().

нормальные компиляторы давно умеют не просто «выносить умножения за пределы внутреннего цикла», но и менять порядок итерации, если он не является оптимальным. После чего еще и применять SSE инструкции для работы с этим массивом. Итого ваш неоптимизированный код над многомерным массивом развернется в линейный проход по 4/8 элементов. В фортране почему-то (наверно в силу хитрой реализации многомерных массивов) порядок индексации в коде важен (кто-то в комментариях писал).
возможно, вы не в курсе, но арифметика на итераторах в бинарном виде преобразуется в операции над адресами точно так же, как арифметика на указателях и операторах []. Если только не используются какие-то оболочки, которые компилятор не может оптимизировать. STL в флагманских компиляторах оптимизируется на ура.
Меня тут внезапно призвал phprus
Главный вопрос, насколько понимаю, жив ли фортран и где именно он жив? И народ по-прежнему не верит, что язык с 50-летней историей активно применяется? Ну это вы зря, ей-богу. Откуда бы тогда взялись стандарты Фортран 2003 и 2008, которые хоть и с запозданием, но начинают воплощаться в компиляторах?

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

А активность использования Фортрана у нас вполне себе велика. Фактически, в Пермском научном центре среди гидродинамиков-расчётчиков можно выделить три типа: первые считают на фортране, вторые — в пакетах типа ANSYS, третьи, недавно обнаружились — воплощают метод конечных разностей в Mathematica. В московских институтах, специализирующихся на механике сплошных сред, ситуация выглядит аналогично. По-поводу, скажем, задач квантовой механики и физики магнетизма, которых у нас не так много — выборка слишком невелика, наверное, чтобы статистику навести. Тут я пока предпочитаю руками на бумажке, но недавно доросла одна задача до необходимости численного счёта — за 20 минут накидал на Фортране программку для взятия интеграла в двумерной области, и она благополучно шуршит уже вторую неделю, выдавая разные результаты.

По собственному опыту и по взгляду на коллег имею следующее впечатление: чтобы успешно писать на C++, надо суметь разобраться с классами и основами ООП, чтоб хотя бы представлять устройство и структуру реализуемой программы. Да, есть сферы, где C/C++ на порядки лучше Фортрана. Моя программистская практика сводится прежде всего к большому количеству именно вычислительных программ на Фортране и C. Так вот, на Фортране вообще думать не надо, что и куда. Завёл кучу переменных, написал, как им положено себя вести и что с ними происходит, и запустил счёт.

Среди этого немножко затесалась работа с микроконтроллерами, и с ними использую именно компиляторы на основе C. На почве Фортрана было тяжеловато осваивать, но в целом жить можно. Но реализовывать численный счёт на них было бы туговато.

Может, использовать вместо С — Аду, возможно, пожертвовав небольшой частью скорости? Ада — тоже стабильный язык (не меняется десятилетиями (последняя версия языка — от 2012 и я не знаю никаких серъёзных причин, по которым он опять сильно изменится в течении лет 30)), кроме того, понятный, привычным к паскалю людям (которых в России и СНГ немало), — поэтому наработки на нём не пропадут с течением десятилетий. Судя по тому, что его часто применяют для встраиваемых систем и систем реального времени а также военной техники — язык не из медлительных и не требует суперресурсов для работы, но, при том, способный сделать жизнь "программирующего непрограммиста"-физика, студента или аспиранта более лёгкой, не мучая его попусту.

А кроме того, уже не первый год уверен, что никакая Mathematica

Какое отношение это имеет к высокопроизводительным вычислениям?

и даже C++ не позволяют добиться тех же результатов с приемлемым уровнем простоты реализации.

Т.е. ваша проблема только в том, что вы не осилили ничего кроме фортрана? Аргумент все аргументам аргумент.

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

Интеловский компилятор является самым слабым компилятором, а как максимум топ2, если мы ограничим код тем, что осилите написать вы. Для справки — в мире существует всего 3компилятора.

Вы используете ifc даже не понимая в чём его собственно основная фишка. Его фишка — это реплейс вашего дерьма на готовый вменяемый код. Он понимает множество паттернов, которые используют для написания дерьма за партой. А так же вмести с ним в комплекте идут десятки блобов. Собственно когда фотран-адепт берёт своё дерьмо и сравнивает с mkl — он потом молится на штеуд. Ему кажется, что это какое-то невероятно колдунство.

И собственно тогда, когда дело доходит уже до сборки вменяемого кода — интел-днище даже в шедулинг под свои камни не может и сливает в хлам гцц, как и шланг.

скорость

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

К высокопроизводительным вычислениям ваше изваяние отношения не имеет.

Собственно поэтому школьники снимают с х86 пару гигафлопс, а нормальные пацаны терафлопс. Понюхайте хоть штеудские блобы. А так да, в параллельной вселенной всё круто — накидал дерьма и веришь, что оно само смоглось.

Так вот, на Фортране вообще думать не надо, что и куда.

Ну вот она собственно и причина.

Ну хорошо. Допустим. Что тут оптимизировать, чёрт возьми? Двумерный интеграл в шестиугольной области тут. Примерно вида I(x,y) = Int( f(x, y, \xi, \eta) d\xi d\eta ), только в полярных координатах, с записью результата в файл и ещё дополнительным его усреднением по углам.

Сумма она сумма и есть, распараллелить разве что слегка (сетка не того размера только). Если у кого-то после этого кода вытекут глаза, уж пардоньте, я физик. :)

Заголовок спойлера
program Graphene_Chi

real(8), parameter :: Pi = 3.14159265d0, &
K0 = 2.0d0*Pi / dsqrt(3.0d0)

real(8), dimension(:), allocatable :: chi_t
real(8), dimension(:,:), allocatable :: phi
real(8), dimension(:,:,:), allocatable :: Kx, Ky, chi

real(8), dimension(1:6) :: T = (/ Pi / 3.0d0, 0.0d0, -Pi / 3.0d0, -2.0d0*Pi / 3.0d0, Pi, 2.0d0*Pi / 3.0d0 /)

real(8) K0N, Pi3M, t0, q, beta, chi_0
integer N, M, p, i, j, p1, i1, j1, n_Ma
character(255) outname, avgname

open(1, file = "input.txt", form = "formatted")
read(1,*) N, M, t0, beta, n_Ma, outname, avgname
close(1)

allocate(phi(1:6,0:M))
allocate(chi_t(0:N))
allocate(Kx(1:6,1:N,0:M))
allocate(Ky(1:6,1:N,0:M))
allocate(chi(1:6,1:N,0:M-1))

q = 1.5d0*dsqrt(3.0d0)
K0N = K0/N
Pi3M = Pi / (3.0d0*M)

write(*,'(A,$)')"Grid creation starts..."
! grid - angular
do p = 1, 6, 1
do j = 0, M, 1
phi(p,j) = T(p) - Pi / 6.0d0 + j*Pi3M
enddo
enddo

! grid - radial; point (0, 0) should be calculated separately
Kx = 0.0d0
Ky = 0.0d0
do p = 1, 6, 1
do i = 1, N, 1
do j = 0, M, 1
Kx(p,i,j) = (real(i)/real(N))**2*K0*dcos(phi(p,j)) / dcos(phi(p,j) - T(p))
Ky(p,i,j) = (real(i)/real(N))**2*K0*dsin(phi(p,j)) / dcos(phi(p,j) - T(p))
enddo
enddo
enddo
write(*,'(A)') " done"

! susceptibility integration
chi = 0.0d0

write(*,'(A)')"Integration starts..."
do p1 = 1, 6, 1
write(*,'(A,I4,A)') "Sector ", p1, " starts..."
do i1 = 1, N, 1
write(*,'(A, I4)') "Line ", i1
do j1 = 0, M-1, 1
do p = 1, 6, 1
do i = 0, N-1, 1
do j = 0, M-1, 1
chi(p1, i1, j1) = chi(p1, i1, j1) + &
0.25d0*( F( Kx(p,i,j), Ky(p,i,j), Kx(p1,i1,j1), Ky(p1,i1,j1), beta, n_Ma ) + &
F( Kx(p,i+1,j), Ky(p,i+1,j), Kx(p1,i1,j1), Ky(p1,i1,j1), beta, n_Ma ) + &
F( Kx(p,i,j+1), Ky(p,i,j+1), Kx(p1,i1,j1), Ky(p1,i1,j1), beta, n_Ma ) + &
F( Kx(p,i+1,j+1), Ky(p,i+1,j+1), Kx(p1,i1,j1), Ky(p1,i1,j1), beta, n_Ma ) ) * &
( dsqrt(kx(p,i+1,j)**2 + ky(p,i+1,j)**2) - dsqrt(kx(p,i,j)**2 + ky(p,i,j)**2) ) * & ! dk
0.5d0*( dsqrt(kx(p,i+1,j)**2 + ky(p,i+1,j)**2) + dsqrt(kx(p,i,j)**2 + ky(p,i,j)**2) ) * Pi3M ! dphi
enddo
enddo
enddo
enddo
enddo
write(*,'(A)') " done"
enddo
write(*,'(A)') " done"

chi_0 = 0.0d0

do p = 1, 6, 1
do i = 0, N-1, 1
do j = 0, M-1, 1
chi_0 = chi_0 + &
0.25d0*( F( Kx(p,i,j), Ky(p,i,j), 0.0d0, 0.0d0, beta, n_Ma ) + &
F( Kx(p,i+1,j), Ky(p,i+1,j), 0.0d0, 0.0d0, beta, n_Ma ) + &
F( Kx(p,i,j+1), Ky(p,i,j+1), 0.0d0, 0.0d0, beta, n_Ma ) + &
F( Kx(p,i+1,j+1), Ky(p,i+1,j+1), 0.0d0, 0.0d0, beta, n_Ma ) ) * &
( dsqrt(kx(p,i+1,j)**2 + ky(p,i+1,j)**2) - dsqrt(kx(p,i,j)**2 + ky(p,i,j)**2) ) * & ! dk
0.5d0*( dsqrt(kx(p,i+1,j)**2 + ky(p,i+1,j)**2) + dsqrt(kx(p,i,j)**2 + ky(p,i,j)**2) ) * Pi3M ! dphi
enddo
enddo
enddo
chi_0 = 0.5d0*chi_0 / (2.0d0*Pi)**2*q
chi = 0.5d0*chi / (2.0d0*Pi)**2*q

! susceptibility output
write(*,'(A,$)')"Output starts..."
open(1, file = trim(outname), form = "formatted")

write(1,'(1x,I4,3E18.5E3)') 0, 0.0d0, 0.0d0, chi_0
do p = 1, 6, 1
do i = 1, N, 1
do j = 0, M-1, 1
write(1,'(1x,I4,3E18.5E3)') p, Kx(p,i,j), Ky(p,i,j), chi(p,i,j)
enddo
enddo
enddo

close(1)
write(*,'(A)') " done"

open(1, file = trim(avgname), form = "formatted")

chi_t(0) = chi_0
write(1,'(1x,2E18.5E3)') 0.0d0, chi_t(0)
do i = 1, N, 1
chi_t(i) = 0.0d0
do j = 0, M/2, 1
chi_t(i) = chi_t(i) + chi(1,i,j)
enddo
chi_t(i) = 2.0d0*chi_t(i) / M
write(1,'(1x,2E18.5E3)') Ky(6,i,0), chi_t(i)
enddo

close(1)
write(*,'(A)') " done"

deallocate(phi)
deallocate(Kx)
deallocate(Ky)
deallocate(chi)

contains

! electron dispersion relation
real(8) function E(x, y)
real(8) x, y
E = t0*dsqrt( 1.0d0 + 4.0d0*dcos(0.5d0*y)**2 + 4.0d0*dcos(0.5d0*y)*dcos(0.5d0*dsqrt(3.0d0)*x) )
end function E

! subintegral function
real(8) function F(x, y, x1, y1, beta, n)
real(8) x, y, x1, y1, beta, w_n
integer n
w_n = (2*n+1)*Pi / beta
F = ( (-w_n**2 - (E(x+x1,y+y1)**2 - E(x,y)**2) )*E(x+x1,y+y1)*dtanh( 0.5d0*beta*E(x+x1,y+y1) ) + &
(-w_n**2 + (E(x+x1,y+y1)**2 - E(x,y)**2) )*E(x,y)*dtanh( 0.5d0*beta*E(x,y) ) ) / &
( ( w_n**2 + (E(x+x1,y+y1) + E(x,y))**2 ) * ( w_n**2 + (E(x+x1,y+y1) - E(x,y))**2 ) )
end function F

end program Graphene_Chi

Советую не спорить с selgjos, тем более вам. Это бесполезно
Вы элементы массивов не в том порядке обходите, наружный цикл желательно делать по последнему индексу, и т.д. вплоть до внутреннего по первому. Плюс, сравнить с вариантом, когда индекс 1:6 не первый, а последний — я не знаю, что в итоге будет лучше векторизовано, если функция F будет заинлайнена (-ipo в ifort), — операции над 6 элементами (плюс — размер известен, минус — не степень двойки), или цикл 0:N-1. Для лучшей поддержки avx можно попробовать выровнять элементы массива, используя 1:8 вместо 1:6. Ну и, OpenMP никто не отменял.
Почему отсутствие многомерных массивов в STL — это такая проблема? Вот он пишется за 24 строчки (25, public забыл). Любой компилятор новее cfront заинлайнит вызов конструктора и получится тот же самый a[i*n+j]. Еще сорок строчек — это по правилам сделать операторы копирования, перемещения и т.п. Можно упороться шаблонами, чтобы у нас были скольки-угодномерные массивы, если очень хочется, еще строчек на сто.
http://pastebin.com/BBEsSAUM
написать двумерную обертку над массивом просто. А вот N-мерный с нормальными итераторами (чтобы потом пользоваться stl-алгоритмами) уже намного сложнее
Для этого в новом стандарте добавят span, который нужен буквально для итераторов.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации