Это статья про многомерные массивы. А ещё про ключевое слово restrict, до появления которого в C язык Fortran был быстрее C. Немного про то, зачем я это написал, см. в конце.

Многомерные массивы. Начну с многомерных массивов. Допустим, вам нужно максимально эффективно работать с большими квадратными матрицами в C++ (скажем, умножать их друг на друга). Причём размер матриц становится известен лишь в runtime. Что делать?

Всякие double a[n][n] и std::array<std::array<double, n>, n> не сработают, т. к. порядок матрицы (n) будет известен лишь в runtime. new double[n][n] не сработает по этой же причине (лишь первое измерение массива, создаваемого new, может быть runtime-выражением). Попробуем так:

double **a = new double *[n]; // Массив длины n указателей на double
for (int i = 0; i != n; ++i)
  {
    a[i] = new double[n];
  }
Вроде нормально. Теперь к i-му j-му элементу можно обратиться при помощи a[i][j]. Вот только есть один нюанс. Массив a — это массив указателей. А не единый двумерный массив, одним куском размещённый в памяти. Значит, обращение к i-му j-му элементу будет медленнее, чем могло бы быть, если бы данные были размещены одним двумерным массивом. И работа с такой матрицей (умножение на другую матрицу и т. д.) тоже будет медленее. То же самое относится к std::vector<std::vector<double>>.

UPD от 2016-12-11 0:00. Код выделения памяти выше можно написать и поэффективнее. Но моя статья не об этом, она об эффективности обращения к элементу.

А теперь правильный ответ:

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

Да, теперь к i-му j-му элементу придётся обращаться так: a[i * n + j]. Зато эффективно.

А как сделать так, чтобы синтаксис был нормальным (a[i][j]), но чтоб было эффективно? Нужно написать свой класс, который будет делать ровно это. В стандартной библиотеке такого нет.

Небольшое замечание. В C99 есть variable length arrays (VLA), но они здесь не помогут, т. к. gcc выделяет место для VLA на стеке, а матрицы у нас большие (я предупреждал) и в стек не влезут. (Тут я не прав, см. UPD.)

А вот в Fortran есть стандартный способ работы с динамическими двумерными массивами. Эффективно и с красивым синтаксисом обращения к i-му j-му элементу.

Было бы очень эффектно, если бы я написал здесь, что этот способ был в Fortran с самого начала. Что он поддерживался ещё в первом компиляторе Fortran'а, написанном в 1957-м году. В первом компиляторе Fortran'а (FORmula TRANslation), почти первого языка программирования на Земле (C создан не раньше 1969-го года). Что он поддерживался ещё когда программы на Fortran'е набирались на перфокартах. Я бы ещё поместил тут фотографию перфокарты для пущей убедительности. Но нет. Эта фича появилась в Fortran 90.

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

Вот так выглядит код для выделения памяти:

REAL, ALLOCATABLE :: A (:,:)
...
ALLOCATE (A (N,N))

И обращение к i-му j-му элементу происходит так: A(I, J) (впрочем, нужно учитывать, что в Fortran порядок индексов не совпадает с таковым в C и C++).

Впрочем, нормальный метод работы с многомерными массивами всё-таки есть в C++. В Boost.MultiArray. Но это Boost, это не стандартная библиотека, а потому не засчитывается.

restrict. Теперь про restrict. В Fortran'е в функцию можно передать два массива (видимо, по ссылке). И компилятор будет знать, что это два разных массива, а потому изменение одного не может привести к изменению другого. Т. е. если в функцию передан массив a и массив b, то компилятор знает, что изменение a[2] (пишу здесь в синтаксисе C) не приведёт к изменению b[3]. А значит, если в коде идёт запись в a[2], а затем чтение из b[3], то необязательно реально писать a[2] в память, а затем читать b[3] из памяти. Можно просто записать пока в регистр. Ищите в интернете по слову aliasing.

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

void f (int a[2])

ну или:

void f (int a[])

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

void f (int *a)

То есть фактически передаваться будет указатель. А значит, если мы передаём в функцию «массив» a и «массив» b, то передаваться будут указатели. А значит, у компилятора нет никакой информации о том, собираемся ли мы, используя один указатель, читать/писать память, доступную из другого, или нет (это было про C89, дальше будет пояснение). То есть он не знает, может ли так получится, что a[2] — это на самом деле b[3]. А значит, если мы пишем в a[2], а потом читаем из b[3], то компилятор не может это соптимизировать и вынужден сделать commit в реальную память. А значит, код будет медленнее эквивалентного на Fortran'е.

Лишь в C99 в языке наконец-то появилась фича restrict, которая позволила явно показать, что «вот эти вот указатели далеки друг от друга», то есть двигаясь начиная от одного, мы не попадём во второй. А значит, C вроде как догнал Fortran.

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

Про статью. Начал писать ответ на этот коммент от aso. Коммент разросся, пришлось превратить его в статью. Плюс давно хотел написать про многомерные массивы в Fortran'е. Статья получилась сумбурной, особенно вторая часть, про restrict. И вообще Fortran я не знаю. :) Насчёт первой части, про многомерные массивы, я почти уверен во всём, что говорю. Во второй части я уверен насчёт того, что относится к C. А вот как там устроены правила алиасинга в Fortran'е, я не знаю, просто где-то в интернете я как-то прочитал, что появление restrict позволило C наконец приблизиться Fortran. А ссылку потерял. :)

UPD от 2016-12-10 14:03. Посмотрел на этот коммент от selgjos, поэкспериментировал с компилятором и понял, что с помощью C99 VLA всё-таки можно добиться нужного мне эффекта. Итак, написать вот так: double a[n][n] не получится, т. к. матрица может не влезь в стек. Зато можно так:

double (*a)[n] = malloc (n * n * sizeof (double));

Теперь a — это то, что мне нужно. Единый блок размера n на n, размещённый в динамической памяти. С обращением к i-му j-му элементу при помощи a[i][j]. Если теперь нужно куда-то этот массив передать, это нужно делать так:

void f (int n, double a[n][n])

В общем, окей, в C есть нужные мне массивы. Как и в Fortran. А в C++ их по-прежнему нет.

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


UPD от 2016-12-12 22:49. Процитирую ещё один свой коммент:
Да не предлагаю я добавить в C++ многомерные массивы в стиле Fortran или C. Вся моя статья нужна просто, чтобы рассказать про этот курьёзный, так сказать, факт. Что в Fortran есть «нормальные» многомерные массивы, а в C++ — нет. Этот факт был удивительным для меня в своё время.
Поделиться с друзьями
-->

Комментарии (225)


  1. HOMPAIN
    10.12.2016 06:45

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

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

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

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


    1. akzhan
      10.12.2016 07:59
      +1

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


      Аналог в C++ -


      Type * __restrict A, Type * __restrict B;

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


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


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


    1. safinaskar
      10.12.2016 13:41
      +3

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

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


      1. DistortNeo
        10.12.2016 18:25

        Есть только один случай, когда 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 интов.


        1. TheCalligrapher
          10.12.2016 23:01
          +2

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


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


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


          1. DistortNeo
            11.12.2016 00:38
            +1

            С одной стороны, я не совсем понимаю, какое отношение вариант 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*).


            1. TheCalligrapher
              11.12.2016 01:36

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


              Поэтому еще раз: тип 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


              1. DistortNeo
                11.12.2016 02:31

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

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

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

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


                1. TheCalligrapher
                  11.12.2016 03:08

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

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


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

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


                  1. DistortNeo
                    11.12.2016 03:27

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

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

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

                    Возможно, баг. У меня при компиляции в x86 вывод 1, 1, 1, а при компиляции в x64 вывод 1, 0, 0. Должно быть 0, 0, 0, понятное дело.


  1. maaGames
    10.12.2016 07:49
    +6

    > Вот только есть один нюанс. Массив a — это массив указателей. А не единый двумерный массив, одним куском размещённый в памяти.

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


    1. vintage
      10.12.2016 10:50
      +2

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


      1. maaGames
        10.12.2016 11:36

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


    1. zuborg
      10.12.2016 14:10
      +1

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


      1. maaGames
        10.12.2016 14:16

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


      1. alexeykuzmin0
        12.12.2016 17:11

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


        1. mayorovp
          12.12.2016 17:14

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


    1. sleeply4cat
      10.12.2016 18:58

      Возможно ли заставить систему делать дефрагментацию RAM, если не удаётся выделить память?


      1. maaGames
        10.12.2016 19:11

        Во времена Вин98 можно было точно, потому что у меня была какая-то программа для дефрагментации оперативной памяти. Не уверен, что она действительно работала, но я был молод и доверчив…
        С практической точки зрения ответ, скорее, нет.


      1. DistortNeo
        10.12.2016 19:28
        +1

        Поясните, пожалуйста, вопрос. Что такое «дефрагментация» RAM и для чего она нужна?

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

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

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

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


    1. Akon32
      10.12.2016 22:10

      Доступ к одному линейному куску 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.Но на "средних" объёмах разница в пользу линейной схемы хранения была заметна.


      1. maaGames
        11.12.2016 05:52
        -1

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


        1. Akon32
          11.12.2016 13:22
          +1

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


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

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


  1. NeoCode
    10.12.2016 11:07
    +4

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


    1. Gorthauer87
      10.12.2016 13:49

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


    1. mikeus
      10.12.2016 14:24
      +7

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


    1. TheCalligrapher
      11.12.2016 20:28

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


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


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


      1. mikeus
        12.12.2016 00:37

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

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


  1. mafia8
    10.12.2016 11:10
    -3

    double a[n*n]
    a[i*n+j]
    устроит?


    1. ivlis
      10.12.2016 11:20
      +3

      Нет, конечно. Потому что хоть сколько-нибудь сложная формула превращается в ад.


      1. mafia8
        10.12.2016 11:58

        Можно привести пример сложной формулы? Чтобы было видно, в каком месте возникает сложность.
        Например, a[complicated_formula(asdf,zxcv)*n+more_complicated_formula(more,arguments)].


        1. vintage
          10.12.2016 12:09

          a[ ( ( y * cols + x ) / group * group + y % group ) * group + x % group ]


          1. mafia8
            10.12.2016 13:54
            +1

            Имелось ввиду, что есть ли разница между a[i][j] и a[i*n+j], даст ли вторая форма заметную дополнительную сложность.


          1. bandit_erik
            10.12.2016 16:09
            -1

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


            1. vintage
              10.12.2016 20:05

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


              1. bandit_erik
                10.12.2016 22:14
                +1

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


        1. DistortNeo
          10.12.2016 18:32

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

          Единственное разумное решение — обернуть всё это дело в класс.


          1. phprus
            10.12.2016 21:47

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

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


            1. DistortNeo
              10.12.2016 21:54

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

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

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


          1. nikolaynnov
            12.12.2016 05:19

            Единственное разумное решение — обернуть всё это дело в класс.

            И назвать его cv::Mat…


            1. DistortNeo
              12.12.2016 05:43
              +1

              Ну зачем же сразу так? Лично мне база OpenCV очень не нравится: зачем указывать тип пикселя при каждом обращении к нему, когда можно один раз указать тип изображения?


    1. MaximChistov
      10.12.2016 11:33
      +4

      статью не читай@коммент пиши?


    1. safinaskar
      10.12.2016 13:45

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


  1. semenyakinVS
    10.12.2016 11:35
    +9

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

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

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

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

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


  1. tzlom
    10.12.2016 13:32
    -1

    Нету — и не надо.

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

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

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

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


    1. vintage
      10.12.2016 13:38

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

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


      1. LibertyPaul
        10.12.2016 16:11

        Да, вы правы, сделать в C++ что то вроде A[x, y] не получится, но можно перегрузить оператор (), или реализовать метод at(x, y), и вобщем-то получить то-же самое.


        1. Jamdaze
          10.12.2016 16:48

          Получится, но это будет равнозначно А[y]. Можно замарочится и c [x][y].


      1. DistortNeo
        10.12.2016 18:35
        +4

        В C++ можно перегрузить оператор ()

        int& operator ()(int x, int y);
        int operator ()(int x, int y) const;


        1. vintage
          10.12.2016 20:10

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


          1. DistortNeo
            10.12.2016 20:14
            +1

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


          1. aso
            12.12.2016 08:34

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



            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


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


            1. safinaskar
              12.12.2016 18:49
              +1

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


      1. bandit_erik
        10.12.2016 22:15

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


  1. ilmarin77
    10.12.2016 13:48
    +1

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

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


  1. sergio_nsk
    10.12.2016 13:49
    +2

    Так вот, а в 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


  1. selgjos
    10.12.2016 13:49
    +3

    Начну с многомерных массивов. Допустим, вам нужно максимально эффективно работать с большими квадратными матрицами в 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.

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


    1. safinaskar
      10.12.2016 13:50

      А с чего это нормальный синтаксис?

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


    1. safinaskar
      10.12.2016 14:10

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


      1. QuakeMan
        11.12.2016 00:35

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

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

        https://ideone.com/99ixtJ


        1. safinaskar
          11.12.2016 00:45

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


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

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


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


        1. TheCalligrapher
          11.12.2016 01:52
          +1

          Стандартный язык С++ никогда не включал в себя язык С. Даже С89.


          1. nikolaynnov
            12.12.2016 05:07

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


            1. safinaskar
              12.12.2016 17:57
              +2

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


              1. nikolaynnov
                12.12.2016 20:29
                +1

                Всё таки не просто ссылается как на левый документ. Многое оттуда берётся и в самом 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.


                1. safinaskar
                  12.12.2016 22:00

                  Нет, ссылается. Это подтверждается большим списоком несовместимостей и фич 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


                1. safinaskar
                  12.12.2016 22:01

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


                  1. nikolaynnov
                    13.12.2016 02:12
                    +1

                    __restrict?
                    __declspec(restrict)?


                    1. safinaskar
                      13.12.2016 02:34
                      -1

                      Я про стандартное решение


            1. TheCalligrapher
              12.12.2016 19:05

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


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


    1. k155la3
      10.12.2016 20:00
      +2

      Насчёт «интересовать не может» — наука (по меньшей мере, в России) очень много пишет на Фортране. То есть, вот тот редкий случай, когда язык используется именно и только для того, для чего создавался.
      Кроме того, переход с Фортрана во многих случаях — это планы на полста лет вперёд. Чисто как пример: у атомщиков переписанные коды потребуют пересертификации, а это дикое бабло и отвественность.


      1. DistortNeo
        10.12.2016 20:08
        +1

        А можете привести пример? В моём научном окружении используют MalLab, Python, C#, C++, но никак не Fortran. Потому что Fortran — это шаг на 30 лет назад.


        1. Psychopompe
          10.12.2016 20:16

          Теория поля, HEP, MD, не буду обобщать, но знаю, что люди пишут.


        1. safinaskar
          10.12.2016 21:29
          +1

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


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

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

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

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


      1. selgjos
        10.12.2016 20:46
        -5

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

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


        1. DistortNeo
          10.12.2016 20:53
          +2

          А зачем тогда Intel выпускает и поддерживает свой Intel Fortran Compiler?


        1. phprus
          10.12.2016 21:54
          +2

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

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

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


          1. selgjos
            11.12.2016 23:32
            -2

            Вот именно в этой области фортран дико популярен и конкурирует с C&C++ нередко выигрывая у них.

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

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

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

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

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

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

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

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

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


            1. safinaskar
              12.12.2016 00:36

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


              1. selgjos
                12.12.2016 07:06
                -4

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

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

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

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

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

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

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

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

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

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

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

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

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

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


                1. mayorovp
                  12.12.2016 08:39
                  +2

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


                1. safinaskar
                  12.12.2016 18:26

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

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


                  1. selgjos
                    12.12.2016 22:06
                    -3

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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


                    1. Antervis
                      13.12.2016 05:50
                      +1

                      Предлагаю вам написать сайт на с++, запрос в бд на хаскелле и программу на микроконтроллер на js

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


                      1. ilmarin77
                        13.12.2016 07:47

                        https://github.com/cesanta/v7 — интерпретатор js для микроконтроллеров, кстати.

                        И еще такая штука есть: http://www.espruino.com/


                      1. selgjos
                        13.12.2016 09:41
                        -2

                        Предлагаю вам написать сайт на с++

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

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

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

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

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

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

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

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

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

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

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

                        Точно так же и с языками.


            1. phprus
              12.12.2016 12:33
              +1

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

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

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

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


              1. selgjos
                13.12.2016 04:47
                -3

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

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

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

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

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

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

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

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

                Аргументация уровня «кукареку все думают, что фортран в дерьме, но это не так — можете мне поверить», а так же высеры уровня «выходят новые „версии“ фортрана». Просто смешно. Выходят новые версии паскаля, делфятинки, бейсика и прочего дерьма. Никого это не волнует.


        1. safinaskar
          11.12.2016 00:13
          -1

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

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

          Но если есть уже написанный на Fortran код, то никакого смысла переписывать его на C нет, т. к. производительность от этого не изменится.


          1. encyclopedist
            11.12.2016 02:21
            +3

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

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


            1. safinaskar
              11.12.2016 02:38

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


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


              1. encyclopedist
                11.12.2016 02:44
                +1

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


                Вряд ли

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


                1. rPman
                  22.03.2017 01:09

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

                  Я бы не отказался от хотя бы по три экрана на глаз (фон, ближняя переферия и фокусные объекты), уже что то, размеры шлема при текущих технологиях громадны.


                  1. DistortNeo
                    11.12.2016 03:08
                    +1

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

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


                  1. encyclopedist
                    11.12.2016 05:05
                    +2

                    Именно поэтому я написал про "правильное использование". Использование вектора тут не нужно, достаточно 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)


                    1. TheCalligrapher
                      11.12.2016 07:40
                      -1

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


                  1. RPG18
                    11.12.2016 10:34

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


              1. DistortNeo
                11.12.2016 02:59
                +1

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

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


              1. selgjos
                13.12.2016 07:14
                -1

                Вот смотрите как интересно получается. Как вы ко мне — так и к вам. Показательно.

                Помогу вам.

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

                class

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

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

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

                vector

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

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

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

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

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

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

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

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

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

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

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


                1. staticlab
                  13.12.2016 17:57
                  +1

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


                  1. selgjos
                    15.12.2016 05:52
                    -2

                    Дело не в том, кто я — это никого не волнует, точно так и «кто вы».

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

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

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

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

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

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

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

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

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

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

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

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


                    1. staticlab
                      15.12.2016 09:38
                      +1

                      Пытаетесь выставить меня в свете будто-бы я дартаньян.
                      Обвиняю я их потому, что будучи несостоятельными в тех областях, о которых они пытаются рассуждать — они, собственно, что-то утверждают и проповедуют.

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


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

                      Обвиняю я в чём-то людей не потому, что они чем-то от меня отличаются.


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

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

                      Да и никакой вменяемый человек не будет писать на фортране

                      Точно так же уважающий себя человек не будет писать на любом коболо-дерьме. Будь то фортран, бейсик, паскалик и прочее.

                      днище не понимает почему не работает инвок

                      Убогая ламрюга. Как это прекрасно, когда обосравшийся балабол начинает съезжать на левые понятия, а потом что-то из себя строит.

                      Разумеется, вы сейчас снова будете психовать и оскорблять меня и всех окружающих, но чем именно вы лучше всех остальных? Тем, что знаете C++?


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

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


                      1. selgjos
                        15.12.2016 12:35
                        -2

                        Разумеется, вы сейчас снова будете психовать

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

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

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

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

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

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

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

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

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

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

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

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


                        1. RubyFOX
                          27.03.2017 09:58

                          Да, нужно в биполярный переделать, дел на 10 минут или даже меньше. Раскрыть, перерезать дорожку, закрыть.


                          1. selgjos
                            15.12.2016 19:50
                            -2

                            Тогда почему вы называете других дебилами

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

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

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

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

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

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

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


                        1. safinaskar
                          15.12.2016 19:26

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


    1. nikolaynnov
      12.12.2016 05:12
      +1

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


      1. safinaskar
        12.12.2016 18:18

        Я видел


  1. onetime
    10.12.2016 14:23
    +1

    Ничто не мешает перекастовать указатель из маллока к указателю на 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]
    


    1. safinaskar
      10.12.2016 14:24
      -1

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


  1. kefirfromperm
    10.12.2016 14:25
    +1

    В Fortran'е вообще много удобств для работы с массивами. Операция поэлементного сложения или вмножения в сях близко нет. Приходится циклы городить.


    1. Idot
      11.12.2016 17:50

      А как обстоит со скоростью у APL?
      (поясню о чём речь: APL имеет встроенные средства работы с матрицами)


  1. mwizard
    10.12.2016 15:49
    +2

    Для передачи в аргументах именно массивов есть static:

    void foo(restrict int a[static 5]);
    Оно говорит компилятору — «a это массив int-ов с не менее, чем пятью элементами». Компилятор никак не проверяет истинность этого утверждения, и если передать не то, или меньше элементов, будет UB.


  1. Akon32
    10.12.2016 16:21
    +6

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


    Не знаю как в фортране, но в 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);

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


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


    1. safinaskar
      10.12.2016 19:59

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


      1. Akon32
        10.12.2016 22:16
        +2

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


  1. AlexHa
    10.12.2016 17:31

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


  1. Lol4t0
    10.12.2016 19:08

    В C++17 до сих пор нет нормальных многомерных массивов

    Неверно


    http://www.boost.org/doc/libs/1_39_0/libs/multi_array/doc/user.html


    1. DistortNeo
      10.12.2016 19:32

      Boost не является частью C++17, а реализовать функционал сторонними библиотеками проблем не вызывает.

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


      1. Lol4t0
        10.12.2016 19:45
        +3

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


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


        1. DistortNeo
          10.12.2016 20:12
          +2

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

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

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

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


          1. safinaskar
            10.12.2016 22:05

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


            1. DistortNeo
              10.12.2016 22:15
              +1

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

              Простоту подключения стороннего кода. Как сборки C# или Java.


              1. safinaskar
                11.12.2016 00:27
                -1

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

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

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


                1. DistortNeo
                  11.12.2016 01:03
                  +1

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

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

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

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

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

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

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

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

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


                  1. safinaskar
                    11.12.2016 01:12
                    -1

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


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


                    1. DistortNeo
                      11.12.2016 01:23

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


                    1. DarkEld3r
                      12.12.2016 19:58

                      К стандарту это не имеет отношения.

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


        1. vintage
          10.12.2016 20:18
          +3

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


          1. DistortNeo
            10.12.2016 20:29

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


            1. vintage
              10.12.2016 21:07

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


              1. mayorovp
                11.12.2016 10:13

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


                1. vintage
                  11.12.2016 10:33
                  +1

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


            1. TheCalligrapher
              12.12.2016 20:17
              -1

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


              1. mayorovp
                12.12.2016 21:29

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


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


                1. TheCalligrapher
                  12.12.2016 21:55

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


                  В С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] — это обращение к нулевому элементу и да, тут вы правы: оператор "запятая" мешает в контексте доступа. Тут бы потребовалось волевое решение: модификация грамматики по образу и подобию грамматики объявления (как я описал выше): грамматически запретить использование оператора "запятая" на верхнем уровне выражения. В принципе, возможно это стоило бы сделать вообще везде, т.е. разрешить использование оператора "запятая" только внутри скобок.


                  1. mayorovp
                    12.12.2016 22:13

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


                    1. TheCalligrapher
                      12.12.2016 22:22

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


      1. safinaskar
        10.12.2016 21:09

        Да


      1. crea7or
        10.12.2016 21:21

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


        1. DistortNeo
          10.12.2016 21:40

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

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


  1. TheCalligrapher
    10.12.2016 22:11

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


    Первый способ организации многомерного массива через индивидуальное выделение памяти для каждой строки массива — это то, как учат создавать 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 для каждой строки. Но ваша-то статья говорит совсем не об этом…


    1. Akon32
      10.12.2016 22:41
      +2

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


      И ещё

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


      1. TheCalligrapher
        10.12.2016 23:03

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


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


    1. safinaskar
      10.12.2016 23:58
      +2

      Рассмотрим данный вами фрагмент кода. Причём предположим, что обращаться к 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, разыменовать каждый элемент, и на основании его посчитать ещё один адрес и в нём уже удвоить значение. С учётом того, что обращение к памяти гораздо медленнее арифметических операций, это будет очень медленно.


      1. semenyakinVS
        11.12.2016 03:53
        -2

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


        1. safinaskar
          11.12.2016 12:43
          +1

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


    1. safinaskar
      11.12.2016 00:02

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


      1. TheCalligrapher
        11.12.2016 00:21

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


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


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


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


        1. safinaskar
          11.12.2016 00:49

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

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


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

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


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


  1. lookid
    10.12.2016 23:35
    -6

    Это всё STL. Ад полнейший. Лучше бы убрали и взяли какие-нибудь корпоративные контейнеры гугла или ЕА. И модули добавили.


  1. dimka11
    11.12.2016 00:51

    Кроме c++ и fortran, в каких других языках нет многомерных массивов, а в каких есть?


  1. ElectroGuard
    11.12.2016 00:51

    К слову — Delphi давно работает с многомерными массивами на уровне компилятора. Как известного, так и неизвестного (динамические) размера. И в параметрах можно их передавать, позже узнавая и устанавливая размерность. И обращение идёт как Arr[i][j]. И в памяти подряд данные лежат. В общем — все плюшки.


    1. aso
      12.12.2016 08:29
      +1

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


  1. alex87is
    11.12.2016 01:03

    Больше всего, мне нравятся комментарии! :)


  1. dkosolobov
    11.12.2016 01:47

    Вспомнился обширный интересный комментарий kraidiky о невероятной разумности компилятора фортран. Правда, он не стал, как автор, раздувать из комментария статью (может, всё таки, зря).


  1. iperov
    11.12.2016 11:22
    -2

    В C++ много чего нет, зато есть много ненужного мусора.

    Я был ярым фанатом C++ как ребенок-максималист. Но сейчас повзрослел, остепенился, добавил пару плюсов в язык, и теперь смотрю в стабильное будущее.


  1. BigW
    11.12.2016 15:48

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


  1. aso
    12.12.2016 08:26
    +2

    Так вот, а в 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,


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

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


    1. kbtsiberkin
      12.12.2016 13:40
      -1

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


      1. aso
        12.12.2016 14:10

        А где может понадобиться 15-тимерный массив?


        Везде.

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


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

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


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

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


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


        1. kbtsiberkin
          12.12.2016 14:16

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


          1. aso
            12.12.2016 14:45
            +1

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


            1. kbtsiberkin
              12.12.2016 15:28
              +1

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

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

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


              1. aso
                12.12.2016 15:41
                -1

                По старинке, старшее поколение продолжает писать де-факто на fortran-77.

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

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


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


                1. DistortNeo
                  12.12.2016 16:08
                  +1

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

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


                  1. aso
                    13.12.2016 13:21
                    -1

                    О чём и речь. Математики-физики редко являются хорошими программистами, поэтому пишут как могут.


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

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


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

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


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


                    1. DistortNeo
                      13.12.2016 17:42
                      +3

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

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

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

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

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


                1. kbtsiberkin
                  12.12.2016 16:13
                  +1

                  Не будут. Мы их фортрану учим. Современному. Радиофизики, будущие железячники и софтовики, на нашем физфаке учат Си. Фундаментальные физики учат Фортран.

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

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


                  1. mayorovp
                    12.12.2016 17:07
                    +1

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


                    1. kbtsiberkin
                      12.12.2016 19:03

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

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


                  1. nikolaynnov
                    12.12.2016 20:43

                    Не будут. Мы их фортрану учим. Современному. Радиофизики, будущие железячники и софтовики, на нашем физфаке учат Си. Фундаментальные физики учат Фортран.

                    А потом мы спрашиваем: почему наши ученые не конкурентоспособные?
                    Слава не_знаю_даже_кому, что такое не во всех университетах. Вот в ННГУ на ФизФаке 15 лет назад обучали на C++/C#/Mathematica.


                    1. kbtsiberkin
                      12.12.2016 21:26
                      +2

                      Не беспокойтесь, Mathematica и Maple (и MATLAB на радиофизическом профиле) у нас тоже есть в программе обучения. А если у человека с головой всё в порядке — он с любым языком освоится. Глобальная проблема не в языке, а в нынешней системе образования и финансирования науки.

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

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

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

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


                      1. nikolaynnov
                        12.12.2016 23:10
                        +1

                        Ну ОК, наши пермские физики-фортранщики самые физически-фортранутые физики-фортранщики в мире, убедили. :)

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

                        Но многие, когда видят утверждения, подобные тому, что я взял в цитату, не испытывают гордость за российскую науку, и в принципе я их понимаю, помню, как лет 13 назад плакали бывшие одноклассники, которых в НГТУ заставляли изучать фортран, и когда они слушали от меня про C#


                    1. kbtsiberkin
                      12.12.2016 21:34

                      И ещё двоих забыл указать, из лаборатории «за стенкой».
                      Два дфмн: постарше (выпуск 1974 г.), h=20, активно работает с европейскими астрофизиками, и помладше (год рождения — 1974), h=16, специализируется в той же области. Оба в сущности занимаются магнитной гидродинамикой и математикой (вейвлет-анализом).


                      1. ilmarin77
                        12.12.2016 23:18

                        В западной науке эмпирическое правило: h ~ количество лет работы в науке (т.е после получения PhD).
                        h ёще по-разному считают, google scholar завышает довольно сильно по сравнению сo scopus, например.


                        1. kbtsiberkin
                          13.12.2016 11:52

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

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


                      1. aso
                        13.12.2016 13:00

                        Оба в сущности занимаются магнитной гидродинамикой и математикой (вейвлет-анализом).


                        И как рисовать вейвлеты на фортране?
                        Рисуя тонны малоразмерных матриц? 0_0


                  1. Antervis
                    13.12.2016 06:04
                    -1

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


                    1. mayorovp
                      13.12.2016 06:51

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


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


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


                      1. Antervis
                        13.12.2016 07:39

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


                        1. mayorovp
                          13.12.2016 08:31

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


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


                      1. aso
                        13.12.2016 13:02

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


                        Остаётся непонятным — зачем в Си/С++ активно использовать указатели и динамическое выделение памяти — ежели в нём не разбираешься?
                        Си достаточно хорош и без этого.


                    1. safinaskar
                      13.12.2016 12:35

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

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

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


                      1. aso
                        13.12.2016 13:06
                        -3

                        Да блин, мне кажется позиция kbtsiberkin давно уже всем понятна. Fortran позволяет сразу научиться писать код.


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


                        1. DistortNeo
                          13.12.2016 17:47
                          +2

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


                          1. alexeykuzmin0
                            14.12.2016 13:55
                            +1

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


                            1. mayorovp
                              14.12.2016 14:10
                              +1

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


                  1. aso
                    13.12.2016 12:57
                    +1

                    Не будут.


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

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


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

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


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

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


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

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


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


                    1. safinaskar
                      13.12.2016 13:03

                      Каких глупостей? Мне интересно


                      1. aso
                        13.12.2016 13:11

                        Например, глупостью является фраза, что double a[5][10] есть «массив указателей», «не размещаемый в памяти единым куском».


                        1. safinaskar
                          13.12.2016 13:23
                          +1

                          И где я такое говорил? Цитату дайте


                1. safinaskar
                  12.12.2016 19:11
                  +1

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


                  1. Antervis
                    13.12.2016 08:00

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


  1. Antervis
    12.12.2016 09:56

    а за счет чего «Fortran быстрее C»?


    1. safinaskar
      12.12.2016 18:52

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


      1. Antervis
        13.12.2016 06:12
        +1

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


  1. Shamov
    12.12.2016 11:40
    +2

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


    1. safinaskar
      12.12.2016 18:55
      -1

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


      1. Shamov
        12.12.2016 22:03

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


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


        1. safinaskar
          12.12.2016 22:48

          Да не предлагаю я добавить в 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];
          

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


          1. Shamov
            13.12.2016 00:21
            +1

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


            1. mayorovp
              13.12.2016 05:57

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


              1. Antervis
                13.12.2016 06:13
                +1

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


                1. semenyakinVS
                  14.12.2016 17:47

                  Сразу вспомнилась старая статья на Хабре, где чувак делал оператор --> (длинная стрелка)…

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


                  1. DistortNeo
                    14.12.2016 17:57
                    +1

                    А ещё операторы «головастик вперёд» и «головастик назад»: ~- и -~.
                    Сам считаю допустимым перегрузку операторов только для чисел, иногда — для векторов.


                  1. Antervis
                    15.12.2016 06:32
                    +1

                    Кстати, у нас на проекте почему-то били по пальцам за использование операторов

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

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


                    1. semenyakinVS
                      15.12.2016 15:21

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


                      На проекте это объясняли так, что, мол, выражение "vec = ((a+b)/r)*A" визуально выглядит невинно и не предвещает "vec.set(a.sum(b).divScalar( r ).mulMatrix(A))" со всеми вытекающими из этого временными объектами и умножениями. Я немного пробовал спорить — мне ответили, что был печальный опыт использования операторов. Я поверил и старался ими не усердствовать.


                      1. wander
                        15.12.2016 15:34
                        +2

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


                      1. DistortNeo
                        15.12.2016 16:58

                        В моём проекте по обработке изображений операция (a + b) / r представляет собой создание временного объекта, который только накапливает операции. При каждом обращении к пикселю (x, y) производится вычисление (a(x,y) + b(x, y)) / r(x, y).

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

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


                        1. mayorovp
                          15.12.2016 17:04

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


              1. Shamov
                13.12.2016 08:53
                -2

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


                1. mayorovp
                  13.12.2016 09:02
                  +1

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


                  1. Shamov
                    13.12.2016 12:34

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


          1. Antervis
            15.12.2016 06:24

            нормальные компиляторы давно умеют не просто «выносить умножения за пределы внутреннего цикла», но и менять порядок итерации, если он не является оптимальным. После чего еще и применять SSE инструкции для работы с этим массивом. Итого ваш неоптимизированный код над многомерным массивом развернется в линейный проход по 4/8 элементов. В фортране почему-то (наверно в силу хитрой реализации многомерных массивов) порядок индексации в коде важен (кто-то в комментариях писал).


      1. Antervis
        15.12.2016 06:14

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


  1. kbtsiberkin
    12.12.2016 13:38
    +1

    Меня тут внезапно призвал phprus
    Главный вопрос, насколько понимаю, жив ли фортран и где именно он жив? И народ по-прежнему не верит, что язык с 50-летней историей активно применяется? Ну это вы зря, ей-богу. Откуда бы тогда взялись стандарты Фортран 2003 и 2008, которые хоть и с запозданием, но начинают воплощаться в компиляторах?

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

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

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

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


    1. Vjatcheslav3345
      12.12.2016 22:59

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


    1. selgjos
      13.12.2016 11:28
      -2

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

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

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

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

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

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

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

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

      скорость

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

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

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

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

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


      1. kbtsiberkin
        13.12.2016 12:01
        -1

        Ну хорошо. Допустим. Что тут оптимизировать, чёрт возьми? Двумерный интеграл в шестиугольной области тут. Примерно вида 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


        1. safinaskar
          13.12.2016 12:43
          +1

          Советую не спорить с selgjos, тем более вам. Это бесполезно


        1. mobi
          13.12.2016 13:05
          +3

          Вы элементы массивов не в том порядке обходите, наружный цикл желательно делать по последнему индексу, и т.д. вплоть до внутреннего по первому. Плюс, сравнить с вариантом, когда индекс 1:6 не первый, а последний — я не знаю, что в итоге будет лучше векторизовано, если функция F будет заинлайнена (-ipo в ifort), — операции над 6 элементами (плюс — размер известен, минус — не степень двойки), или цикл 0:N-1. Для лучшей поддержки avx можно попробовать выровнять элементы массива, используя 1:8 вместо 1:6. Ну и, OpenMP никто не отменял.


  1. Salabar
    12.12.2016 22:35

    Почему отсутствие многомерных массивов в STL — это такая проблема? Вот он пишется за 24 строчки (25, public забыл). Любой компилятор новее cfront заинлайнит вызов конструктора и получится тот же самый a[i*n+j]. Еще сорок строчек — это по правилам сделать операторы копирования, перемещения и т.п. Можно упороться шаблонами, чтобы у нас были скольки-угодномерные массивы, если очень хочется, еще строчек на сто.
    http://pastebin.com/BBEsSAUM


    1. Antervis
      13.12.2016 14:01

      написать двумерную обертку над массивом просто. А вот N-мерный с нормальными итераторами (чтобы потом пользоваться stl-алгоритмами) уже намного сложнее


      1. Salabar
        13.12.2016 20:04

        Для этого в новом стандарте добавят span, который нужен буквально для итераторов.