Новости из Блогов Измерение производительности Linux на компьютерах POWER

Discussion in 'Мировые новости. Обсуждения.' started by Suicide, 18 Jan 2013.

  1. Suicide

    Suicide Super Moderator
    Staff Member

    Joined:
    24 Apr 2009
    Messages:
    2,482
    Likes Received:
    7,062
    Reputations:
    693
    Измерение производительности Linux на компьютерах POWER
    Анализ производительности с помощью инструментов Linux



    Дата: 17.01.2013
    Адхимервал Занелла Нетто, программист, IBM
    Райан С. Арнольд, программист-консультант, IBM
    http://www.ibm.com/developerworks/ru/library/l-evaluatelinuxonpower/index.html



    Описание: Статья рассказывает, как на платформе POWER® можно измерить потери производительности ОС Linux®, связанные с компилируемыми языками, например, С или С++. В этой статье разбирается CPI-модель POWER7® и объясняется, как можно использовать стандартные инструменты Linux для поиска возможных точек останова процессора, сбоев конвейерной обработки и проблем, связанных с производительностью. В заключение дан пример анализа и оптимизации алгоритма для POWER7.


    Введение

    Оценки производительности приложений на современных компьютерах может оказаться сложной задачей. Стандартные общеизвестные инструменты с трудом справляются со всеми переменными, относящимися к производительности. Любая рабочая нагрузка по-разному влияет на подсистемы, которые она использует. Процесс измерения и настройки программы, «привязанной» к использованию CPU, сильно зависит от настройки программ, ориентированных на работу с вводом/выводом или памятью. В этой статье мы сконцентрируемся на программах, интенсивно использующих процессор и память и работающих в окружениях компилируемых языков (C, C++ и другие). Мы покажем как:
    -найти в программе "горячую точку" (область или функцию в программе, где находится большая часть исполняемых инструкций);
    -измерить, как программа работает на платформе POWER7 с помощью встроенного в процессор аппаратного счётчика производительности;
    -выбрать инструменты, которые используются для оценки производительности в ОС Linux.

    CPI-модель для POWER7

    Анализ производительности приложения основан на использовании CPI-метрик. Величина CPI (cycles per instruction) определяет количество тактов процессора, необходимых для выполнения инструкции. Каждая инструкция раскладывается на несколько этапов: в классическом RISC-конвейере имеется стадия загрузки инструкции, за которой следует декодирование инструкции и выборка регистров, непосредственное исполнение с доступом к памяти (если это требуется), и, наконец, запись результата. Процессор может улучшить свою CPI-характеристику (т.е. снизить её величину) за счёт внедрения параллелизма на уровне инструкций, когда на каждом этапе могут обрабатываться различные инструкции на разных стадиях. При оптимизации, постарайтесь уменьшить значение CPI, чтобы повысить уровень использования системы. На Рисунок 1 показан оптимальный поток исполнения инструкций для конвейерного процессора.

    Рисунок 1. Оптимальный поток инструкций для конвейерного процессора.
    [​IMG]

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

    Рисунок 2. "Замерший" конвейерный процессор
    [​IMG]

    В примерах, показанных на рисунках Рисунок 1 и Рисунок 2, предполагается, что за 11 рабочих тактов на полностью заполненном конвейере (где выполнение инструкции занимает 1 такт) процессор может выполнить восемь инструкций. Однако когда возникает "останов" на три такта, то за тоже количество тактов выполняются только 5 инструкций. Потери производительности в данном случае составляет порядка 40%. В зависимости от алгоритма невозможно избежать некоторых "остановов”, однако тщательный анализ поможет определить, как переписать или настроить отдельные фрагменты кода, чтобы устранить "останов”. Более полное и формальное описание реализации конвейерной обработки в современных процессорах и параллелизма на уровне инструкций можно найти в статье "Modern Microprocessors - a 90 minute guide", приведенной в разделе "Ресурсы".

    Модель CPI (CBM - CPI Breakdown Model) связывает этапы работы процессора со счётчиками производительности, чтобы показать, какой функциональный модуль CPU приводит к задержкам. CBM-модель зависит от архитектуры и модели процессора, так архитектуры POWER и INTEL используют абсолютно разные CBM-модели, а CBM-модели у POWER5 и POWER7 различаются. На Рисунок 3 изображен фрагмент CBM-модели POWER7, а здесь можно познакомиться с её текстовой версией.

    Рисунок 3. Фрагмент CBM-модели для архитектуры POWER7
    [​IMG]

    В архитектуре POWER аппаратные счётчики производительность представляют собой набор регистров специального назначения, содержимое которых обновляется, когда в процессоре происходит определенное событие. Процессор POWER7 содержит встроенный PMU-модуль (Performance Monitoring Unit – модуль мониторинга производительности) с шестью PCM-блоками (Performance Counter Monitor – счётчики-мониторы производительности), работающими на уровне потоков. Четыре блока из шести являются программируемыми, что позволяет одновременно вести мониторинг четырех событий, а всего существует более 500 событий, связанных с производительностью. Счётчики производительности в POWER7 объединяются в группы, и PMU может одновременно наблюдать только за событиями, относящимися к одной группе. На Рисунок 3 показана часть счётчиков производительности POWER7, используемых для создания CBM-модели POWER7. Счётчики, изображенные на Рисунок 3 работают в соответствии с этим профилем, чтобы определить, какой модуль CPU приводит к "останову" процессора и предоставить рекомендации о том, как необходимо настроить алгоритм для восстановления производительности.

    На Рисунок 3 белые прямоугольники – это определенные счётчики производительности POWER7, отслеживаемые в соответствии с профилем. В зависимости от их значений вычисляются значений серых прямоугольников, отмеченных звёздочкой, которые представляют собой метрики без выделенных аппаратных счётчиков производительности.

    Примечание: развёрнутое руководство по PMU для POWER7 можно найти в статье "Comprehensive PMU Event Reference POWER7", приведенной в разделе "Ресурсы".

    Инструменты для Linux

    Как можно использовать модули PCM, существующие в процессорах POWER7? Хотя на архитектуре POWER доступны различные методы для профилирования, включая аппаратные прерывания и контроль выполнения кода (например, инструмент gprof), «хуки» операционной системы (systemtap); но PCM предлагает богатый набор счётчиков, работающих непосредственно с функциональностью процессора. Профилировщик PCM постоянно через определенные интервалы предоставляет значение регистров процессора с помощью прерываний операционной системы. Хотя подобный способ профилирования может давать результаты менее точные, нежели результаты трассировки инструкций, но он меньше влияет на общую производительность системы и позволяет бенчмарк-программе работать фактически на полной скорости. Но полученные данные будут неточными, так как представляют собой аппроксимацию с учётом возможной ошибки.

    Для профилирования PCM в Linux чаще всего используются два инструмента: OProfile и perf (см. ссылки в разделе "Ресурсы"). Хотя оба инструмента основаны на одном и том же принципе, постоянно делая выборку из специального аппаратного регистра через syscall вместе с обратным трассированием рабочей нагрузки, но каждый из них конфигурируется и используется различными способами.

    Инструмент OProfile – это профайлер системного уровня для Linux, способный выполнять профилирование всего работающего кода с минимальной добавочной нагрузкой. Он состоит из драйвера ядра и демона для сборки проверочных данных, а также нескольких инструментов, которые используются после процесса профилирования для преобразования данных в информацию. Отладочные символы (параметр –g компилятора gcc) не требуются, если вы не хотите работать с размеченным исходным кодом. В свежей версии 2.6 ядра Linux OProfile может предоставлять информацию о результатах профилирования в стиле gprof. Обычно при использовании OProfile нагрузка на систему увеличивается на 1 – 8 %, в зависимости от частоты выборки данных и сложности рабочей нагрузки.

    На архитектуре POWER OProfile работает, наблюдая за группами аппаратных и программных счётчиков производительности, хотя различные группы и не могут использоваться вместе. Поэтому, чтобы получить значения различных счётчиков производительности для одной и той же рабочей нагрузки, приходится выполнять её несколько раз с различной конфигурацией событий OProfile. Это также значит, что нельзя наблюдать за всей CBM-моделью в один момент времени. Доступные группы определены в уже упоминавшемся документе “POWER7 PMY Detailed Event Description”, также их список можно получить с помощью команды, приведенной в Листинг 1.

    Листинг 1. Вывод групп OProfile
    Code:
    # opcontrol -l
    В Листинг 2 дается пример команд для конфигурации и запуска OProfile.

    Листинг 2. Конфигурация OProfile для отслеживания тактов CPU на платформе POWER7
    Code:
    # opcontrol -l
    # opcontrol -–no-vmlinux
    # opcontrol -e PM_CYC_GRP1:500000 -e PM_INST_CMPL_GRP1:500000 -e PM_RUN_CYC_GRP1:500000 
    -e PM_RUN_INST_CMPL_GRP1:500000
    # opcontrol --start
    В Листинг 3 запускается рабочая нагрузка и собирается информация с помощью OProfile.

    Листинг 3. Последовательность команд для запуска OProfile
    Code:
    # opcontrol --dump 
    # opcontrol –-stop
    # opcontrol --shutdown
    Чтобы получить отчёт счётчика производительности, воспользуйтесь командой из Листинг 4.

    Листинг 4. Генерация отчёта OProfile
    Code:
    # opreport -l > workload_report
    Примечание: подробное руководство по OProfile (хотя и не обновленное для POWER7) можно найти в статье "Identify performance bottlenecks with OProfile for Linux on POWER", опубликованной на портале developerWorks (см. раздел "Ресурсы").

    Инструмент perf, представленный в версии 2.6.29 ядра Linux, анализирует события, связанные с производительностью, на аппаратном и программном уровне. Преимуществом данного инструмента является возможность использования на уровне программ, а не на уровне всей системы, как Oprofile. Он также поддерживает настраиваемые списки счётчиков производительности, например, 'cpu-cycles OR cycles', 'branch-misses' или 'L1-icache-prefetch-misses', а также умеет объёдинять группы PMU для одновременного сбора информации со счётчиков производительности, относящихся к различным группам, но за счёт некоторой потери точности измерения.

    Негативным фактором, связанным с perf, является то, что хотя этот инструмент позволяет собирать информацию с аппаратных счётчиков производительности напрямую, но он не распознаёт имена счётчиков, объявленные в CBM-модели POWER7, а использует вместо них оригинальные шестнадцатеричные значения. В Таблица 1 представлено соответствие между событиями OProfile и шестнадцатеричными значениями, используемыми в perf (с помощью опции для вывода необработанных записей о событиях), который можно использовать для привязки к CBM-модели POWER7.

    Таблица 1. Исходные коды событий для архитектуры POWER7
    [​IMG]

    Примечание: подробное руководство по perf (хотя и не обновленное для POWER7) можно найти в статье "Using perf on POWER7 systems", опубликованной на IBM Wiki (см. раздел "Ресурсы").

    Привязать оригинальные коды, используемые perf, к событиям POWER7, определенным в OProfile, можно также с помощью проекта libpfm4 (см. раздел "Ресурсы"). Эти коды определены в заголовочном файле для POWER7 – lib/events/power7_events.h. В файле примера examples/showevtinfo также приведены имена событий и соответствующие им шестнадцатеричные коды.

    Профилирование является стандартным способом сбора информации со счётчиков производительности, который также позволяет разработчикам идентифицировать в коде наиболее часто исполняемые места и места с интенсивным обращением к данным. Также профилирование помогает определить в коде участки, влияющие на производительность, выявить шаблоны обращения к памяти и т.д. Но прежде чем начать профилирование приложения, необходимо определить стратегию оценки производительности. Программа может состоять из различных модулей и/или динамических объектов совместного доступа (DSO - dynamic shared object); она может интенсивно использовать возможности ядра, зависеть от шаблона доступа к данным (например, при интенсивном обращении к L2 или L3-кэшу) или сконцентрировать нагрузку на модулях, выполняющих векторные операции. В следующем разделе мы расскажем о возможных стратегиях для оценки производительности.

    Стратегии оценки производительности

    Первоначальная оценка производительности сводится к поиску "горячих точек" программы изучением счетчика утилизации циклов CPU. Для этого на архитектуре POWER7 необходимо пронаблюдать за событиями, перечисленными в Таблица 2.

    Таблица 2. Счётчики утилизации тактов CPU для архитектуры POWER7
    [​IMG]

    Запустив OProfile для отслеживания этих событий, можно получить информацию о полном времени, потраченном процессором, в необработанном виде. В листинге 5 представлен результат вывода профайлера для бенчмарк-компонента 403.gcc из тестового набора SPECcpu2006, скомпилированного с помощью IBM Advance Toolchain 5.0 for POWER (см. ссылку в разделе "Ресурсы"). Представленный вывод был получен с помощью команды opreport –l.

    Листинг 5. Вывод команды opreport –l для бенчмарк-компонента 403.gcc (выведены значения счётчиков PM_CYC_GRP1 и PM_INST_CMPL_GRP1)
    Code:
    CPU: ppc64 POWER7, speed 3550 MHz (estimated) 
    Counted PM_CYC_GRP1 events ((Group 1 pm_utilization) Processor Cycles) with a unit 
    mask of 0x00 (No unit mask) count 500000 
    Counted PM_INST_CMPL_GRP1 events ((Group 1 pm_utilization) Number of PowerPC 
    Instructions that completed.) with a unit mask of 0x00 (No unit mask) count 500000 
    
    samples  %        samples  %        image name      app name        symbol name 
    204528    7.9112  32132     1.3848  gcc_base.none   gcc_base.none   reg_is_remote_cons\
                                                                        tant_p.isra.3.part.4 
    125218    4.8434  246710   10.6324  gcc_base.none   gcc_base.none   bitmap_operation 
    113190    4.3782  50950     2.1958  libc-2.13.so    libc-2.13.so    memset 
    90316     3.4934  22193     0.9564  gcc_base.none   gcc_base.none   compute_transp 
    89978     3.4804  11753     0.5065  vmlinux         vmlinux         .pseries_dedicated_\
                                                                        idle_sleep 
    88429     3.4204  130166    5.6097  gcc_base.none   gcc_base.none   bitmap_element_\
                                                                        allocate 
    67720     2.6194  41479     1.7876  gcc_base.none   gcc_base.none   ggc_set_mark 
    56613     2.1898  89418     3.8536  gcc_base.none   gcc_base.none   canon_rtx 
    53949     2.0868  6985      0.3010  gcc_base.none   gcc_base.none   delete_null_\
                                                                        pointer_checks 
    51587     1.9954  26000     1.1205  gcc_base.none   gcc_base.none   ggc_mark_rtx_\
                                                                        children_1 
    48050     1.8586  16086     0.6933  gcc_base.none   gcc_base.none   single_set_2 
    47115     1.8224  33772     1.4555  gcc_base.none   gcc_base.none   note_stores
    Листинг 6. Вывод команды opreport –l для бенчмарк-компонента 403.gcc (выведены значения счётчиков PM_RUN_CYC_GRP1 и PM_RUN_INST_CMPL_GRP1)
    Code:
    Counted PM_RUN_CYC_GRP1 events ((Group 1 pm_utilization) Processor Cycles gated by the 
    run latch.  Operating systems use the run latch to indicate when they are doing useful 
    work.  The run 
    latch is typically cleared in the OS idle loop.  Gating by the run latch filters out 
    the idle loop.) with a unit mask of 0x00 (No unit mask) count 500000 
    Counted PM_RUN_INST_CMPL_GRP1 events ((Group 1 pm_utilization) Number of run 
    instructions completed.) with a unit mask of 0x00 (No unit mask) count 500000 
    
    samples  %        samples  %        samples  %      app name        symbol name 
    204538    8.3658  32078     1.3965  gcc_base.none   gcc_base.none   reg_is_remote_consta\
                                                                        nt_p.isra.3.part.4 
    124596    5.0961  252227   10.9809  gcc_base.none   gcc_base.none   bitmap_operation 
    112326    4.5943  50890     2.2155  libc-2.13.so    libc-2.13.so    memset 
    90312     3.6939  21882     0.9527  gcc_base.none   gcc_base.none   compute_transp 
    0              0  0              0  vmlinux         vmlinux         .pseries_dedicated\
                                                                        _idle_sleep 
    88894     3.6359  124831    5.4346  gcc_base.none   gcc_base.none   bitmap_element_all\
                                                                        ocate 
    67995     2.7811  41331     1.7994  gcc_base.none   gcc_base.none   ggc_set_mark
    56460     2.3093  89484     3.8958  gcc_base.none   gcc_base.none   canon_rtx
    54076     2.2118  6965      0.3032  gcc_base.none   gcc_base.none   delete_null_pointer\
                                                                        _checks
    51228     2.0953  26057     1.1344  gcc_base.none   gcc_base.none   ggc_mark_rtx_childr\
                                                                        en_1 
    48057     1.9656  16005     0.6968  gcc_base.none   gcc_base.none   single_set_2 
    47160     1.9289  33766     1.4700  gcc_base.none   gcc_base.none   note_stores
    Каждое отслеживаемое событие представлено в выводимой информации двумя столбцами. В первом столбце показывается количество обращений к счётчику PCM, относящемуся к данному событию, а во втором столбце показывается, какое количество процентов от общего числа обращений занимает первое значение. Как видно по данному отчёту, символ reg_is_remote_constant_p занимает первое место по количеству использованных тактов процессора, и поэтому является подходящим кандидатом для оптимизации. Данный тип профилирования позволяет только выявить, какие символы занимают больше всего тактов процессора, но не позволяет определить, насколько полно загружен конвейер процессора. Определить степень утилизации конвейера можно, если сравнить показания счётчиков.

    Рассмотрим счётчик PM_INST_CMPL_GRP1 (вторая пара столбцов), его процентное значение для символа bitmap_operation оказывается выше, чем для символа reg_is_remote_constant_p. Этот счетчик производительности увеличивается на единицу каждый раз, когда процессор завершает выполнение инструкции, тогда как счётчик PM_CYC_GRP1 измеряет только количество использованных тактов процессора. Даже без дополнительного анализа это соотношение может показать, что символ reg_is_remote_constant_p сильнее "тормозит" процессор, нежели символ bitmap_operation, так как количество инструкций, выполненных процессором для символа reg_is_remote_constant_p меньше, чем количество инструкций, выполненных для символа bitmap_operation. Данное исследование указывает направление, в котором следует сфокусировать дальнейшие усилия по оптимизации.

    Прежде, чем вы влезать глубже и разбирать код, будет полезно понять, с чем именно в основном взаимодействует рабочая нагрузка: с CPU или с памятью? Это важно, так как для каждого типа нагрузки подходы к оптимизации значительно отличаются. Например, чаще всего доступ к памяти осуществляется из кэша или основной памяти (в противоположность доступу к памяти удаленного хоста в технологии NUMA), и производительность в основном зависит от используемых структур данных и алгоритмов. Чтобы исследовать шаблоны доступа к памяти, применяемые в рабочей нагрузке, необходимо пронаблюдать за двумя счётчиками производительности, указанными в Таблица 3.

    Таблица 3. Счётчики утилизации памяти в архитектуре POWER7
    [​IMG]

    Эти два счётчика могут показать, из каких операций в основном состоит шаблон доступа к памяти: из чтения, записи или и чтения и записи одновременно. Воспользуемся уже знакомым бенчмарком 403.gcc из пакета SPECcpu2006 и провёдем профилирование нагрузки.

    Листинг 7. Вывод команды opreport –l для бенчмарк-компонента 403.gcc (выведены значения счётчиков PM_MEM0_RQ_DISP и PM_MEM0_WQ_DISP)
    Code:
    CPU: ppc64 POWER7, speed 3550 MHz (estimated) 
    Counted PM_MEM0_RQ_DISP_GRP59 events ((Group 59 pm_nest2)  Nest events (MC0/MC1/PB/GX), 
    Pair0 Bit1) with a unit mask of 0x00 (No unit mask) count 1000 
    Counted PM_MEM0_WQ_DISP_GRP59 events ((Group 59 pm_nest2)  Nest events (MC0/MC1/PB/GX), 
    Pair3 Bit1) with a unit mask of 0x00 (No unit mask) count 1000 
    samples  %        samples  %        app name                 symbol name 
    225841   25.8000  289       0.4086  gcc_base.none            reg_is_remote_constant_p.\
                                                                 isra.3.part.4 
    90068    10.2893  2183      3.0862  gcc_base.none            compute_transp 
    54038     6.1733  308       0.4354  gcc_base.none            single_set_2 
    32660     3.7311  2006      2.8359  gcc_base.none            delete_null_pointer_checks 
    26352     3.0104  1498      2.1178  gcc_base.none            note_stores 
    21306     2.4340  1950      2.7568  vmlinux                  .pseries_dedicated_idle_sl\
                                                                 eep 
    18059     2.0631  9186     12.9865  libc-2.13.so             memset 
    15867     1.8126  659       0.9316  gcc_base.none            init_alias_analysis
    Также полезным могут оказаться счётчики производительности, связанные с доступом к кэшу, точнее к обоим кэшам – L2 и L3. В примере, приведенном ниже, используется инструмент perf для профилирования компонента 483.xalancbmk, собранного с помощью компилятора RHEL6.2 Linux system GCC, из пакета SPECcpu2006 (см. раздел "Ресурсы"). Этот компонент интенсивно использует процедуры выделения памяти, так что можно ожидать значительной нагрузки на подсистему памяти. Чтобы проверить это, будем следить за счётчиками, перечисленными в Таблица 4, с помощью OProfile.


    Продолжение ->
     
    _________________________
  2. Suicide

    Suicide Super Moderator
    Staff Member

    Joined:
    24 Apr 2009
    Messages:
    2,482
    Likes Received:
    7,062
    Reputations:
    693
    Продолжение

    Таблица 4. Счётчики, измеряющие доступ к памяти/кэшу в архитектуре POWER7
    [​IMG]

    В Листинг 8 приведен результат профилирования.

    Листинг 8. Вывод команды opreport –l для бенчмарк-компонента 489.Xalancbmk (выведены значения счётчиков PM_DATA_FROM_L2_GRP91 и PM_DATA_FROM_L3_GRP91)
    Code:
    CPU: ppc64 POWER7, speed 3550 MHz (estimated) 
    Counted PM_DATA_FROM_L2_GRP91 events ((Group 91 pm_dsource1) The processor's Data Cache
    was reloaded from the local L2 due to a demand load.) with a unit mask of 0x00 (No unit
     mask) count 1000 
    Counted PM_DATA_FROM_L3_GRP91 events ((Group 91 pm_dsource1) The processor's Data Cache
     was reloaded from the local L3 due to a demand load.) with a unit mask of 0x00 (No unit
     mask) count 1000 
    samples  %        samples  %        image name     app name       symbol name 
    767827   25.5750  7581      0.2525  gcc_base.none  gcc_base.none  bitmap_element_allocate
    377138   12.5618  8341      0.2778  gcc_base.none  gcc_base.none  bitmap_operation 
    93334     3.1088  3160      0.1052  gcc_base.none  gcc_base.none  bitmap_bit_p 
    70278     2.3408  5913      0.1969  libc-2.13.so   libc-2.13.so   _int_free 
    56851     1.8936  22874     0.7618  oprofile       oprofile       /oprofile 
    47570     1.5845  2881      0.0959  gcc_base.none  gcc_base.none  rehash_using_reg 
    41441     1.3803  8532      0.2841  libc-2.13.so   libc-2.13.so   _int_malloc
    Листинг 9. Вывод команды opreport –l для бенчмарк-компонента 489.Xalancbmk (выведены значения счётчиков PM_DATA_FROM_LMEM_GRP91 и PM_DATA_FROM_RMEM_GRP91)
    Code:
    Counted PM_DATA_FROM_LMEM_GRP91 events ((Group 91 pm_dsource1) The processor's Data Cache
    was reloaded from memory attached to the same module this proccessor is located on.) with
     a unit mask of 0x00 (No unit mask) count 1000 
    Counted PM_DATA_FROM_RMEM_GRP91 events ((Group 91 pm_dsource1) The processor's Data Cache
     was reloaded from memory attached to a different module than this proccessor is located 
    on.) with a unit mask of 0x00 (No unit mask) count 1000
    samples  %        samples  %        image name     app name       symbol name 
    1605      0.3344  0              0  gcc_base.none  gcc_base.none  bitmap_element_allocate
    1778      0.3704  0              0  gcc_base.none  gcc_base.none  bitmap_operation 
    1231      0.2564  0              0  gcc_base.none  gcc_base.none  bitmap_bit_p 
    205       0.0427  0              0  libc-2.13.so   libc-2.13.so   _int_free 
    583       0.1215  327      100.000  oprofile       oprofile       /oprofile 
    0              0  0              0  gcc_base.none  gcc_base.none  rehash_using_reg 
    225       0.0469  0              0  libc-2.13.so   libc-2.13.so   _int_malloc
    Анализ результатов профилирования показывает, что основная нагрузка на кэш процессора связана с доступом из кэша L2, в то время как кэш L3 практически не требует перезагрузки. Общее и относительно значение счётчика, отслеживающего доступ к кэшу L2 (PM_DATA_FROM_L2) значительно выше, чем количество запросов на перезагрузку со стороны кэша L3 (PM_DATA_FROM_L2). Можно продолжить наблюдение за другими счётчиками, чтобы получить дополнительную информацию и узнать, например, не вызывает ли доступ к кэшу L2 "остановов" в работе процессора из-за непредвиденных обращений к кэшу. По результатам профилирования данного примера можно сделать заключение, что доступ к основной памяти (событие PM_DATA_FROM_LMEM) довольно мал по сравнению с доступом к кэшу, а также то, что удалённый доступ к памяти по технологии NUMA (событие PM_DATA_FROM_RMEM) отсутствует. Анализ «горячих точек» программы и шаблонов доступа к памяти может указать направления, куда следует приложить усилия по оптимизации. В данном случае требуется дальнейший анализ, чтобы определить, что конкретно вызывает "остановы" CPU, так как идентификации "горячих точек" и шаблонов доступа к памяти, присутствующих в рабочей нагрузке недостаточно для точного определения причины "простаивания" CPU.

    Чтобы выработать лучшую стратегию для оптимизации производительности, дальнейший анализ потребует использования инструмента perf, а не OProfile, так как многие CBM-счётчики в архитектуре POWER7 должны наблюдаться одновременно (например, 22 счётчика, представленные на Рисунок 3). Многие из этих событий находятся в различных группах, поэтому использование OProfile для наблюдения за ними потребует нескольких запусков рабочей нагрузки. Инструмент perf может одновременно наблюдать за аппаратными счётчиками, даже если они находятся в нескольких группах. Хотя это и снижает точность, но общие результаты обоих подходов в целом совпадают, кроме того, использование perf позволяет сократить количество времени необходимого на выполнение профилирования.

    В следующем примере инструмент perf используется для профилирования компонента 483.xalancbmk из пакета SPECcpu2006. Чтобы запустить процесс профилирования, воспользуйтесь командой из Листинг 10.

    Листинг 10. Запуск команды perf для наблюдения за CBM-счётчиками POWER7
    Code:
    $ /usr/bin/perf stat -C 0 -e r100f2,r4001a,r100f8,r4001c,r2001a,r200f4,r2004a,r4004a,
    r4004e,r4004c,r20016,r40018,r20012,r40016,r40012,r20018,r4000a,r2001c,r1001c,r20014,
    r40014,r30004 taskset -c 0 ./Xalan_base.none -v t5.xml xalanc.xsl > power7_cbm.dat
    Эта команда заставляет perf наблюдать за оригинальными событиями, определенными в опции -e, на процессоре, определенном в опции -с. Системный вызов taskset гарантирует, что компонент будет работать исключительно на процессоре с номером 0. Файл с рабочей нагрузкой ./Xalan_base.none -v t5.xml xalanc.xsl можно заменить на другое приложение, которое требуется подвергнуть профилированию. После того как профилирование будет завершено, команда perf выведет таблицу с общим количеством срабатываний каждого отслеживаемого события и количеством секунд, потраченных на выполнение работы.

    Листинг 11. Вывод команды 'perf stat' для бенчмарк-компонента 489.Xalancbmk
    Code:
    Performance counter stats for 'taskset -c 0 ./Xalan_base.none -v t5.xml xalanc.xsl': 
    
    
       366,860,486,404 r100f2                                                       [18.15%] 
         8,090,500,758 r4001a                                                       [13.65%] 
        50,655,176,004 r100f8                                                       [ 9.13%] 
        11,358,043,420 r4001c                                                       [ 9.11%] 
        10,318,533,758 r2001a                                                       [13.68%] 
     1,301,183,175,870 r200f4                                                       [18.22%] 
         2,150,935,303 r2004a                                                       [ 9.10%] 
                     0 r4004a                                                       [13.65%] 
       211,224,577,427 r4004e                                                       [ 4.54%] 
       212,033,138,844 r4004c                                                       [ 4.54%] 
       264,721,636,705 r20016                                                       [ 9.09%] 
        22,176,093,590 r40018                                                       [ 9.11%] 
       510,728,741,936 r20012                                                       [ 9.10%] 
        39,823,575,049 r40016                                                       [ 9.07%] 
         7,219,335,816 r40012                                                       [ 4.54%] 
             1,585,358 r20018                                                       [ 9.08%] 
       882,639,601,431 r4000a                                                       [ 9.08%] 
         1,219,039,175 r2001c                                                       [ 9.08%] 
             3,107,304 r1001c                                                       [13.62%] 
       120,319,547,023 r20014                                                       [ 9.09%] 
        50,684,413,751 r40014                                                       [13.62%] 
       366,940,826,307 r30004                                                       [18.16%] 
    
         461.057870036 seconds time elapsed 
    Для анализа вывода инструмента perf для CBM-модели для POWER7 можно воспользоваться сценарием на Python, который можно найти в архиве power7_cbm.zip в разделе "Материалы для скачивания", объединяющем метрики, собранные с виртуальных и аппаратных счётчиков. Для создания отчёта воспользуйтесь командной приведенной в Листинг 12.

    Листинг 12. Вызов python-сценария для создания отчёта по счётчикам CBM-модели POWER7
    Code:
    $ power7_cbm.py power7_cbm.dat
    
    В результате будет выведена информация, аналогичная приведенной в Листинг 13.

    Листинг 13. Вывод сценария power7_cbm.py для бенчмарк-компонента 489.Xalancbmk
    Code:
    CPI Breakdown Model (Complete) 
    
    Metric                         :            Value :    Percent 
    PM_CMPLU_STALL_DIV             :    49802421337.0 :        0.0 
    PM_CMPLU_STALL_FXU_OTHER       :    67578558649.0 :        5.2 
    PM_CMPLU_STALL_SCALAR_LONG     :        2011413.0 :        0.0 
    PM_CMPLU_STALL_SCALAR_OTHER    :     7195240404.0 :        0.6 
    PM_CMPLU_STALL_VECTOR_LONG     :              0.0 :        0.0 
    PM_CMPLU_STALL_VECTOR_OTHER    :     1209603592.0 :        0.1 
    PM_CMPLU_STALL_ERAT_MISS       :    22193968056.0 :        1.7 
    PM_CMPLU_STALL_REJECT_OTHER    :    18190293594.0 :        1.4 
    PM_CMPLU_STALL_DCACHE_MISS     :   261865838255.0 :       20.3 
    PM_CMPLU_STALL_STORE           :     2001544985.0 :        0.2 
    PM_CMPLU_STALL_LSU_OTHER       :   202313206181.0 :       15.7 
    PM_CMPLU_STALL_THRD            :        2025705.0 :        0.0 
    PM_CMPLU_STALL_BRU             :   208356542821.0 :       16.2 
    PM_CMPLU_STALL_IFU_OTHER       :     2171796336.0 :        0.2 
    PM_CMPLU_STALL_OTHER           :    30895294057.0 :        2.4 
    PM_GCT_NOSLOT_IC_MISS          :     9805421042.0 :        0.8 
    PM_GCT_NOSLOT_BR_MPRED         :     7823508357.0 :        0.6 
    PM_GCT_NOSLOT_BR_MPRED_IC_MISS :    11059314150.0 :        0.9 
    PM_GCT_EMPTY_OTHER             :    20292049774.0 :        1.6 
    PM_1PLUS_PPC_CMPL              :   365158978504.0 :       28.3 
    OVERHEAD_EXPANSION             :      590057044.0 :        0.0 
    Total                                             :       96.1
    Этот отчёт основан на статистических значениях с учетом возможной погрешности, так что окончательные процентные значения не являются точными на 100%. Но даже с учетом высокой погрешности, около 20% задержек в работе CPU связанно с ошибочными обращениями к кэшу данных (PM_CMPLU_STALL_DCACHE_MISS). Окончательный процент выполнения инструкций (PM_1PLUS_PPC_CMPL) составляет около 28%.

    В будущем для оптимизации стоит постараться увеличить это значение, уменьшив процент количества остановов CPU и/или GCT (Global Completion Table — глобальной таблицы исполнения). На базе анализа этого отчёта также можно найти в коде программы места, где происходят остановы CPU. Для этого можно воспользоваться командой perf record, отслеживающую с помощью счётчика "необработанную" производительность и создающую карту выполнения процесса, которая позволяет идентифицировать, какой символ процесса сгенерировал больше всего аппаратных событий. Это похоже на то, как работает OProfile. В Листинг 14 приведена команда для отслеживания событий типа PM_CMPLU_STALL_DCACHE_MISS.

    Листинг 14. Команда perf record для отслеживания события PM_CMPLU_STALL_DCACHE_MISS
    Code:
    $ /usr/bin/perf record -C 0 -e r20016 taskset -c 0 ./Xalan_base.none -v t5.xml xalanc.xsl
    Команда perf обычно создаёт файл с результатами (который обычно называется "perf.dat"). Его можно считать в интерактивном режиме с помощью команды perf report, как показано в Листинг 15.

    Листинг 15. Вывод команды perf report для бенчмарк-компонента 489.Xalancbmk
    Code:
    Events: 192  raw 0x20016
        39.58%  Xalan_base.none  Xalan_base.none  [.] xercesc_2_5::ValueStore::contains 
        11.46%  Xalan_base.none  Xalan_base.none  [.] xalanc_1_8::XStringCachedAllocator
         9.90%  Xalan_base.none  Xalan_base.none  [.] xalanc_1_8::XStringCachedAllocator
         7.29%  Xalan_base.none  Xalan_base.none  [.] xercesc_2_5::ValueStore::isDuplica
         5.21%  Xalan_base.none  libc-2.13.so     [.] _int_malloc 
         5.21%  Xalan_base.none  Xalan_base.none  [.] __gnu_cxx::__normal_iterator<xa
         4.17%  Xalan_base.none  libc-2.13.so     [.] __GI___libc_malloc 
         2.08%  Xalan_base.none  libc-2.13.so     [.] malloc_consolidate.part.4 
         1.56%  Xalan_base.none  Xalan_base.none  [.] xalanc_1_8::ReusableArenaBlock<xa
         1.56%  Xalan_base.none  Xalan_base.none  [.] xalanc_1_8::ReusableArenaBlock<xa
         1.04%  Xalan_base.none  libc-2.13.so     [.] __free
    [...]
    
    Данное исследование, проведенное с помощью счётчика CBM-модели POWER7 и отчёта инструмента perf, показывает, что усилия можно сконцентрировать на оптимизации доступа к памяти и кэшу в символе xercesc_2_5::ValueStore::contains(xercesc_2_5::FieldValueMap const*).

    Это пример демонстрирует только один из возможных подходов к выполнению анализа. CBM-модель POWER7 показывает, что хотя задержки при обращении к кешу являются причиной большей части остановов CPU, модуль загрузки и хранения (PM_CMPLU_STALL_LSU) и модуль ветвления (PM_CMPLU_STALL_BRU) также тормозят работу CPU. Поэтому дальнейшие анализ может быть направлен на изучение этих счётчиков.

    Практический пример

    В следующем примере мы на практике применим все эти стратегии для оценки производительности реализации тригонометрической функции. Основываясь на результатах анализа, мы сможем выделить направления для оптимизации. В данном примере используется функция hypot из стандарта ISO С, вычисляющая длину гипотенузы прямоугольного треугольника. В стандарте C99, POSIX.1-2001 эта функция определена как:
    Code:
    double hypot(double x, double y);

    Функция hypot() возвращает sqrt(x*x+y*y). В случае успешного вызова функция возвращает длину гипотенузы прямоугольного треугольника со сторонами длиной x и y. Если x или y равны бесконечности, то возвращается бесконечность (Infinity) со знаком плюс. Если один из аргументов не является числом (NaN – not a number), а другой аргумент не равен бесконечности, то возвращается NaN-значение. Если величина результата превышает размерность типа данных, то произойдет ошибка диапазона числа, и функция возвращает одно из возможных значений: HUGE_VAL, HUGE_VALF или HUGE_VALL. Если оба аргумента являются экспоненциальными величинами и результат тоже будет экспоненциальным, то произойдет ошибка диапазона числа, и будет возвращено правильное значение.

    Хотя этот алгоритм кажется простым, но обработка аргументов с плавающей точкой (floating point) в случае с Infinity или NaN-значениями или превышение размерности / потеря разрядности, связанные с действиями над значениями с плавающей точкой, оказывают определенное влияние на производительность. В библиотеке GNU C (см. ссылку в разделе "Ресурсы") приведена реализация функции на языке C, которую можно найти в дереве исходного кода в файле sysdeps/ieee754/dbl-64/e_hypot.c.

    Примечание: Информация о лицензии для данного фрагмента кода приведена в приложении.

    Листинг 16. Стандартная реализация функции hypot() в GLIBC
    double __ieee754_hypot(double x, double y)

    Code:
    { 
            double a,b,t1,t2,y1,y2,w; 
            int32_t j,k,ha,hb; 
    
            GET_HIGH_WORD(ha,x); 
            ha &= 0x7fffffff; 
            GET_HIGH_WORD(hb,y); 
            hb &= 0x7fffffff; 
            if(hb > ha) {a=y;b=x;j=ha; ha=hb;hb=j;} else {a=x;b=y;} 
            SET_HIGH_WORD(a,ha);    /* a <- |a| */ 
            SET_HIGH_WORD(b,hb);    /* b <- |b| */ 
            if((ha-hb)>0x3c00000) {return a+b;} /* x/y > 2**60 */ 
            k=0; 
            if(ha > 0x5f300000) {   /* a>2**500 */ 
               if(ha >= 0x7ff00000) {       /* Inf or NaN */ 
                   u_int32_t low; 
                   w = a+b;                 /* for sNaN */ 
                   GET_LOW_WORD(low,a); 
                   if(((ha&0xfffff)|low)==0) w = a; 
                   GET_LOW_WORD(low,b); 
                   if(((hb^0x7ff00000)|low)==0) w = b; 
                   return w; 
               } 
               /* scale a and b by 2**-600 */ 
               ha -= 0x25800000; hb -= 0x25800000;  k += 600; 
               SET_HIGH_WORD(a,ha); 
               SET_HIGH_WORD(b,hb); 
            } 
            if(hb < 0x20b00000) {   /* b < 2**-500 */ 
                if(hb <= 0x000fffff) {      /* subnormal b or 0 */ 
                    u_int32_t low; 
                    GET_LOW_WORD(low,b); 
                    if((hb|low)==0) return a; 
                    t1=0; 
                    SET_HIGH_WORD(t1,0x7fd00000);   /* t1=2^1022 */ 
                    b *= t1; 
                    a *= t1; 
                    k -= 1022; 
                } else {            /* scale a and b by 2^600 */ 
                    ha += 0x25800000;       /* a *= 2^600 */ 
                    hb += 0x25800000;       /* b *= 2^600 */ 
                    k -= 600; 
                    SET_HIGH_WORD(a,ha); 
                    SET_HIGH_WORD(b,hb); 
                } 
            } 
        /* medium size a and b */ 
            w = a-b; 
            if (w>b) { 
                t1 = 0; 
                SET_HIGH_WORD(t1,ha); 
                t2 = a-t1; 
                w  = __ieee754_sqrt(t1*t1-(b*(-b)-t2*(a+t1))); 
            } else { 
                a  = a+a; 
                y1 = 0; 
                SET_HIGH_WORD(y1,hb); 
                y2 = b - y1; 
                t1 = 0; 
                SET_HIGH_WORD(t1,ha+0x00100000); 
                t2 = a - t1; 
                w  = __ieee754_sqrt(t1*y1-(w*(-w)-(t1*y2+t2*b))); 
            } 
            if(k!=0) { 
                u_int32_t high; 
                t1 = 1.0; 
                GET_HIGH_WORD(high,t1); 
                SET_HIGH_WORD(t1,high+(k<<20)); 
                return t1*w; 
            } else return w; 
    } 
    Представленная реализация довольно сложна в основном из-за того, что алгоритм выполняет много побитовых преобразований значений с плавающей точкой в целочисленные значения. Считается, что определенные FP-операции требуют больше ресурсов при использовании инструкций с плавающей точкой, чем при использовании ресурсов с фиксированной точкой. Это справедливо для некоторых архитектур, но не для POWER .

    Продолжение ->
     
    _________________________
    #2 Suicide, 18 Jan 2013
    Last edited: 18 Jan 2013
  3. Suicide

    Suicide Super Moderator
    Staff Member

    Joined:
    24 Apr 2009
    Messages:
    2,482
    Likes Received:
    7,062
    Reputations:
    693
    Продолжение



    Первым шагом в оценке данной реализации должно стать создание бенчмарк-программы, которая и будет подвергнута профилированию. В данном случае, так как мы работаем с обычной функцией от двух аргументов и простым алгоритмом (без внутренних вызовов других функций или дополнительных ветвей исполнения) можно создать простую бенчмарк-программу, которая находится в файле hypot_bench.tar.gz в разделе "Материалы для скачивания". Создание бенчмарк-программы – это один из этапов оценки производительности; предложенные оптимизации должны ускорить алгоритмы или важнейшие участки кода, что приведет к улучшению общей производительности всей рабочей нагрузки. Синтетические (т.е. искусственные) бенчмарк-программы, как наш пример, должны воспроизводить обычный сценарий использования данной функции. Так оптимизация требует определенных ресурсов и времени, то необходимо сфокусироваться на наиболее часто встречающемся применении или ожидаемом поведении функции. Попытка оптимизации кода, который редко использует возможности программы, будет пустой тратой ресурсов.

    Так как данный анализ производительности выполняется только для одной функции, то можно пропустить этап выявления "горячих точек" программы и сфокусироваться на анализе CBM-модели. Воспользуемся бенчмарк-программой hypot_bench.c вместе с утилитой perf, чтобы получить информацию о CBM-модели, приведенную в Листинг 17.

    Листинг 17. Вывод сценария power7_cbm.py для бенчмарк-программы hypot
    Code:
    CPI Breakdown Model (Complete) 
    
    Metric                         :            Value :    Percent 
    PM_CMPLU_STALL_DIV             :        8921688.0 :        8.7 
    PM_CMPLU_STALL_FXU_OTHER       :    13953382275.0 :        5.0 
    PM_CMPLU_STALL_SCALAR_LONG     :    24380128688.0 :        8.7 
    PM_CMPLU_STALL_SCALAR_OTHER    :    33862492798.0 :       12.0 
    PM_CMPLU_STALL_VECTOR_LONG     :              0.0 :        0.0 
    PM_CMPLU_STALL_VECTOR_OTHER    :      275057010.0 :        0.1 
    PM_CMPLU_STALL_ERAT_MISS       :         173439.0 :        0.0 
    PM_CMPLU_STALL_REJECT_OTHER    :         902838.0 :        0.0 
    PM_CMPLU_STALL_DCACHE_MISS     :       15200163.0 :        0.0 
    PM_CMPLU_STALL_STORE           :        1837414.0 :        0.0 
    PM_CMPLU_STALL_LSU_OTHER       :    94866270200.0 :       33.7 
    PM_CMPLU_STALL_THRD            :         569036.0 :        0.0 
    PM_CMPLU_STALL_BRU             :    10470012464.0 :        3.7 
    PM_CMPLU_STALL_IFU_OTHER       :      -73357562.0 :        0.0 
    PM_CMPLU_STALL_OTHER           :     7140295432.0 :        2.5 
    PM_GCT_NOSLOT_IC_MISS          :        3586554.0 :        0.0 
    PM_GCT_NOSLOT_BR_MPRED         :     1008950510.0 :        0.4 
    PM_GCT_NOSLOT_BR_MPRED_IC_MISS :         795943.0 :        0.0 
    PM_GCT_EMPTY_OTHER             :    42488384303.0 :       15.1 
    PM_1PLUS_PPC_CMPL              :    53138626513.0 :       18.9 
    OVERHEAD_EXPANSION             :       30852715.0 :        0.0 
    Total                                             :      108.7
    Результаты профилирования показывают, что большинство остановов CPU и, следовательно, потерь производительности связаны со счётчиком PM_CMPLU_STALL_LSU_OTHER для модуля LSU (Load and Store Unit – модуль загрузки и хранения). Хотя к LSU-модулю относится несколько счётчиков, но во время анализа причин замедления CPU мы исследуем только те счётчики, которые связаны с потерями производительности. Те из них, которые демонстрируют снижение производительности на платформе POWER, связаны с LHS-ошибками (Load-Hit-Store). Это существенная задержка, возникающая, когда CPU записывает данные по определённому адресу и практически сразу же после этого пытается загрузить их обратно. На следующем этапе необходимо убедиться, действительно ли эта ошибка присутствует в нашем алгоритме, для чего следует проверить событие PM_LSU_REJECT_LHS (его оригинальный код - "rc8ac"), как показано в Листинг 18.

    Листинг 18. Отчёт команды perf record по событию PM_LSU_REJECT_LHS
    Code:
    $ perf record -C 0 -e rc8ac taskset -c 0 ./hypot_bench_glibc
    $ perf report
    Events: 14K raw 0xc8ac
        79.19%  hypot_bench_gli  libm-2.12.so       [.] __ieee754_hypot
        10.38%  hypot_bench_gli  libm-2.12.so       [.] __hypot
         6.34%  hypot_bench_gli  libm-2.12.so       [.] __GI___finite
    По результатам профилирования видно, что символ __ieee754_hypot генерирует больше всего событий PM_LSU_REJECT_LHS. Исследуем код сборки, подготовленный компилятором, чтобы определить какие инструкции приводят к возникновению события. Развернём символ __ieee754_hypot для более подробного анализа и просмотрим отчёт утилиты perf, чтобы найти информацию по символу __ieee754_hypot, которая показана в Листинг 19.

    Листинг 19. Отчёт команды perf record по событию PM_LSU_REJECT_LHS
    Code:
         :        00000080fc38b730 <.__ieee754_hypot>:
        0.00 :          80fc38b730:   7c 08 02 a6     mflr    r0
        0.00 :          80fc38b734:   fb c1 ff f0     std     r30,-16(r1)
        0.00 :          80fc38b738:   fb e1 ff f8     std     r31,-8(r1)
       13.62 :          80fc38b73c:   f8 01 00 10     std     r0,16(r1)
        0.00 :          80fc38b740:   f8 21 ff 71     stdu    r1,-144(r1)
       10.82 :          80fc38b744:   d8 21 00 70     stfd    f1,112(r1)
        0.23 :          80fc38b748:   e9 21 00 70     ld      r9,112(r1)
       17.54 :          80fc38b74c:   d8 41 00 70     stfd    f2,112(r1)
        0.00 :          80fc38b750:   79 29 00 62     rldicl  r9,r9,32,33
        0.00 :          80fc38b754:   e9 61 00 70     ld      r11,112(r1)
        0.00 :          80fc38b758:   e8 01 00 70     ld      r0,112(r1)
        8.46 :          80fc38b75c:   d8 21 00 70     stfd    f1,112(r1)
    [...]
    Ранее в исходном коде для реализации функции был использован макрос GET_HIGH_WORD, преобразующий значение с запятой в целочисленное значение для последующих побитовых операций. В заголовочном классе GLIBC math/math_private.h этот макрос определен, как показано в Листинг 20.

    Листинг 20. Определение макроса GET_HIGH_WORD
    Code:
    #define GET_HIGH_WORD(i,d)                                      \
    do {                                                            \
      ieee_double_shape_type gh_u;                                  \
      gh_u.value = (d);                                             \
      (i) = gh_u.parts.msw;                                         \
    } while (0)
    Возможным источником проблемы, из-за которой возникает событие LHS, и, следовательно, простой процессора, является операция в Листинг 20, которая сохраняет атрибуты значения с плавающей точкой во внутреннюю переменную value, а затем загружающая их в переменную i. В процессоре POWER7 отсутствует встроенная инструкция для побитового перемещения значения из регистра с плавающей точкой в регистр с фиксированной точкой. Подобное действие на архитектуре POWER выполняется путём сохранения числа с плавающей точкой из регистра с плавающей точкой в область памяти с помощью операции сохранения (store), а затем загрузка этой области памяти в универсальный регистр с фиксированной точкой. Так как доступ к памяти выполняется медленнее, чем операции с регистрами (даже при обращении к данным в кэше L1), то CPU "простаивает" в ожидании пока завершится загрузка, следующая за сохранением.

    Примечание: Подробную информацию по этому вопросу можно найти в документе "POWER ISA 2.06 (POWER7)" (см. раздел "Ресурсы").

    Довольно часто события, порождённые счётчиками производительности, вызывают прерывания, которые сохраняют PC-адрес инструкции, которая находится рядом с исполняющимися инструкциями. Это может привести к недостаточно точному аннотированию сборки. Чтобы устранить этот недостаток в архитектуре POWER начиная с четвертого поколения появился фиксированный набор счётчиков производительности, которые были названы "помеченными" (англ. marked). Такие инструкции генерируют меньше событий за тот же промежуток времени, поэтому индикация PC-инструкции будет точной, что приведет к более точному аннотированию сборки. В списке счётчиков Oprofile, который можно получить командой opcontrol -l, "помеченные" события можно идентифицировать по префиксу PM_MRK.

    Чтобы ещё раз проверить результаты анализа, пронаблюдаем за счётчиком PM_MRK_LSU_REJECT_LHS. Оба счётчика PM_MRK_LSU_REJECT_LHS and PM_LSU_REJECT_LHS отслеживают одно и тоже событие. Однако "помеченный" счётчик (PM_MRK_LSU_REJECT_LHS) будет генерировать меньше событий за тот же период времени, но в результате сборка будет аннотирована более точно (см. Листинг 21).

    Листинг 21. Запуск команды perf для отслеживания события PM_MRK_LSU_REJECT_LHS для архитектуры POWER7
    Code:
    $ perf record -C 0 -e rd082 taskset -c 0 ./hypot_bench_glibc
    $ perf report
    Events: 256K raw 0xd082
        64.61%  hypot_bench_gli  libm-2.12.so       [.] __ieee754_hypot
        35.33%  hypot_bench_gli  libm-2.12.so       [.] __GI___finite
    
    В результате профилирования будет получен результат, приведенный в Листинг 22.

    Листинг 22. Отчёт команды perf по событию PM_MRK_LSU_REJECT_LHS для архитектуры POWER7
    Code:
            :        00000080fc38b730 <.__ieee754_hypot>:
    [...]
        1.23 :          80fc38b7a8:   c9 a1 00 70     lfd     f13,112(r1)
        0.00 :          80fc38b7ac:   f8 01 00 70     std     r0,112(r1)
       32.66 :          80fc38b7b0:   c8 01 00 70     lfd     f0,112(r1)
    [...]
        0.00 :          80fc38b954:   f8 01 00 70     std     r0,112(r1)
        0.00 :          80fc38b958:   e8 0b 00 00     ld      r0,0(r11)
        0.00 :          80fc38b95c:   79 00 00 0e     rldimi  r0,r8,32,0
       61.72 :          80fc38b960:   c9 61 00 70     lfd     f11,112(r1
    [...]
    Как видно, более 35% тех же самых событий приходится уже на другой символ, указанный в Листинг 23.

    Листинг 23. Подробная информация из отчёта инструмента perf
    Code:
       :        00000080fc3a2610 <.__finitel>>
        0.00 :          80fc3a2610:   d8 21 ff f0     stfd    f1,-16(r1)
      100.00 :          80fc3a2614:   e8 01 ff f0     ld      r0,-16(r1)
    
    Основываясь на полученной информации, оптимизацию следует сконцентрировать на устранении задержек, вызванных преобразованием значений с плавающей точкой в целочисленные значения. POWER-процессор обладает быстрым и эффективным модулем для исполнения инструкций с плавающей точкой, поэтому нет необходимости выполнять подобные вычисления в целочисленном режиме. Из текущей реализации алгоритма данной функции, который используется в библиотеке GLIBC (файл sysdeps/powerpc/fpu/e_hypot.c) для платформы POWER, все задержки, связанные с событием LHS, были устранены благодаря полному переходу на FP-операции. В результате был получен более простой алгоритм, приведенный в Листинг 24.

    Листинг 24. Исходный код функции hypot() из библиотеки GLIBC для платформы PowerPC
    Code:
    double
    __ieee754_hypot (double x, double y)
    {
      x = fabs (x);
      y = fabs (y);
    
      TEST_INF_NAN (x, y);
    
      if (y > x)
        {
          double t = x;
          x = y;
          y = t;
        }
      if (y == 0.0 || (x / y) > two60)
        {
          return x + y;
        }
      if (x > two500)
        {
          x *= twoM600;
          y *= twoM600;
          return __ieee754_sqrt (x * x + y * y) / twoM600;
        }
      if (y < twoM500)
        {
          if (y <= pdnum)
            {
              x *= two1022;
              y *= two1022;
              return __ieee754_sqrt (x * x + y * y) / two1022;
            }
          else
            {
              x *= two600;
              y *= two600;
              return __ieee754_sqrt (x * x + y * y) / two600;
            }
        }
      return __ieee754_sqrt (x * x + y * y);
    }
    Макрос TEST_INF_NAN - это ещё одна небольшая оптимизация, проверяющая, что число не является NaN или INFINITY перед выполнением последующих FP-операций. Это необходимо, так как действия с NaN или INFINITY значениями могут привести к возникновению FP-исключений, и поэтому спецификация функции не допускает подобных значений. На платформе POWER7 функции isinf и isnan были оптимизированы компилятором для FP-инструкций так, чтобы не выполнять лишних вызовов других функций, в то время как на процессорах POWER6 и более ранних эти действия генерируют вызовы к соответствующим функциям. В оптимизированной версии используется примерно такая же реализация, но она построена на inline-функциях, а не на вызовах полноценных функций.

    В конце сравним обе реализации, выполнив следующий простой тест. Выполним компиляцию GLIBC с новым алгоритмом и без него, и сравнив общее время выполнения для каждой бенчмарк-программы. В Листинг 25 приведены результаты запуска стандартной реализации GLIBC.

    Листинг 25. Бенчмарк-программа для стандартной реализации функции hypot() из пакета GLIBC
    Code:
    $ /usr/bin/time ./hypot_bench_glibc
    INF_CASE       : elapsed time: 14:994339 
    NAN_CASE       : elapsed time: 14:707085 
    TWO60_CASE     : elapsed time: 12:983906 
    TWO500_CASE    : elapsed time: 10:589746 
    TWOM500_CASE   : elapsed time: 11:215079 
    NORMAL_CASE    : elapsed time: 15:325237 
    79.80user 0.01system 1:19.81elapsed 99%CPU (0avgtext+0avgdata 151552maxresident)k 
    0inputs+0outputs (0major+48minor)pagefaults 0swaps
    В Листинг 26 приведены результаты для оптимизированной версии.

    Листинг 26. Бенчмарк-программа для усовершенствованной реализации функции hypot() из пакета GLIBC
    Code:
    $ /usr/bin/time ./hypot_bench_glibc 
    INF_CASE       : elapsed time: 4:667043 
    NAN_CASE       : elapsed time: 5:100940 
    TWO60_CASE     : elapsed time: 6:245313 
    TWO500_CASE    : elapsed time: 4:838627 
    TWOM500_CASE   : elapsed time: 8:946053 
    NORMAL_CASE    : elapsed time: 6:245218 
    36.03user 0.00system 0:36.04elapsed 99%CPU (0avgtext+0avgdata 163840maxresident)k 
    0inputs+0outputs (0major+50minor)pagefaults 0swaps 
    Окончательное улучшение производительности составило более 100%, а время исполнения бенчмарк-программы для оптимизированной версии сократилось вдвое.

    Заключение

    Оценка производительности путём профилирования аппаратных счётчиков – это мощный инструмент для понимания того, как рабочая нагрузка ведёт себя на конкретном процессоре и в каком месте следует приложить усилия для оптимизации производительности. В новейшем процессоре POWER7 для использования доступны сотни счётчиков производительности, так что мы представили только самую простую модель для поиска зависимостирабочей нагрузки от простоев CPU. Понимание CBM-модели POWER7 иногда может оказаться затруднительным, так что мы также рассказали о Linux-инструментах, способных упростить этот процесс. Стратегии оценки производительности фокусируются на том, как искать "горячие точки" в программе, как понять шаблон доступа к памяти, используемый в приложении и как использовать CBM-модель POWER7. В конце мы использовали недавнюю оптимизацию, выполненную для тригонометрической функции hypot() в пакете GLIBC, чтобы пояснить анализ производительности, который и привёл к появлению оптимизированной версии кода.

    Приложение

    Настоящим предоставляется разрешение копировать, распространять и/или изменять этот документ в рамках лицензии GNU Free Documentation License, Version 1.3. Копия лицензии доступна по данному URL-адресу.

    Загрузка

    Бенчмарк-программа для функции hypot hypot_bench.tar.gz 6KB HTTP

    Сценарий для форматирования результатов perf power7_cbm.zip 2KB HTTP
     
    _________________________
    #3 Suicide, 18 Jan 2013
    Last edited: 18 Jan 2013
Loading...