[Алгоритмы, C] Корни разные нужны, корни разные важны

Автор Сообщение
news_bot ®

Стаж: 6 лет 2 месяца
Сообщений: 27286

Создавать темы news_bot ® написал(а)
14-Июн-2021 18:32

Вместо вступленияПрежде всего хочется выразить признательность всем, кто откликнулся на первую статью об оптимизации кода на языке C/C++ на примере функции для вычисления квадратного корня из целого с округлением до ближайшего целого. Благодаря экспертному вниманию была исправлена опечатка в тексте; копилка эффективных алгоритмов пополнилась.Интересен алгоритм sqrxi32 от @Sdima1357 — Пример 1, далее для краткости именуемый как «_i32». Алгоритм «_i32» безусловно выполняет главное условие задачи — «округление до ближайшего целого» — на всём множестве значений аргумента [ 0 .. 0xFFFFFFFF ], при этом показывает высокую производительность. Пример 1: Вычисление квадратного корня из целого с округлением до ближайшего целого.
uint16_t sqrxi32( uint32_t y )
{
  if ( y == 1 )
    return 1;
  uint32_t xh = y > 0x10000ul ? 0x10000ul : y;
  uint32_t xl = 0;
  uint32_t xc;
  for ( int k = 0; k < 16; k++ )
  {
    xc = ( xh + xl ) >> 1ul;
    if ( xc * xc - xc >= y )
    {
      xh = xc;
    }
    else
    {
      xl = xc;
    }
  }
  return ( xh + xl ) >> 1ul;
}
Другое хорошее качество алгоритма «_i32» — предсказуемость по времени. Время работы «_i32» постоянно в отличие от алгоритма «_evn», потребляющего машинное время пропорционально модулю аргумента.О чём этот текстНаблюдение комплексного влияния параметров сборки и целевой аппаратной платформы на итоговую производительность, применительно к одному и тому же исходному коду.Исходный код содержит содержит решение одной задачи разными алгоритмами.Анализ результатов наблюдений за рамками настоящей публикации.Условия и допускиДля сокращение текста принимаем:
  • аппаратных платформ для тестов — 3 платформы;
  • вариантов оптимизации сборки — 3 значения
Для сборки двоичного кода применяем:
  • Одну единицу компиляции теста (файл main.c)
  • Компиляцию в моно-поточный исполняемый файл
  • Единую сборочную среду: CubeIDE (она же Eclipce CDT)
  • Стандартные настройки профиля сборки RELEASE в среде CubeIDE
  • Единый диалект компилятора: «ISO C11 + gnu extensions» (-std=gnu11)
  • Применительно к микроконтроллерам:
    • CubeMX — default settings, +48MHz, +USART1, +HAL;
    • Runtime lib: Reduced C ( --spec=nano.specs );
    • Use float with printf from new lib nano ( -u _printf_float );
Таблица 1: Варианты сборки исполняемого кода
Таблица 2: Общие характеристики аппаратных платформ
Таблица 3: Технические характеристики аппаратных платформ
Тестовый набор содержит некоторые алгоритмы из предыдущей статьи и комментариев к ней.Для оценки FPU платформы «M4» в тестовый набор добавлена функция sqrt_fps, решающая вычислительную задачу с применением коротких действительных (float), именуемая далее «_fps» (Float Point Short) — Пример 2. Пример 2: Квадратный корень из целого с точностью float
uint16_t sqrt_fps( uint32_t number )
{
  if ( number < 2 )
    return (uint16_t) number;
  float f_rslt = sqrtf( number );
  uint32_t rslt = (uint32_t) f_rslt;
  if ( !( f_rslt - (float) rslt < .5 ) )
    rslt++;
  return (uint16_t) rslt;
}
Функция «_fps» работает без ошибок с аргументом менее 22-х бит, что соответствует десятичному порядку 1+E5 — Иллюстрация 1.Иллюстрация 1: Ошибки функции "_fps" на порядках 1+E6+
Для всех наблюдаемых алгоритмов ограничиваем диапазон аргумента множеством значений
[0 .. 1+E5]. Таблица 4: Список наблюдаемых алгоритмов
Основная единица измерения — производительность, среднее число циклических вызовов функции за одну миллисекунду.Относительная производительность платформОжидаемо, производительность платформы «x86» выше производительности платформы «ARM Cortex» безотносительно характера оптимизации сборки. Последнее демонстрирует левая часть графика — Иллюстрация 2.Иллюстрация 2: Относительная производительность аппаратных платформ
На левой части графика по оси Y отображается среднее время последовательного выполнения всех тестов (Таблица 4), измеренное в секундах. На оси X — аппаратные платформы.Чем ниже высота столбика на левой части графика (Иллюстрация 2), тем выше скорость работы, тем лучше производительность соответствующей аппаратной платформы.График каждой платформы, в свою очередь, представлен тремя столбцами, демонстрирующими зависимость производительности от варианта оптимизации сборки: -O0, -Os, -O3.Правая часть графика (Иллюстрация 2) показывает относительный прирост производительности у каждой аппаратной платформы в зависимости от варианта оптимизации сборки: -O0, -Os, -O3.Производительность 100% демонстрирует двоичный код, собранный без оптимизации ( -O0 ). Это базовая производительность платформы.Чем выше высота столбика относительно базовой производительности ( ‑O0 ) в правой части графика, тем лучше оптимизационные возможности программно-аппаратной платформы.Наблюдаем наибольший прирост производительности от оптимизации на этапе сборки на платформе «M4».Платформа x86На графике (Иллюстрация 3) по оси Y отображается число цикличных вызовов наблюдаемых функций за одну миллисекунду. На оси X — наблюдаемые функции (Таблица 4).Чем выше на графике столбики, тем лучше производительность.Цветом на оси X обозначен способ оптимизации на этапе сборки. Соответствие цвета и характера оптимизации отражает легенда.Иллюстрация 3: Производительность алгоритмов на платформе x86
Платформа «x86» максимально раскрывает преимущества алгоритмов с плавающей точкой перед целочисленными.Заслуживает отдельного внимания часть графика в оранжевом контуре.Производительность кода без оптимизации ( ‑O0 ) лучше на 39% для алгоритма «_fpu» ( ‑Os ) и на 16% для алгоритма «_fps» ( ‑O3 ). Другими словами, любая оптимизация на этапе сборки снижает производительность платформы «x86» на действительных числах.В то же время, целочисленные алгоритмы показывают ожидаемый прирост производительности при сборке с параметрами -O3 и -Os.Платформа M4Платформа «M4» демонстрирует предсказуемый результат (Иллюстрация 4).Иллюстрация 4: Производительность алгоритмов на платформе M4
Модуль с плавающей точкой «M4» даёт ожидаемый прирост производительности для алгоритма «_fps», основанного на коротком действительном — float.Последнее подтверждается результатом сравнения производительности алгоритмов при отключенном модуле FPU на платформе «M4» — Иллюстрация 5.Наблюдая графики помним, что точность вычислений алгоритма «_fps» гарантируется в диапазоном 1+E5 (см. Иллюстрация 1) без относительно того, включен ли модуль FPU на «M4» или нет.Иллюстрация 5: Производительность алгоритмов на платформе M4 без FPU
Платформа M0Результаты платформы «M0» похожи на результаты платформы «M4» без FPU (Иллюстрация 5), только медленнее — Иллюстрация 6.Заметим, тактовая частота при тестировании устанавливалась одинаковой и для «M4», и для «M0» — 48 MHz. Однако, производительность «M0» хуже в два с лишним раза, чем «M4», в условиях равенства прочих характеристик.Иллюстрация 6: Производительность алгоритмов на платформе M0
Алгоритм «_fps» на платформе «M0» ожидаемо опережает в два раза алгоритм «_fpu».Целочисленные алгоритмы опережают алгоритмы с плавающей точкой.По странному стечению обстоятельств в заключительном графике (Иллюстрация 6) снова есть место для оранжевого контура.При сборке без оптимизации ( ‑O0 ) алгоритм «_evn» быстрее алгоритма «_i32». И алгоритм «_evn» медленнее, чем «_i32», если сборка проводится с оптимизацией.Снова наблюдается относительное снижение производительности от оптимизации на этапе сборки. Заметим, у целочисленных алгоритмов на платформах «M4» и «x86» подобного эффекта нет.Вместо заключенияПроизводительность программы зависит от многих причин.Оптимизация на этапе сборки способна ухудшать производительность, замедлять программу, что демонстрируют экспериментальные данные в оранжевых контурах выше (Иллюстрация 3 и Иллюстрация 6).Цена аппаратной платформы безусловно повышает производительность программы безотносительно качества исходного кода и эффективности компилятора.Общего правила, как создавать эффективные программы, нет. Задача оптимизации и профилирования решается индивидуально в каждом конкретном случае.Приложение 1. Порядок тестирования платформы x86
  • Создать в среде CubeIDE (Eclipse CDT) проект C штатным способом
  • Написать текст программы — Пример 3
  • Добавить в проект файл sqrt_cmp.h — Пример 6
  • Осуществить сборку и запуск программы:
    • штатными средствами IDE;
    • или из командной строки — Пример 4
  • Меняя вид оптимизации ( -O0, -O3, -Os ) наблюдать результат.
Пример 3: Исходный текст программы x86 — main.c
#include "sqrt_cmp.h"
int main( void )
{
  main_Of_SqrtComp();
  return 0;
}
Пример 4 Запуск теста на платформе x86 из терминала
gcc main.c -o main -I. -Wall -lm -std=gnu11 -O3 && ./main
Запуск теста из терминала платформы «x86» предполагает, что файлы main.c и sqrt_cmp.h располагаются в одном каталоге, и этот каталог выбран рабочим (pwd).Иллюстрация 7: Запуск теста из терминала «x86»
Приложение 2. Порядок тестирования платформы STM32
  • Создать в среде CubeIDE проект STM32 штатным способом (CubeMX)
  • Добавить файл sqrt_cmp.h в проект STM32 — Пример 6
  • Включить sqrt_cmp.h в файл main.c — Пример 5
  • Осуществить сборку и запуск программы штатными средствами IDE
  • Меняя вид оптимизации ( -O0, -O3, -Os ) наблюдать результат
Пример 5: Исходный текст для STM32 (с пропусками < ... >) — main.c
< … >
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "sqrt_cmp.h"
/* USER CODE END Includes */
< … >
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
< … >
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  main_Of_SqrtComp();
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
Приложение 3. Порядок тестирования других алгоритмов и платформСборка теста для других платформ проводится по аналогии.Для отличных от упомянутых выше аппаратных платформ (Таблица 3), вероятно, потребуется косметическая модификация файла «sqrt_cmp.h».Пример 6: Содержание файла sqrt_cmp.h
/******************************************************************************
* File: sqrt_cmp.h Created on 5 авг. 2020 г.
* CC0 1.0 Universal (CC0 1.0)
* Creative Commons Public Domain Dedication
* No Copyright
*
* TAB Size .EQ 4
********************************************************************************/
#ifndef __SQRT_CMP_H
#define __SQRT_CMP_H
#include  <math.h>
#include  <stdio.h>
#include  <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/******************************************************************************
* Interface of the entry point for all sqrt tests
******************************************************************************/
void main_Of_SqrtComp();
/******************************************************************************
* test case selection: TEST_SET
* select one of the test suite via a comment.
******************************************************************************/
#define TEST_SET      TEST_ALL
//#define TEST_SET      TEST_ROUNDING
//#define TEST_SET      TEST_PERFORMANCE
/******************************************************************************
* Interfaces of test functions.
* See implementation of them at the end of this file.
******************************************************************************/
typedef uint16_t (*sqrt_func)( uint32_t number );
uint16_t sqrt_fpu( uint32_t number );  // floating point function from article
uint16_t sqrt_evn( uint32_t number );  // integer function from article
uint16_t sqrxi32( uint32_t y );      // integer function from comment by
uint16_t sqrt_fps( uint32_t number );  // optimized floating point function for Cortex M4
                    // <-- insert interface of your function here
/******************************************************************************
* Set to variable named as 'round_test_func' below
* to the alias of one of the functions above.
* The NULL will select last function in comp_list[]
******************************************************************************/
sqrt_func round_test_func = sqrt_fps;  // specific instance for the rounding test
//sqrt_func round_test_func = sqrxi32;  // specific instance for the rounding test
//sqrt_func round_test_func = sqrt_evn;  // specific instance for the rounding test
//sqrt_func round_test_func = NULL;    // last function in comp_list[]
/******************************************************************************
* The array of test functions for competing routines is called comp_list[].
* Adding a new function to the test:
- copy the implementation of the new function to the end of this file;
- declare the function interface at the beginning of this file;
- add the alias and declaration of the new function to
end of array named comp_list[].
******************************************************************************/
// @formatter:off
typedef struct
{
  sqrt_func  fsqrt;
  char *    alias;
} SCompFunc;
SCompFunc comp_list[] =  // competition list
{
  { sqrt_fpu, "_fpu" },
  { sqrt_fps, "_fps" },
  { sqrt_evn, "_evn" },
  { sqrxi32,  "_i32" }
              // <-- insert your function name & alias here
};
/* @formatter:on */
/******************************************************************************
* Platform-independent definitions
******************************************************************************/
#define PUT_FORMAT_MSG(f_, ...) { \
        sprintf( (char *)s_buf, (char *)f_, ##__VA_ARGS__ ); \
        PUT_MSG( (char *)s_buf ); }
#define MS_PER_SEC  1000
#define US_PER_SEC  ( MS_PER_SEC * MS_PER_SEC )
#define ARRAY_SIZE(a) (sizeof a / sizeof *a)  // size of static array at runtime
#define SIRV(f) if ( f ) ;            // suppress Ignore Return Value warning
/******************************************************************************
* Platform-specific defines
******************************************************************************/
#if defined( USE_HAL_DRIVER )  // STM32 ARM Cortex platform
#  include  <string.h>
#  include "main.h"
  //*****************************************************************************
  // Platform-specific defines for the helper functions
#  define SCALE_RATE    1  // must be .GE than 1
#  define X_CLOCK      HAL_GetTick()
#  define X_DELAY( ms )  HAL_Delay( ms )
  //*****************************************************************************
  // Platform-specific defines for the terminal output
#  define USART_HANDLE    huart1  // set valid USART handler alias here defined by the config of MCU
#  define USART_TIMEOUT  150    // max timeout for HAL_UART_Transmit
extern UART_HandleTypeDef USART_HANDLE;
extern HAL_StatusTypeDef HAL_UART_Transmit ( UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout );
#  define PUT_MSG( msg ) \
    HAL_UART_Transmit( &USART_HANDLE, (uint8_t *)msg, strlen( (char *)msg ), USART_TIMEOUT )
#  define CPU_CLOCK_MHz  ( SystemCoreClock / US_PER_SEC )  // CPU CLK in MHz
#  if defined( STM32F0 )
#    define  CPU_ID ( "STM32 ARM Cortex M0" )
#  elif defined ( STM32F3 )
#    define  CPU_ID ( "STM32 ARM Cortex M4" )
#  else
#    define  CPU_ID ( "Maybe STM32 ARM Cortex" )
#  endif
#  define PUT_SYS_INFO  PUT_FORMAT_MSG( " %s @ "fdU()" MHz\n", CPU_ID, CPU_CLOCK_MHz )
#else  // #if defined( USE_HAL_DRIVER  )
#    include <time.h>
#    include <stdlib.h>
  //*****************************************************************************
  // Platform-specific defines for the helper functions
#  define SCALE_RATE    100    // must be .GE than 1
#  define X_CLOCK      (uint32_t) x_clock()
#  define X_DELAY( ms )  x_delay( ms )
uint32_t x_clock()
{
  uint64_t result = (uint64_t) clock();
  result *= MS_PER_SEC;
  result /= CLOCKS_PER_SEC;
  return (uint32_t) result;
}
void x_delay( uint32_t ms )
{
  uint64_t tm = x_clock();
  while ( ( x_clock() - tm ) < ms )
    ;
}
  //*****************************************************************************
  // Platform-specific defines for the terminal output
#  define PUT_MSG( msg ) \
    printf( "%s", (char *)msg ), fflush ( stdout );
#  if defined( __unix__ )    // anybody other platform for gcc
#    define PUT_SYS_INFO  SIRV( system( "cat /proc/cpuinfo | grep 'model name' | head -1 | sed s/'model name\t:'/''/" ) )
#  else
#    define PUT_SYS_INFO  PUT_MSG( "Undefined System & CPU" )
#  endif  // #  if defined( __unix__ )  // anybody other platform for gcc
#endif    // #if defined( USE_HAL_DRIVER  )
#if  ( __WORDSIZE == 64 )
#  define fdI(s)  "%" #s "d"
#  define fdU(s)  "%" #s "u"
#  define fdX(s)  "%" #s "x"
#else  // let's say __WORDSIZE == 32
#  define fdI(s)  "%" #s "ld"
#  define fdU(s)  "%" #s "lu"
#  define fdX(s)  "%" #s "lx"
#endif  // #if ( __WORDSIZE == 64 )
#if defined ( DEBUG ) || defined ( _DEBUG ) // chk build mode of CubeIDE
#  define  BUILD_MODE  "DEBUG"
#else // Maybe Release
#  define  BUILD_MODE  "RELEASE"
#endif  // #if defined ( DEBUG ) || defined ( _DEBUG )
/******************************************************************************
* the helper data with testing ranges
******************************************************************************/
// @formatter:off
typedef struct
{
  uint32_t  start;
  uint32_t  stop;
  uint32_t  repeat;
} STestRange;
STestRange  test_rngs[] =
{
  { 0, 1000, 100 * SCALE_RATE },
  { 0, 10000, 10 * SCALE_RATE },
  { 0, 100000, 1 * SCALE_RATE }
};
uint32_t test_results[ARRAY_SIZE( test_rngs )][ARRAY_SIZE( comp_list ) + 1];
#define MSG_BUFF_SIZE  512
uint8_t s_buf[MSG_BUFF_SIZE];  // buffer for a terminal output
/* @formatter:on */
/******************************************************************************
* Test sets definitions. Do not change it.
******************************************************************************/
#define TEST_ROUNDING    1
#define TEST_PERFORMANCE  2
#define TEST_ALL      ( TEST_ROUNDING | TEST_PERFORMANCE )
#ifndef TEST_SET
#  define  TEST_SET  TEST_ALL
#endif
#define HI_ROUND_TEST_RANGE_END    0x007FFFFFUL
#define HI_ROUND_TEST_RANGE_START  ( HI_ROUND_TEST_RANGE_END >> 4 )
/******************************************************************************
* Interface of helper functions
******************************************************************************/
void main_Header();
void testRounding();
void testPerformance();
/******************************************************************************
* Implementation of the entry point for all sqrt tests
******************************************************************************/
void main_Of_SqrtComp()
{
  X_DELAY( MS_PER_SEC / 2 );  // suppress the output of a previous instance
                // while the new instance is loading into the MCU
  uint32_t start_time = X_CLOCK;
  main_Header();
  // checking normal and extended ranges for rounding
  if ( TEST_SET & TEST_ROUNDING )
    testRounding();
  // checking normal ranges on execution time
  if ( TEST_SET & TEST_PERFORMANCE )
    testPerformance();
  uint32_t test_time = X_CLOCK - start_time;
  uint32_t test_m = ( test_time / MS_PER_SEC ) / 60;
  uint32_t test_s = ( test_time / MS_PER_SEC ) % 60;
  uint32_t test_ms = test_time % MS_PER_SEC;
  PUT_FORMAT_MSG( "\ndone, spent time: "fdU()" m, "fdU()"."fdU()" s\n", test_m, test_s, test_ms );
}
/******************************************************************************
* Implementation of the helper functions
******************************************************************************/
void main_Header()
{
  PUT_MSG( "\n\n**********************************************************\n" );
  PUT_SYS_INFO;
  PUT_FORMAT_MSG( "*********** %s, built at %s\n", BUILD_MODE, __TIME__ );
}
void testPerformance()
{
  uint32_t i_func, i_rpt, i_rng;
  uint32_t number, first, second, diff;
  uint64_t temp;
  PUT_MSG( "----------+ Performance test" );
  for ( i_rng = 0; i_rng < ARRAY_SIZE( test_rngs ); i_rng++ )
  {
    PUT_MSG( "\n" );
    PUT_FORMAT_MSG( "test range:["fdU()".."fdU()"], repeat="fdU()"\n", test_rngs[i_rng].start, test_rngs[i_rng].stop,
        test_rngs[i_rng].repeat );
    test_results[i_rng][0] = test_rngs[i_rng].stop;
    for ( i_func = 0; i_func < ARRAY_SIZE( comp_list ); i_func++ )
    {
      PUT_FORMAT_MSG( "%s ... ", comp_list[i_func].alias );
      first = X_CLOCK;
      for ( i_rpt = 0; i_rpt < test_rngs[i_rng].repeat; i_rpt++ )
        for ( number = test_rngs[i_rng].start; number < test_rngs[i_rng].stop; number++ )
          comp_list[i_func].fsqrt( number );
      second = X_CLOCK;
      diff = second - first;
      temp = ( test_rngs[i_rng].stop - test_rngs[i_rng].start ) * test_rngs[i_rng].repeat;
      test_results[i_rng][i_func + 1] = (uint32_t) ( temp / diff );
      if ( i_func < ARRAY_SIZE( comp_list ) - 1 )
        PUT_MSG( ", " );
    }
  }
  // small report
  PUT_FORMAT_MSG( "\n----------+ Report: sqrt`s calls per ms\n%10s", "range" );
  for ( i_func = 0; i_func < ARRAY_SIZE( comp_list ); i_func++ )
    PUT_FORMAT_MSG( "%10s", comp_list[i_func].alias );
  for ( i_rng = 0; i_rng < ARRAY_SIZE( test_rngs ); i_rng++ )
  {
    PUT_MSG( "\n" );
    for ( i_func = 0; i_func < ARRAY_SIZE( comp_list ) + 1; i_func++ )
      PUT_FORMAT_MSG( fdU( 10 ), test_results[i_rng][i_func] );
  }
  PUT_FORMAT_MSG( "\n----------+\n%10s", "average" );
  for ( i_func = 0; i_func < ARRAY_SIZE( comp_list ); i_func++ )
  {
    temp = 0;
    for ( i_rng = 0; i_rng < ARRAY_SIZE( test_rngs ); i_rng++ )
      temp += test_results[i_rng][i_func + 1];
    temp /= ARRAY_SIZE( test_rngs );
    PUT_FORMAT_MSG( fdU( 10 ), (uint32_t)temp );
  }
}
void testRoundingFunction( uint32_t start, uint32_t finish, sqrt_func psqrt, char *fname );
void testRounding()
{
  uint16_t i_rng;
  uint16_t f_rng;
  PUT_MSG( "----------+ Rounding test\n" );
  // checking the existence for the test function
  for ( f_rng = 0; f_rng < ARRAY_SIZE( comp_list ); f_rng++ )
    if ( comp_list[f_rng].fsqrt == round_test_func )
      break;
  if ( !( f_rng < ARRAY_SIZE( comp_list ) ) )
  {
    f_rng = ARRAY_SIZE( comp_list ) - 1;
    PUT_FORMAT_MSG( "Value of 'round_test_func' not found.\n" );
  }
  PUT_FORMAT_MSG( "Function '%s' is tested for rounding.\n", comp_list[f_rng].alias );
  // checking standard ranges
  for ( i_rng = 0; i_rng < ARRAY_SIZE( test_rngs ); i_rng++ )
    testRoundingFunction( test_rngs[i_rng].start, test_rngs[i_rng].stop, comp_list[f_rng].fsqrt, comp_list[f_rng].alias );
  // checking extended range
  testRoundingFunction( HI_ROUND_TEST_RANGE_START, HI_ROUND_TEST_RANGE_END, comp_list[f_rng].fsqrt, comp_list[f_rng].alias );
}
void turn_the_fan( uint32_t ms );
void testRoundingFunction( uint32_t start, uint32_t finish, sqrt_func psqrt, char *fname )
{
  uint32_t rf, ri;
  uint32_t n, c = 0;
  PUT_FORMAT_MSG( "test range:["fdU( 10 )".."fdU( 10 )"] ... ", start, finish );
  for ( n = start; n < finish; n++ )
  {
    rf = sqrt_fpu( n );
    ri = ( *psqrt )( n );
    if ( rf != ri )
    {
      if ( c++ > 3 )
      {
        PUT_FORMAT_MSG( "\b\n(!)too many mistakes in '%s', ", fname );
        break;
      }
      else
      {
        double d = sqrt( (double) n );
        PUT_FORMAT_MSG( "\b\n%s("fdU( 10 )")="fdU()" != "fdU(), fname, n, ri, rf );
        PUT_FORMAT_MSG( " (real value is %.6lf)", d );
      }
    }
    turn_the_fan( MS_PER_SEC );
  }
  if ( !c )
  {
    PUT_FORMAT_MSG( "\b done.\n" );
  }
  else
  {
    PUT_FORMAT_MSG( "test failed.\n" );
  }
}
void turn_the_fan( uint32_t ms )
{
  static char ca[] = "|/-\";
  static uint32_t cs = ARRAY_SIZE(ca) - 1;
  static uint32_t cn = 0;
  static uint32_t at = 0;
  uint32_t ct = X_CLOCK;
  if ( ct - at > ms )
  {
    at = ct;
    PUT_FORMAT_MSG( "\b%c", ca[cn++ % cs] );
  }
}
/******************************************************************************
* Implementation of the sqrt functions
******************************************************************************/
// floating point arg & result with double
uint16_t sqrt_fpu( uint32_t number )
{
  if ( number < 2 )
    return (uint16_t) number;
  double f_rslt = sqrt( number );
  uint32_t rslt = (uint32_t) f_rslt;
  if ( !( f_rslt - (double) rslt < .5 ) )
    rslt++;
  return (uint16_t) rslt;
}
// floating point arg & result with float
uint16_t sqrt_fps( uint32_t number )
{
  if ( number < 2 )
    return (uint16_t) number;
  float f_rslt = sqrtf( number );
  uint32_t rslt = (uint32_t) f_rslt;
  if ( !( f_rslt - (float) rslt < .5 ) )
    rslt++;
  return (uint16_t) rslt;
}
// unsigned integer arg & result
// @formatter:off
uint16_t sqrt_evn ( uint32_t number )
{
  if ( number < 2 )
    return ( uint16_t ) number;
  uint32_t temp;
  uint32_t div;
  uint32_t rslt;
  if ( number & 0xFFFF0000L )
    if ( number & 0xFF000000L )
      if ( number & 0xF0000000L )
        if ( number & 0xE0000000L )
          div = 43771;
        else
          div = 22250;
      else
        if ( number & 0x0C000000L )
          div = 11310;
        else
          div = 5749;
    else
      if ( number & 0x00F00000L )
        if ( number & 0x00C00000L )
          div = 2923;
        else
          div = 1486;
      else
        if ( number & 0x000C0000L )
          div = 755;
        else
          div = 384;
  else
    if ( number & 0xFF00L )
      if ( number & 0xF000L )
        if ( number & 0xC000L )
          div = 195;
        else
          div = 99;
      else
        if ( number & 0x0C00L )
          div = 50;
        else
          div = 25;
    else
      if ( number & 0xF0L )
        if ( number & 0x80L )
          div = 13;
        else
          div = 7;
      else
        div = 3;
  rslt = number;
  while ( 1 )
  {
    temp = number / div;
    temp += div;
    div = temp >> 1;
    div += temp & 1;
    if ( rslt > div )
      rslt = div;
    else
    {
      if ( number / rslt == rslt - 1 && number % rslt == 0 )
        rslt--;
      return ( uint16_t ) rslt;
    }
  }
}
/* @formatter:on */
// unsigned integer arg & result
uint16_t sqrxi32( uint32_t y )
{
  if ( y == 1 )
    return 1;
  uint32_t xh = y > 0x10000ul ? 0x10000ul : y;
  uint32_t xl = 0;
  uint32_t xc;
  for ( int k = 0; k < 16; k++ )
  {
    xc = ( xh + xl ) >> 1ul;
    if ( xc * xc - xc >= y )
    {
      xh = xc;
    }
    else
    {
      xl = xc;
    }
  }
  return ( xh + xl ) >> 1ul;
}
// <-- insert implementation of your function sqrt here
#ifdef __cplusplus
}
#endif
#endif // __SQRT_CMP_H

===========
Источник:
habr.com
===========

Похожие новости: Теги для поиска: #_algoritmy (Алгоритмы), #_c, #_algoritm (алгоритм), #_optimizatsija (оптимизация), #_algoritmy (
Алгоритмы
)
, #_c
Профиль  ЛС 
Показать сообщения:     

Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы

Текущее время: 25-Апр 19:39
Часовой пояс: UTC + 5