На прошлой неделе вышла новая версия компилятора С/С++ от Intel — 16.0 aka Parallel Studio XE Composer Edition for C++. Существенно расширилась поддержка новых стандартов (C11, C++14, OpenMP 4.1), возможности по работе с Xeon Phi, вышли новые версии библиотек и ещё много всего «вкусного». Давайте более подробно посмотрим на то, что появилось в последнем релизе. Поехали!

  • Добавлена поддержка SIMD операторов для работы с целочисленными типами SSE на Линуксе. Теперь следующие операторы работают: + — * / & | ^ += -= *= /= &= |= ^= == != > < >= <=
    Простой пример, который раньше не компилировался (не забываем инклудить immintrin.h):
    __m128i x,y,z;
    x = y + z;
    

    Стоит отметить, что работает это только на Линуксе. Компилятор на Windows всё так же ругается: operation not supported for these simd operands
    Кроме того, поддерживаются только 128 и 256 битные SIMD типы, и только форма с двумя операндами, как в примере. Сами операнды должны быть одного типа, например, типы SSE от Intel не могут использоваться вместе с GNU типами, объявленными с атрибутом vector_size.
  • Существенно расширилась поддержка стандартов. Пожалуй, это направление развития компилятора можно так же отнести к одной из самых главных в новой версии.
    Так, если в версии 15 из стандарта C11 (для языка С, не путать с С++11) поддерживались только бинарные литералы (начинаются с префикса 0b или 0B), то сейчас есть практически всё. Последний общедоступный драфт стандарта можно найти здесь в свободном доступе. Хорошей обзорной статьи на русском про С11 я не нашёл, поэтому про каждую возможность языка напишу более детально. Не забываем, что при компиляции нужно указывать ключ /Qstd=c11 на Windows и -std=c11 на Linux и Mac OS X для того, чтобы всё это заработало:
    • Новые ключевые слова (как в C++11) для выравнивания данных _Alignas и _Alignof, позволяющие уйти от компиляторозависимых решений:
      // массив  cacheline выравнен по 64 байта
      _Alignas(64) char cacheline[64];
      printf("Alignment of char = %d\n", _Alignof(char));
      

      Про необходимость и значимость выравнивания данных я уже писал ранее.
    • Выражения, не зависящие от типа, с использованием ключевого слова _Generic. Это своего рода «шаблоны» из С++. Например, следующий макрос для извлечения квадратного корня sqrt(x) транслируется в sqrtl(x), sqrt(x) или sqrtf(x) в зависимости от типа параметра x:
      #define sqrt(x) _Generic((x), long double: sqrtl, default: sqrt, float: sqrtf)(x)
      

      А ведь раньше приходилось хорошо потрудиться, чтобы реализовать это ручками самому!
    • Спецификатор функции _Noreturn позволяет объявлять функции, которые никогда не возвращаются в вызывающий код. Это позволяет избегать предупреждений от компилятора для функций, у которых нет return’а, а так же включать ряд оптимизаций, которые можно выполнять только над «невозвратными» функциями.
      _Noreturn void func (); // func never returns
      

    • Новое ключевое слово _Static_assert, позволяющее выдавать ошибку компиляции в случае, если выражение равно нулю. Простой пример:
      // ошибка во время компиляции
      static_assert(sizeof(int) < sizeof(char), "app requires sizeof(char) to be less than char");
      error: static assertion failed with "app requires sizeof(char) to be less than char"
      

      В отличие от директив #if и #error позволяет отлавливать ошибки, которые трудно найти во время препроцессинга.
    • Анонимные структуры и объединения. Это некоммерческое общество анонимных алкоголиков… Шучу, просто проверил вашу концентрацию. Они используются для вложения структур и объединений. Например:
      struct T // C11
      {
        int m;
        union //анонимное объединение
        {
          char * index;
          int key;
        };
      };
      struct T t;
      t.key=1300; //прямой доступ к члену объединения key 
      

    • Особенность стандарта С11 в том, что он стандартизирует многопоточность в языке С. Конечно же, разработчики уже давным-давно используют блага параллельности в С, но, тем не менее, через библиотеки и другие расширения языка. Сейчас же это прописано в стандарте.
      Так, одно из новых ключевых слов, поддерживаемое компилятором Intel, _Thread_local. С его помощью можно указать, что переменная не является общей для потоков и каждый из них получает свою локальную копию.

  • По стандартам С и С++, компилятор не обязан соблюдать приоритет для вычислений выражений в скобках. Например, далеко не факт, что сложение B и C в выражении A+(B+C) будет выполнено в первую очередь, что приводит к различиям в численных результатах. Наконец появилась опция компилятора, которая отключает оптимизацию, меняющую порядок суммирования (реассоциацию) для типов с плавающей точкой. Теперь, если используются опции -fprotect-parens (Linux* OS и OS X*) или /Qprotect-parens (Windows*), порядок выполнения операций будет определяться расставленными скобками. Использование данной опции может несколько замедлить выполнение кода. По умолчанию, компилятор не включает данную опцию.
  • Полностью реализовав поддержку С++11 в версии 15.0, разработчики компилятора вплотную занялись следующим – С++14, который сейчас будет поддерживаться более чем на половину. Аналогично С11, есть страничка, на которой отслеживается поддержка различных возможностей стандарта в разных версиях компилятора. Включить поддержку С++14 можно опцией /Qstd=c++14 на Windows и -std=c++14 на Linux и Mac OS X.
    Итак, что же теперь поддерживается, начиная с нового релиза:
    • Обобщённые лямбда-функции
    • Захват выражений для лямбда-функций
    • Разделители разрядов
    • Атрибут [[deprecated]]
    • Вывод типа возвращаемого значения для функций
    • Агрегатная инициализация классов с инициализаторами полей

    Более детальное описание этих возможностей стандарта можно без труда найти в сети, например, на Вики, или в отличных постах на Хабре (Ч1 и Ч2).
    Так же появилась поддержка макросов (Feature Test macros), позволяющих определять поддержку той или иной возможности стандарта компилятором, или наличия хедера. Этот функционал пока ещё не в стандарте С++14, а только в TR, но обещает быть в скором времени. Простой пример:
    #if __cpp_binary_literals
    int const packed_zero_to_three = 0b00011011;
    #else
    int const packed_zero_to_three = 0x1B;
    #endif
    

    Теперь мы очень легко можем определить, поддерживаются ли компилятором бинарные литералы. Более подробно (например, найти табличку с именами макросов __cpp_binary_literals, __cpp_digit_separators и т.д.) ознакомиться с этой полезной возможностью можно здесь.
  • Добавлена очень полезная директива pragma block_loop, позволяющая контролировать оптимизацию с разделением циклов на блоки (loop blocking), о которой я подробно писал в этом посте.
  • Поддержка очередной версии стандарта OpenMP 4.1 (Technical Report 3), в основном расширяет возможности по работе с оффлодом (выгрузке) вычислений на сопроцессор Xeon Phi и другие возможные ускорители:
    • Появилась новая директива omp target enter data для того, чтобы мапить переменные на сопроцессор (возможно задавать to и alloc для опции map). Если директива omp target мапила переменные и выполняла код на устройстве, то omp target data только занимается данными. Соответственно, теперь имеется и директива omp target exit data для unmap’a переменных (возможно задавать from, release и delete для опции map).
    • Улучшены возможности по асинхронному выполнению кода. target регион теперь является задачей (task), поэтому возможен асинхронный оффлод с помощью существующей модели работы с задачами и опции nowait для директивы omp task.
    • Опция depend для директивы omp task, позволяющая реализовать выгрузку с зависимостями
    • Новые модификаторы always и delete для опции map

    Кроме того, что в рамках OpenMP 4.1 существенно расширяются возможности по работе с ускорителями, так же доработана специфичная для компилятора Intel реализация работы с сопроцессорами:
    • Если раньше было невозможно передавать через указатель поле объекта для выгрузки на сопроцессор в виде ptr->field, то теперь это ограничение убрано. При этом появилась возможность передать структуры, поля которой являются указателями. Сами структуры в этом случае передаются побитово, указатели копируются, а вот поля структур, на которые они указывают, нет.
    • Стало возможным выделять память только на сопроцессоре, не выделяя память на хосте, с помощью модификаторов targetptr и preallocated.
    • Появилось понятие stream (и новая опция stream для директивы pragma offload) – логической очереди для выгрузок. С её помощью теперь можно выгрузить несколько независимых вычислений на Xeon Phi из одного CPU потока. Порядок работы такой:
      Сначала, создаем стрим с помощью API функции _Offload_stream_create:
      OFFLOAD_STREAM* handle = _Offload_stream_create(
              int device,                  // Intel® MIC Architecture device number
              int number_of_cpus);         // Threads allocated to the stream
      

      И оффлодим в стрим с помощью директивы offload и опции stream, указывая при этом сигнальное значение для идентификации выгрузки. Это поможет определить, выполнен ли тот или иной оффлод:
      // Issue offload to a stream and identify with signal value s1
      #pragma offload … stream(handle) signal(s1)
      { … }
      …
      // Issue offload to a stream and identify with signal value s2
      #pragma offload … stream(handle) signal(s2)
      { … }
      …
      // Check if offload with signal value s1 has completed
      if (_Offload_signaled(s1)) …
      

      Гораздо больше деталей можно найти в нашей документации, которая была ощутимо расширены с выходом новой версии.



    Кроме всего перечисленного, естественно вышли и новые версии всех библиотек (Intel IPP, TBB, MKL), где тоже можно найти много всего интересного. Кроме того, в дополнение к известным «трехбуквенным», добавилась новая библиотека Intel DAAL, о которой я уже рассказывал в отдельном посте.Мой список улучшений и дополнений не является самым полным, но я постарался рассказать о наиболее значимых вещах. За бортом обзора остались изменения в Intel Cilk™ Plus, новые листинги с аннотациями от компилятора, улучшения скорости компиляции и ещё много мелких доработок. Пробуем самую свежую версию, которая всё так же доступна с пробной лицензией на 30 дней (без каких-либо функциональных ограничений и с полной поддержкой) и делимся с нами вашим опытом!

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


  1. QtRoS
    31.08.2015 18:06
    +4

    А кто использует компилятор от Intel?
    Это не сарказм или «наезд», просто интересен сегмент.


    1. FoxCanFly
      31.08.2015 19:22

      Intel, очевидно


    1. mezastel
      31.08.2015 19:30
      +6

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


      1. ivorobts
        31.08.2015 19:54

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


        1. mezastel
          10.09.2015 10:12

          Да, но тут компилятор просто глухо ломается, «ошибка номер 3», без документации. Приходится писать в Intel напрямую.


      1. OlegMax
        31.08.2015 21:47

        Когда уже они перейдут на Clang frontend? Сильная сторона Intel — оптимизация, зачем тратить ресурсы на повторение сделанного другими?


        1. Kepp
          01.09.2015 08:22
          +3

          В процессе. Насколько я знаю, команду под этот проект сформировали около года назад.

          Причина в том, что текущий компилятор настолько прекрасен, что что-то новое туда добавить (С++11,14,17) совсем непросто.


    1. ivorobts
      31.08.2015 19:53
      +3

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


      1. AxisPod
        31.08.2015 20:55
        +1

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


        1. ivorobts
          31.08.2015 22:54
          +3

          Я бы не сказал, что компилятор от Интел в плане поддержки стандартов проигрывает. Скажем, тот же С++11 полностью поддерживается у Microsoft только с VS2015, а в Интеловском с прошлой версии 15.0, что даже чуть раньше. На Windows для Интела ориентир в сторону Visual C++, на Линуксе gcc. GNUшному конечно, проигрывает в каких то фичах, но зато OpenMP лучше поддерживает(4.0 раньше всех поддерживался). В целом не всё так однозначно.


    1. pak63
      01.09.2015 10:12

      Я использую, хорошие возмножности для оптимизации кода. Советую вам попробовать PGO для своих задач, если используете VS2013 и выше, то там это сделано очень удобно. Компилятор интегрируются в VS, а для PGO сделано отдельное окно. Однако, сам был удивлен падению компилятора на конструкции copyin в OpenMP.