Страницы

Поиск по вопросам

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

понедельник, 30 марта 2020 г.

sealed, virtual, невиртуальные методы в C# и производительность

#c_sharp #net #ооп #производительность #clr


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

class X
{
    public int NonVirtual() => DateTime.Now.Millisecond;
    public virtual int Virtual() => DateTime.Now.Minute;
}

class Y : X
{
    public override int Virtual() => DateTime.Now.Millisecond;
}

class Program
{

    private static volatile int TST = 0;

    static int Slow(Y x) => x.Virtual();

    static int Fast(Y y) => y.NonVirtual();

    static void Main(string[] args)
    {

        int i = 0;
        var stopwatch1 = new Stopwatch();
        var stopwatch2 = new Stopwatch();

        stopwatch1.Start();
        var y = new Y();
        for (i = 0; i < 100000000; i++)
        {
            TST = Fast(y);
        }
        stopwatch1.Stop();
        Console.WriteLine("Time elapsed: {0}", stopwatch1.Elapsed);

        stopwatch2.Start();
        for (i = 0; i < 100000000; i++)
        {
            TST = Slow(y);
        }
        stopwatch2.Stop();
        Console.WriteLine("Time elapsed: {0}", stopwatch2.Elapsed);

        Console.ReadKey();
    }
}


Поле TST объявлено как volatile, чтобы компилятор не оптимизировал вызовы.

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

Тогда я полез в IL:

IL_0001: callvirt  instance int32 PerformanceTests.X::Virtual()


Это вызов виртуального метода. Вроде всё нормально. Второй вызов:

IL_0001: callvirt  instance int32 PerformanceTests.X::NonVirtual()


Что, простите? callvirt? Разве тут не должно быть call? sealed так-же никак не влияет
на вызов виртуального метода.

Хотел бы выяснить у более опытных коллег, всё-таки, есть ли смысл в запечатывании
классов в плане производительности? А так-же, почему вызов невиртуальной функции в
IL такой же как и виртуальной?

Update:
В комментариях @Grundy написал, что callvirt - из-за того, что метод объявлен в базовом
классе. Переписал код так, что теперь используется базовый класс (т.е. Y - не используется).
callvirt так и вызывается.
    


Ответы

Ответ 1



Как уже сказали в комментариях, компилятор использует callvirt что бы генерировать NullReferenceException. Что бы получить чистую call инструкцию компилятор должен быть уверен, что экземпляр класса не может быть null. Пример: class Test { public void Method() => Console.WriteLine(123); } static void Main(string[] args) { new Test().Method(); } IL-код: IL_0001: newobj instance void ConsoleApp1.Program/Test::.ctor() IL_0006: call instance void ConsoleApp1.Program/Test::Method() Если код немного изменить: static Test GetTest() => new Test(); static void Main(string[] args) { GetTest().Method(); } То уже получаем callvirt, так как компилятор предполает, что GetTest может вернуть null: IL_0001: call class ConsoleApp1.Program/Test ConsoleApp1.Program::GetTest() IL_0006: callvirt instance void ConsoleApp1.Program/Test::Method() sealed ни на что не влияет в рантайме. Это просто маркер для разработчиков, который сообщает, что можно делать в высокоуровневом коде, а что нельзя. Для каждого callvirt невиртуального метода JIT вставляет одну дополнительную инструкцию перед каждым call: cmp dword ptr [/*здесь регистр с адресом экземляра*/],ecx call 00007FF9CD540098 // метод Эффект от одной cmp инструкции очень незначителен.

вторник, 17 марта 2020 г.

Возможность перевести GC в режим реального времени

#c_sharp #сборщик_мусора #производительность


Недавно увидел что на хабре промелькнула информация о том, что

..в C# есть возможность перевести GC в
режим реального времени (возможность
гарантировать выполнение кода
последовательно без перерыва на сборку
мусора).

может поподробней кто нибудь пожалуйста объяснить как это сделать?    


Ответы

Ответ 1



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

воскресенье, 15 марта 2020 г.

Быстродействие загрузки данных из DB2 в Oracle

#oracle #производительность #odbc #db2


Вопрос сложный и неоднозначный, но надеюсь на любые советы (а может кому и мой опыт
поможет):


Есть сервер с Oracle 11.2.0.3 x32 на Windows Server 2008R2 x64 (4ядра/8 потоков,
8гб оперативы, один HDD без рейда).
В источниках ODBCx32 настроено подключение к DB2 через CLI DRIVER с именем db2
Целевой сервер DB2 находится на мейнфрейме, настроек которого я не знаю, версия 9fix15
Поднят дополнительный listener:   

SID_LIST_LISTENERdb2 =
  (SID_LIST =
    (SID_DESC=
         (SID_NAME=db2)
         (ORACLE_HOME=C:\app\product\11.2.0\dbhome_1)
         (PROGRAM=dg4odbc))
  )
LISTENERdb2 =
 (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1523))
    )
  )


В tnsnames.ora:   

db2 =
 (DESCRIPTION=
   (ADDRESS=(PROTOCOL=tcp)(HOST=localhost)(PORT=1523))
   (CONNECT_DATA=(SID=db2))
   (HS=OK)
) 

Создан public dblink: 

CREATE PUBLIC DATABASE LINK db2 CONNECT TO
"login" IDENTIFIED BY "pwd" using 'db2';`

Создана процедура:  

CREATE OR REPLACE PROCEDURE P_LOAD_DATA IS 
BEGIN 
insert into data_table select * from data_table@db2 d where d.date_op between TRUNC(SYSDATE
- 4/24, 'HH24') + 1/24/60 AND TRUNC(SYSDATE - 2/24, 'HH24'));
END;/

Создан job на запуск процедуры раз в 2 часа (или 12 джобов на каждый запуск).


Целевая таблица размером примерно 13млн записей по 50 столбцов, моя таблица собирает
архив из целевой и имеет размер примерно 150млн записей тех же 50 столбцов.

Перекачка ~600к записей (150-200мб) занимает в лучшем случае час, периодически переваливает
за 4-5 часов. Плюс хорошо бы держать индексы на таблицу архива, чтобы из нее можно
было потом что-то вытащить в приемлемый срок, но это опционально. Корректный ли выбран
способ наполнения архива или есть альтернативы лучше? Проблема в том, что периодически
джобы прерываются с причиной "Job slave process was terminated", а в логах 

ORA-00603: ORACLE server session terminated by fatal error
ORA-28500: connection from ORACLE to a non-Oracle system returned this message:
[IBM][CLI Driver] CLI0106E  Connection is closed. SQLSTATE=08003 {08003,NativeErr
= -99999}
ORA-02063: preceding 2 line


Это вероятно мейнфрейм закрывает подключение по таймауту.
Причина разрядности Оракла - ни один найденный вариант драйвера DB2 не работал на х64.
    


Ответы

Ответ 1



Ну, очевидно, стоит начать ковырять с анализа того, куда же уходит и час и 4 часа - т.е. включаем трассировку на сессию джоба и анализируем полученный трейс. Дальше, в порядке отработки версии что тормозит DB2 на мейнфрейме, я бы просто отселектил необходимый диапазон, но без записи в таблицу - т.е., как говорят в unix-мире, в /dev/null. Но при этом чтобы IBM отдал все записи и они прошли по сети и db-link'у - т.е. просто SELECT, но с COUNT'ом или группировкой + поиграть с хинтом +driving_site - чтоб точно все записи сначала пришли к нам в Оракл. Ну и потом уже - классические варианты записи большого объема в Oracle - NOLOGGING, +APPEND, варианты с временной таблицей (партицией) и DBMS_REDEFINITION / EXCHANGE PARTITION, удаление индексов до / пересоздание после, BULK INSERT и тд и тп. Главное начать и выяснить, что оптимизировать или где затык. А дальше уже дело техники. Если есть такая физическая/юридическая возможность + необходимость в предлагаемом - если Вы можете предоставить удаленный доступ - с удовольствием помогу решить (или хотя бы "осмотреть наружно" / применить озвученные выше рекомендации) текущую задачку "физически", бо интересный случай + имею предыдущий опыт в решении похожих ситуаций, когда перекачка данных в гетерогенных средах ведет себя не так, как ожидается. Это предложение, кстати - подключиться и помочь удаленно - относится и ко всем остальным, кто рано или поздно попадет в этот тред/обсуждение (через поиск или еще как).

воскресенье, 8 марта 2020 г.

Медленный ли Symfony?

#php #производительность #symfony #phalcon


В данный момент, решил попробовать Symfony 3 на PHP 7, и задаюсь вопросом производительности.

Есть задача написания REST API, решил попробовать Symfony 3, написал тестовое API
и попробовал выполнить тест Apache AB (ab -t 10 -c 10) на несуществующий метод, скорость
обработки 2062 запроса за 10 секунд или 206 запросов в секунду.

Делаю тоже самое на Phalcon, скорость 23297 запросов за 10 секунд или 2329.69 запросов
в секунду. Если запускать один запрос, то скорость одинаковая, примерно 90 мс.

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

Отрыв Phalcon становится меньше, если взять и добавить операцию поиска в базу и маппинга,
через модель встроенную в фреймворк. Phalcon правда всеравно быстрее, 1600 запросов
против 900 запросов Symfony за 10 секунд.

Конфигурация: PHP 7, Debian, opcache on, Symfony3, Phalcon 3, Кеш Symfony включен,
режим Production.

Подключенные бандлы в Symfony:

new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
new Symfony\Bundle\SecurityBundle\SecurityBundle(),
new Symfony\Bundle\TwigBundle\TwigBundle(),
new Symfony\Bundle\MonologBundle\MonologBundle(),
new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
new Doctrine\Bundle\DoctrineBundle\DoctrineBundle(),
new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
new AppBundle\AppBundle(),
new FOS\RestBundle\FOSRestBundle(),
new JMS\SerializerBundle\JMSSerializerBundle(),
new Nelmio\CorsBundle\NelmioCorsBundle()


app.php:

use Symfony\Component\HttpFoundation\Request;

/**
 * @var Composer\Autoload\ClassLoader
 */
$loader = require __DIR__.'/../app/autoload.php';
include_once __DIR__.'/../var/bootstrap.php.cache';

$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$kernel = new AppCache($kernel);

// When using the HttpCache, you need to call the method in your front controller
instead of relying on the configuration parameter
Request::enableHttpMethodParameterOverride();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);




Ладно, делаем замеры потребляемых ресурсов при выполнении запроса в базу данных на
поиск и маппинг:

Выполняем замер Symfony и существующей конфигурации PHP-FPM при работе Apache AB
(ab -t 10 -c 10):

20383 www-data 20 0 380884 26040 18020 R **58,3** 0,3 0:10.35 php-fpm7.0
21394 www-data 20 0 380884 26040 18020 R **57,3** 0,3 0:03.62 php-fpm7.0
21498 www-data 20 0 380884 25908 17900 R **57,3** 0,3 0:02.77 php-fpm7.0
19479 www-data 20 0 380884 25020 18020 D **55,7** 0,3 0:15.08 php-fpm7.0
20481 www-data 20 0 380884 26044 18020 R **52,0** 0,3 0:09.30 php-fpm7.0


Окей, в среднем FPM жрет 52 процента ядра на каждый процесс, делаем замер Phalcon:

24110 www-data 20 0 379016 17876 12848 S **13,6** 0,2 0:03.57 php-fpm7.0
25593 www-data 20 0 378884 17776 12848 R **13,3** 0,2 0:02.30 php-fpm7.0
27023 www-data 20 0 378884 17608 12788 S **13,3** 0,2 0:01.08 php-fpm7.0
25445 www-data 20 0 378884 17780 12848 S **13,0** 0,2 0:02.48 php-fpm7.0
27177 www-data 20 0 378884 17600 12788 R **13,0** 0,2 0:00.91 php-fpm7.0


Phalcon не грузит систему, при одинаковых действиях, загрузка на каждый процесс 13
процентов. 

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

Кто разбирается в Symfony, скажите, как сделать его быстрее и возможно ли это? В
чем ошибка?
    


Ответы

Ответ 1



фалькон быстрый потому, что написан как расширение для PHP на С Да, симфони очень медленный, вовсе не по тому, что "Symfony дергает кеш с диска каждый раз, а Phalcon по умолчанию все пишет в память.", а потому, что представляет собой достаточно толстую прослойку уймы абстракций. Нет, невозможно. Они предназначены для разных задач. Удобство клепать мелкие сайты с модным MVC на Симфони vs условно-хардкорная производительность чистых сей для фалькона. и нет, у вас не получится как-то супер-быстро разрешать нагрузку на на существующий урл, если все урлы обрабатывает симфони, если урл дошел от веб-сервера до симфони, она его еще как обработает. Придется решать проблему несуществующих урлов как-то по-другому (на стороне веб-сервера) Сделать симфоню быстрее можно, наверное, настроив правильное кэширование (opcache), но тем не менее, с фальконом она не сможет соревноваться по скорости. Т.е. даже симфоневый раутинг (я про 404) будет медленней фальконовского. Кроме того, использование Апача в качестве сервера для хайлоада не oche рекомендуется (возможно, он, конечно, вырос за последние годы) P.S. Как я уже говорил, 36 тысяч вызовов функции substr на один http запрос в Симфони... ну.. окей.

Ответ 2



Привет из 2018, Symfony 4 теперь мега крутой, мощный и при этом быстрый микрофреймворк со своим Flex плагином для Composer-а и конкурентов 0.

Получить счетчики производительности

#c_sharp #производительность #счетчики #мониторинг


Мне необходимо получить данные системы как в мониторинге ресурсов Windows. А именно
работу каждого процесса с файлами и статистику. Данные из верхней таблице нашел через
GetPidIoCounters и структуру IO_COUNTERS а вот данные из нижней сетки не получается.


    


Ответы

Ответ 1



1) Через WMI, класс Win32_Process, как с ним работать написано тут и тут 2) Через PerformanceCounter void Main() { Process p = Process.GetProcesses()[0]; PerformanceCounter ramCounter = new PerformanceCounter("Process", "Working Set", p.ProcessName); PerformanceCounter cpuCounter = new PerformanceCounter("Process", "% Processor Time", p.ProcessName); PerformanceCounter privateCounter = new PerformanceCounter("Process", "Private Bytes", p.ProcessName); while (true) { Thread.Sleep(1000); double ram = ramCounter.NextValue(); double cpu = cpuCounter.NextValue(); double privateBytes = privateCounter.NextValue(); Console.WriteLine("RAM: "+(ram/1024/1024)+" MB; CPU: "+(cpu)+" %; " + privateBytes/1024/1024 + " MB"); } }

Ответ 2



Вам нужен класс PerfomanceCounter: Примерно так будет: foreach(var process in Process.GetProcesses()) { PerformanceCounter pcRead = new PerformanceCounter("Process", "IO Write Bytes/sec", process.ProcessName); PerformanceCounter pcWrite = new PerformanceCounter("Process", "IO Read Bytes/sec", process.ProcessName); Console.WriteLine($"Read: {pcRead.NextValue()} + %"); Console.WriteLine($"Write: {pcWrite.NextValue()} + %"); } Пример описание использования Список категорий для процессов

среда, 4 марта 2020 г.

Есть ли разница при SELECT * или SELECT `some_column` в PHP

#php #mysql #база_данных #select #производительность


Интересует насколько это влияет на производительность запроса в PHP

все запросы я делаю просто SELECT * ... LIMIT 0,20 и он постоянно подтягивает значения
всех столбцов (их примерно до 15), но использую из них я максимум 6-7.

У меня все время стоит лимит, а в базе строк больше > 10000 вот интересно это сильно
влияет на скорость? а что если дальше продолжать еще делать JOIN'ы ?
    


Ответы

Ответ 1



В большинстве случаев морочиться на эту тему не нужно. Вопрос про * в запросах - это такая trivia, которую нубы с придыханием рассказывают друг другу и которую можно прочитать в идиотских списках "25 способов ускорить свой скрипт в 100500 раз". Если удобно писать * и в базе нет блобов с гигазами варезов, а есть 15 несчастных варчаров, то никто не умрет от звездочки. Плюсы использования звездочки: при добавлении полей в таблицу не надо менять код запроса запрос становится читабельнее и короче Минусы использования звездочки: большой потенциальный объем данных, передаваемых в пхп, может сказаться на производительности если в запросе используется временная таблица, то наличие ненужных полей может катастрофически отразиться на времени запроса, поскольку во временную таблицу будут записываться все поля таблицы. Разумеется, лучше избегать запросов, которым требуется создание временной таблицы.

Ответ 2



использование * = "bad practice". минусы использование * : 1)если вы будете дальнейшей писать JOIN-i тогда у вас будет проблемы 2)если вы будете дальнейшей добавляет столбцы тогда они будет автоматический подключиться "без нужды" 3)лишние данные это зло 4)использование не дает вам прав наименование столбцов 5)может быть меньше но влияет на загрузку

вторник, 18 февраля 2020 г.

Эффективные ли побитовые операции в Java?

#java #производительность #битовые_операции


Если брать нативные языки на подобие "C++" или "C", то там понятен выигрыш в производительности
напрямую играться с регистрами, но если брать язык, где все крутится на виртуалке JVM
- не совсем понятно, в чем мы можем выиграть в производительности и выиграем ли вообще??

Как работают битовые операции под капотом JVM?
Или в джава просто реализованы побитовые операции для лучшей переносимости тех же
алгоритмов например с плюсов или си?

И в каких реальных случаях в java мире нам будет интересно применять на практике
битовые операции?
    


Ответы

Ответ 1



в чем мы можем выиграть в производительности В производительности перед операциями с аналогичными результатами без применения битовых операций. Если вы думаете, что JVM делает всё медленнее, то ведь не только битовые операции страдают, верно? в каких рельных случаях в java мире нам будет интересно применять на практике битовіе операции? В тех же, что и в остальном мире Если брать нативные языки на подобие "с++" или "С" то там понятен выиграш в производительности напрямую игратся с регистрами, но если брать язык где все крутится на виртуалке JVM JVM - это не интерпретатор, это машина, транслирующая java-байткод в машинный код, соответствующий спецификации. В результате битовых операций будут вызываться ровно те же инструкции процессора, что и на ассемблере. Конкретно в самой битовой операции никакой потери производительности нет, основной "замедлитель" относительно условного си - это наличие сборки мусора и присущих stop-the-world пауз.

воскресенье, 16 февраля 2020 г.

Эффективные ли побитовые операции в Java?

#java #производительность #битовые_операции


Если брать нативные языки на подобие "C++" или "C", то там понятен выигрыш в производительности
напрямую играться с регистрами, но если брать язык, где все крутится на виртуалке JVM
- не совсем понятно, в чем мы можем выиграть в производительности и выиграем ли вообще??

Как работают битовые операции под капотом JVM?
Или в джава просто реализованы побитовые операции для лучшей переносимости тех же
алгоритмов например с плюсов или си?

И в каких реальных случаях в java мире нам будет интересно применять на практике
битовые операции?
    


Ответы

Ответ 1



в чем мы можем выиграть в производительности В производительности перед операциями с аналогичными результатами без применения битовых операций. Если вы думаете, что JVM делает всё медленнее, то ведь не только битовые операции страдают, верно? в каких рельных случаях в java мире нам будет интересно применять на практике битовіе операции? В тех же, что и в остальном мире Если брать нативные языки на подобие "с++" или "С" то там понятен выиграш в производительности напрямую игратся с регистрами, но если брать язык где все крутится на виртуалке JVM JVM - это не интерпретатор, это машина, транслирующая java-байткод в машинный код, соответствующий спецификации. В результате битовых операций будут вызываться ровно те же инструкции процессора, что и на ассемблере. Конкретно в самой битовой операции никакой потери производительности нет, основной "замедлитель" относительно условного си - это наличие сборки мусора и присущих stop-the-world пауз.

среда, 12 февраля 2020 г.

Где размещать css и js файлы - в начале или конце документа?

#html #производительность


Как, всё-таки, правильно подключать стили и js файлы? 

Во многих источниках указано, что наиболее верным является такой способ:
css в начале(в head), а js в конце, перед закрытием . Но когда я смотрю на крупнейших
ресурсах как это сделано, то получается везде по-разному, а ведь, как я понимаю, крупнейшие
ресурсы заботятся о скорости загрузки сайта в первую очередь. Кто-то всё размещает
в head, кто-то и так, и сяк. 

Или тут есть какие-то "подводные камни" на которые стоит обратить внимание? Просто
весьма интересно. Сам всегда размещаю css в начале, а все js в конце.
    


Ответы

Ответ 1



Почему модель css вверху, js внизу более привлекательная? Тут важный момент это то, что браузер загружает страницу сверху вниз Сценарий №1 Есть страница на которой скрипты и стили вверху Примерный сценарий ее загрузки будет такой: Загрузка стилей Загрузка скриптов Загрузка остальной разметки В данном сценарии есть недостаток, до момента отображения непосредственно контента страницы браузер должен загрузить стили и скрипты, что дает довольно большую задержку если это крупный ресурс. Сценарий №2 Есть страница на которой стили вверху, а скрипты перед закрывающим тегом body Примерный сценарий ее загрузки будет такой: Загрузка стилей Загрузка разметки Загрузка скриптов В данном сценарии есть преимущества по сравнению с первым, до момента отображения непосредственно контента страницы браузер должен загрузить только стили, а скрипты он загрузит в последнюю очередь, что позволяет пользователю быстрее увидеть содержимое страницы. Почему стили стоит оставлять вверху? Если стили также как и скрипты перенести вниз страницы то разметка после загрузки будет не стилизована, что будет выглядеть довольно некрасиво до момента загрузки стилей.

Ответ 2



Вынесите в конец то, что не является необходимым для оформления вашего сайта. Ведь при открытии сайта пользователь вначале видит его, а потом - взаимодействует с ним. Если (условно говоря) в каком-то скрипте у вас стоит изменение шрифтов (в зависимости от устройства, размера экрана, Math.random() =)), то этот скрипт имеет смысл разместить повыше, что бы пользователь скорее увидел сайт в нужном виде. Если же какой-то скрипт занимаются обработкой изображений предпросмотров, подгрузку статей по мере прокрутки или выполняет ещё какие-то действия, не влияющие на то, что видит пользователь в первое мгновение открытия сайта - вынесите это ниже. В общем, перефразируя один лозунг, - "функционал ничто, вид - всё!" :)

суббота, 8 февраля 2020 г.

Производительность алгоритмов std

#cpp #производительность


Потихоньку учусь использовать алгоритмы стандартной библиотеки C++. Самодокументирование
кода - это хорошо, но решил проверить и производительность. Например, std::accumulate.
Qt Creator, mingw, windows, QTest. В объекте создаём приватные поля:

QVector  results_;
float           sum_;
static const size_t size_=65536;
float           arr_[size_];


В конструкторе пишем:

std::fill(arr_,arr_+size_,1.0);


Создаём три слота:

void Tester::benchmarkSimpleLoopOverArray()
{
    sum_=0;
    QBENCHMARK
    {
        for (size_t i=0;i


Ответы

Ответ 1



У вас ошибка в коде. Вот это float * pf=arr_; float * pf0=pf+size_; должно быть внутри QBENCHMARK а не снаружи. Также тесты у вас не равнозначны, потому что используется общая переменная sum_, она принимает разные значения в зависимости от кол-ва итераций. Ее надо инициализировать внутри QBENCHMARK float sum_ = 0; Тогда получите приблизительно одинаковые результаты RESULT : TestClass::benchmarkSmartLoopOverArray(): 0.058 msecs per iteration (total: 60, iterations: 1024) PASS : TestClass::benchmarkSimpleLoopOverArray() RESULT : TestClass::benchmarkSimpleLoopOverArray(): 0.059 msecs per iteration (total: 61, iterations: 1024) PASS : TestClass::benchmarkAccumulateOverArray() RESULT : TestClass::benchmarkAccumulateOverArray(): 0.059 msecs per iteration (total: 61, iterations: 1024) PASS : TestClass::cleanupTestCase() Totals: 5 passed, 0 failed, 0 skipped, 0 blacklisted ********* Finished testing of TestClass *********

Ответ 2



Для начала, ваш эксперимент выглядит неправильным. У вас разное количество итераций, почему? Например, для «выигрышного» случая у вас почему-то 0x1000000 итераций, а не 65536 = 0x10000. Проверьте, скорее всего причина больших расхождений в этом. Вы занимаетесь ненужной микрооптимизацией. Дело в том, что расходы на управление циклом ничтожны в любом из случаев. Если в вашем цикле делается что-то нетривиальное, то эта операция по времени зарулит управление циклом. Если же в цикле делается какая-то быстрая мелочь, типа сложения, то он всё равно быстрый, и выигрыш в несколько наносекунд Кроме того, хороший компилятор с включенной оптимизацией скомпилирует ваш код в одно и то же, т. к. смысл у кода одинаковый. Можно, конечно. Операционка, думаю, тут не у дел, а вот разные компиляторы оптимизируют по-разному. Скорее всего, это просто ошибка. Проверьте сложение на одних и тех же данных. Да, существуют случаи, когда микрооптимизации нужны. Обычно квалификации даже хорошего программиста не хватает, чтобы определить, где узкое место в программе. Поэтому обычно не принято заниматься преждевременной оптимизацией (как завещал дедушка Кнут), а использовать профайлер. Когда вы точно знаете, где проблема, вы можете провести оптимизацию там. Таким образом, вы не испортите читаемость и поддерживаемость программы там, где это не принесёт никакого результата.

Ответ 3



Интересно, это не Вы публикуете на хабре статьи на эту же тему - http://habrahabr.ru/post/260025/ и http://habrahabr.ru/post/260193/ ? там есть ответы на многие Ваши вопросы.

среда, 5 февраля 2020 г.

Зависит ли время на создание объекта от количества его свойств и методов?

#c_sharp #net #производительность


Есть 2 класса:

public class MyClass
{
    public int Prop { get; set; }
}

public class MyClass2
{
    public int Prop1 { get; set; }
    public int Prop2 { get; set; }
    public int Prop3 { get; set; }
    public int Prop4 { get; set; }
    public int Prop5 { get; set; }
}


Конструкторы этих классов отработают за одинаковое количество времени или нет?
    


Ответы

Ответ 1



Исследования показали, что время выполнения конструктора зависит от количества памяти, выделяемой под экземпляр класса. Соответственно, поля и свойства с неявными get/set (как у вас) влияют, а методы и свойства с явными get/set не влияют (память под них в экземпляре не выделяется). using System; using System.Collections.Generic; using System.Diagnostics; namespace ConsoleTest { public class LittleClass { public int Prop0 { get; set; } } public class BigClass { public int Prop1 { get; set; } public int Prop2 { get; set; } public int Prop3 { get; set; } public int Prop4 { get; set; } public int Prop5 { get; set; } } class Program { const int N = 100000000; static void Main(string[] args) { Stopwatch s; int i; LittleClass lc = new LittleClass(); BigClass bc = new BigClass(); /* ------------------------------------------------------------------*/ s = new Stopwatch(); Console.WriteLine("BigClass test..."); s.Start(); for (i = 0; i < N; i++) { bc = new BigClass(); } s.Stop(); Console.WriteLine("t=" + s.ElapsedMilliseconds.ToString()); /* ------------------------------------------------------------------*/ s = new Stopwatch(); Console.WriteLine("LittleClass test..."); s.Start(); for (i = 0; i < N; i++) { lc = new LittleClass(); } s.Stop(); Console.WriteLine("t=" + s.ElapsedMilliseconds.ToString()); /* ------------------------------------------------------------------*/ Console.ReadKey(); } } } Результат (оптимизация включена) По моим расчетам, время инициализации класса описывается формулой t=(2,6*s+43,8)/(10^7) мс где s - суммарный размер типов всех членов, под которые память выделяется (см.выше), с коэффициентом корелляции 0,99. Наличие постоянной составляющей, я полагаю, объясняется наличие служебной информации, под которую выделяется, предположительно, 16 байт. Исследование проводилось на машине с процессором с тактовой частотой 2.33 ГГц. Для вычисления времени, не зависимого от машины, надо эту формулу умножить на тактовую частоту, получая время в "тактах процессора": t = 0,6*s+10,9

Ответ 2



Поскольку нет инициализации свойств, IL-код будет одинаков и разницы в скорости работы скорее всего не будет. IL для MyClass2: .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { .maxstack 8 // [46 9 - 46 26] IL_0000: ldarg.0 // this IL_0001: call instance void [mscorlib]System.Object::.ctor() // [49 9 - 49 10] IL_0006: ret } IL для MyClass: .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { .maxstack 8 // [32 9 - 32 25] IL_0000: ldarg.0 // this IL_0001: call instance void [mscorlib]System.Object::.ctor() // [35 9 - 35 10] IL_0006: ret } // end of method MyClass::.ctor Разница все-таки есть, провел тесты с помощью BenchmarkDotNet. Связано это скорее всего с тем, что для полей автоматических свойств вызывается конструктор по-умолчанию при создании класса. Код программы: using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; namespace ConsoleApp { class Program { static void Main(string[] args) { var summary = BenchmarkRunner.Run(); } } public class ClassCreator { [Benchmark] public MyClass CreateMyClass() { return new MyClass(); } [Benchmark] public MyClass2 CreateMyClass2() { return new MyClass2(); } } public class MyClass { public int Prop { get; set; } } public class MyClass2 { public int Prop1 { get; set; } public int Prop2 { get; set; } public int Prop3 { get; set; } public int Prop4 { get; set; } public int Prop5 { get; set; } } } Результат: BenchmarkDotNet=v0.10.3.0, OS=Microsoft Windows 10.0.14393 Processor=Intel(R) Core(TM) i5-4670 CPU 3.40GHz, ProcessorCount=4 Frequency=3320319 Hz, Resolution=301.1759 ns, Timer=TSC dotnet cli version=1.0.1 [Host] : .NET Core 4.6.25009.03, 64bit RyuJIT DefaultJob : .NET Core 4.6.25009.03, 64bit RyuJIT | Method | Mean | StdDev | |--------------- |---------- |---------- | | CreateMyClass | 2.4932 ns | 0.0185 ns | | CreateMyClass2 | 3.3029 ns | 0.0982 ns |

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

#cpp #алгоритм #оптимизация #производительность


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

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

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

#include 
#include 
#include 
#include 

const long double _10_POWERS[40] = 
{
    1e+0,  1e+1,  1e+2,  1e+3,  1e+4,  1e+5,  1e+6,  1e+7,  1e+9,  1e+10,
    1e+11, 1e+12, 1e+13, 1e+13, 1e+14, 1e+15, 1e+16, 1e+17, 1e+18, 1e+19,
    1e+20, 1e+21, 1e+22, 1e+23, 1e+24, 1e+25, 1e+26, 1e+27, 1e+28, 1e+29,
    1e+30, 1e+31, 1e+32, 1e+33, 1e+34, 1e+35, 1e+36, 1e+37, 1e+38, 1e+39
};    

static inline uint32_t log10_fast(long double x) 
{
    //uint32_t res = 0;    
    int l = 0, r = 40 - 1;

    while (l <= r)
    {
        int mid = l + ((r -l) >> 1);

        if ( x >= _10_POWERS[mid] && x < _10_POWERS[mid + 1] )
        {   return mid; }   

        if (x >= _10_POWERS[mid])
            l = mid;
        else
            r = mid;
    }

    return 0;
};


uint32_t compute(int n, std::vector>& a)
{
    long double x = 0.0f;
    uint32_t s = 0;

    const long double _2_96 = pow(2, 96);
    const long double _2_64 = pow(2, 64);
    const long double _2_32 = pow(2, 32);    

    for (int i = 0; i < n; ++i)
    {
        for (int j = i + 1; j < n; ++j)
        {                       
            x =  _2_96 * (a[i][0] ^ a[j][0]);
            x += _2_64 * (a[i][1] ^ a[j][1]);
            x += _2_32 * (a[i][2] ^ a[j][2]);
            x += (a[i][3] ^ a[j][3]);

            s += log10_fast(x);
        }    
    }

    return 2 * s;
}    

int main(int argc, char const *argv[])
{     
    int n;
    std::cin >> n;
    std::vector> a(n, std::vector(4));

    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < 4; ++j)
        {
            std::cin >> a[i][j];
        }
    }    
    std::cout << compute(n, a) << '\n';
    return 0;
}


Какие есть идеи по поводу оптимизаций тут ?

Может есть другой более быстрый способ вычислить log10 ? Или может дело не в логарифме ?

p.s.

сравнение log10 и log10_fast

uint32_t s = 0;
high_resolution_clock::time_point t1 = high_resolution_clock::now();

for (unsigned i = 0; i < 1e+8; ++i)
{
    s += log10( static_cast(rand()) );
}     
high_resolution_clock::time_point t2 = high_resolution_clock::now();
duration dur = duration_cast( t2 - t1 );
std::cout << dur.count() << '\n';  // 6.374 sec


s = 0;
t1 = high_resolution_clock::now();
for (unsigned i = 0; i < 1e+8; ++i)
{
    s += log10_fast( static_cast(rand()) );
}    
t2 = high_resolution_clock::now(); 
dur = duration_cast( t2 - t1 );
std::cout << dur.count() << '\n'; // 5.907 sec

    


Ответы

Ответ 1



Мне кажется, что главная проблема заключается в том, что вы много раз вызываете функцию log10. Давайте распишем сумму логарифмов как логарифм произведения: Если чисел немного, так что их произведение не вызовет переполнение, то можно так и посчитать. Другая проблема заключается в использовании дробных чисел, они не так быстро перемножаются, как целые. Хочу предложить приближённое решение в целых числах. Заметим, что если, например, a[i][1] xor a[j][1] не равно нулю, то a[i][3] xor a[j][3] и a[i][4] xor a[j][4] можно не считать, так как их добавка к xor'у будет очень маленькой. Рассмотрим следующий алгоритм: Для каждой пары (i, j), приближённо считаем A_i xor A_j в виде x * 2^k, где x, k --- некоторые целые числа, причём 0 <= x < 2^32. Перемножаем полученные значения следующим образом: (x1 * 2^k1) * (x2 * 2^k2) = (x1 * x2) * 2^(k1+k2) = x * 2^(k1+k2), причём 0 <= x <= 2^64. Представляем x в виде x=y * 2^m', причём 0 <= y < 2^32. Итак, (x1 * 2^k1) * (x2 * 2^k2) = y * 2^(k1+k2+m) По факту мы посчитали не десятичный логарифм, а двоичный, чтобы получить десятичный логарифм, нужно домножить на log_10(2). Собственно, код: #include #include #include #include #include "/home/dima/C++/debug.h" using namespace std; double compute(const vector &a) { static const uint64_t two_power_32 = 1ull << 32; int n = a.size(); // текущий накопленный результат равен value * 2^power_index uint64_t value = 1; int power_index = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < i; ++j) { uint64_t xor1 = a[i][0] ^a[j][0]; uint64_t xor2 = a[i][1] ^a[j][1]; uint64_t value_current; if (xor1 == 0) { value_current = xor2; } else if (xor1 >= two_power_32) { value_current = xor1; power_index += 64; } else { assert(0 <= xor1 && xor1 < two_power_32); value_current = (xor1 << 32) + (xor2 >> 32); power_index += 32; } while (value_current >= two_power_32) { value_current /= 2; ++power_index; } assert(0 <= value_current && value_current < two_power_32); assert(0 <= value && value < two_power_32); value *= value_current; while (value >= two_power_32) { value /= 2; ++power_index; } } } // result = log10(value * 2^power_index) // result = log10(value) + log10(2^power_index) // result = log10(value) + power_index * log10(2) double result = log10(value) + power_index * log10(2); return result * 2; } int main() { freopen("input.txt", "r", stdin); int n; cin >> n; vector a(n); for (int i = 0; i < n; ++i) { for (int j = 0; j < 2; ++j) { uint32_t ai1, ai2; cin >> ai1 >> ai2; a[i][j] = (uint64_t(ai1) << 32) + ai2; } } cout << compute(a) << endl; return 0; } К сожалению, я не сравнивал производительность, но я искренне верю, что это работает быстрее, чем n^2 раз вычислять log10. Обновление: я тут потестировал, при n=5000 моя реализация чуть медленнее вашей оригинальной. Всё дело в этих циклах: while (value >= two_power_32) { value /= 2; ++power_index; } Их можно переписать разными способами, вот вариант для GCC: static const uint64_t two_power_32 = 1ull << 32; inline void divide_until_less_then_two_power_32(uint64_t &value, int &power_index) { // Эта функция эквивалентна этим строчкам: // while (value >= two_power_32) { // value /= 2; // ++power_index; // } if (value < two_power_32) { return; } int power_index_delta = 32 - __builtin_clzll(value); power_index += power_index_delta; value >>= power_index_delta; assert(0 <= value && value < two_power_32); } Полный код: #include #include #include #include using namespace std; #include "/home/dima/C++/debug.h" static const uint64_t two_power_32 = 1ull << 32; inline void divide_until_less_then_two_power_32(uint64_t &value, int &power_index) { // Эта функция эквивалентна этим строчкам: // while (value >= two_power_32) { // value /= 2; // ++power_index; // } if (value < two_power_32) { return; } int power_index_delta = 32 - __builtin_clzll(value); power_index += power_index_delta; value >>= power_index_delta; assert(0 <= value && value < two_power_32); } double compute(const vector &a) { int n = a.size(); // текущий накопленный результат равен value * 2^power_index uint64_t value = 1; int power_index = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < i; ++j) { uint64_t xor1 = a[i][0] ^a[j][0]; uint64_t xor2 = a[i][1] ^a[j][1]; uint64_t value_current; if (xor1 == 0) { value_current = xor2; } else if (xor1 >= two_power_32) { value_current = xor1; power_index += 64; } else { assert(0 <= xor1 && xor1 < two_power_32); value_current = (xor1 << 32) + (xor2 >> 32); power_index += 32; } divide_until_less_then_two_power_32(value_current, power_index); assert(0 <= value_current && value_current < two_power_32); assert(0 <= value && value < two_power_32); value *= value_current; divide_until_less_then_two_power_32(value, power_index); } } // result = log10(value * 2^power_index) // result = log10(value) + log10(2^power_index) // result = log10(value) + power_index * log10(2) double result = log10(value) + power_index * log10(2); return result * 2; } int main() { freopen("input.txt", "r", stdin); int n; cin >> n; vector a(n); for (int i = 0; i < n; ++i) { for (int j = 0; j < 2; ++j) { uint32_t ai1, ai2; cin >> ai1 >> ai2; a[i][j] = (uint64_t(ai1) << 32) + ai2; } } cout << compute(a) << endl; return 0; } Если я всё правильно посчитал, то эта версия работает в два раза быстрее. Обновление 2: исправил ошибку (добавил строчку power_index += 32;)

Ответ 2



У меня не готовый код, но... Я бы делал так - как минимум для повышения точности (потому что ваш double никак не отловит точное значение, например, того же 1039). Сделал бы 128-разрядное число - как unsigned long a[4]; Далее, вот такую табличку степеней 10 в таком точном представлении - unsigned long p10[39][4] = { { 0x00000001, 0x00000000, 0x00000000, 0x00000000 }, // 1 { 0x0000000a, 0x00000000, 0x00000000, 0x00000000 }, // 10 { 0x00000064, 0x00000000, 0x00000000, 0x00000000 }, // 100 { 0x000003e8, 0x00000000, 0x00000000, 0x00000000 }, // 1000 { 0x00002710, 0x00000000, 0x00000000, 0x00000000 }, // 10000 { 0x000186a0, 0x00000000, 0x00000000, 0x00000000 }, // 100000 ... { 0x00000000, 0x098a2240, 0x5a86c47a, 0x4b3b4ca8 }, // 10000000000....0 { 0x00000000, 0x5f655680, 0x8943acc4, 0xf050fe93 }, // 10000000000....00 (полностью - здесь: http://vpaste.net/RljZF). Ну, или если удобнее - то наоборот, от старшего к младшему. Далее я бы написал простую функцию сравнения вот таких 128-разрядных чисел - очень просто, начиная со старшего - и вперед... И логарифмировал бы без всяких переводов в doubleы - аналогично вашему поиску при взятии логарифма. Причем надо еще померить, дает ли что-то при таком небольшом количестве бинарный поиск или нет. Можно поиграться, начиная с поиска по старшему элементу. При несовпадении - сразу определяется логарифм, при совпадении - переходим к следующему и так далее... Написать у вас, я думаю, проблем не составит. Плюсы - точность, не используется арифметика с плавающей точкой. Update о точности... Рассмотрим значение 0x00000000 0x00000000 0x00038d7e 0xa4c67FFE, т.е. число 999999999999998. Очевидно, что значение его логарифма, floor до целого - 14. Теперь вычисляем ваше значение - double x = pow(2,32)*0x00038d7e + 0xa4c67FFE; VC++ 2015 дает для double x = pow(2,32)*0x00038d7e + 0xa4c67FFE; printf("%.10lf\n",x); printf("%.10lf\n",log10(x)); int l = log10(x); printf("%d\n",l); следующие результаты: 999999999999998.0000000000 15.0000000000 15 Вы можете возразить, что логарифм вы считаете не так... но проверьте сами, что число 0x00000000 0x00000000 0x0DE0B6B3 0xA763FFF8 - т.е. 999999999999999992 - даст при вычислении вашим способом - с умножением на pow(2,...) - число double x == 1000000000000000000.00000 Так что значения логарифмов у вас все равно для некоторых чисел окажутся неверными.

среда, 29 января 2020 г.

Различие в работе эмуляторов iPad и Android

#эмулятор #ipad #android #objective_c #производительность


Почему приложение (Objective-C) при запуске на эмуляторе для iPad работает быстрее,
чем на самом устройстве, а на эмуляторе для Android (Java приложение) работает медленнее,
чем на устройстве?    


Ответы

Ответ 1



Потому что в случае с iPad это не эмулятор, а симулятор. Эмулятор Android эмулирует ARM на x86, iPad же работает сразу на x86 (поэтому, например, нельзя в симуляторе iPad запустить приложение собранное для устройства). Соответственно, у любого современного компьютера ресурсов больше, чем у iPhone/iPad, поэтому приложение в симуляторе работает быстрее, чем на устройстве. Эмулятору же Android'а недостаточно ресурсов компьютера (я ещё не видел компьютера, на котором эмулятор не тормозил бы), он и без всяких приложений заметно медленнее телефонов/планшетов.

Ответ 2



Рекомендую прочитать статью на хабре о порте android x86: Как ускорить эмулятор Android на 400%

Ответ 3



У андроида именно эмулятор т.е. имитация работы процессора мобильного устройства на х86 компа, все действия при работе выполняются с дополнительным переводом инструкций одного проца под другой, а у iPhone это всего лишь симмуляция - библиотеки и какая-то урезанная версия оси откомпилированы под х86_64 и работают в нативном коде

Использование регистровых переменных сравнения в циклах Си

#c #оптимизация #производительность


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

double array[100ULL];

size_t length = 20ULL;

for(register size_t i = 0ULL; i < length - 1ULL; i++){
    array[i] = (double)i;
}


В этом примере индексная переменная i - регистровая. Но сравнение внутри for происходит
с переменной length, которая НЕ объявлена как регистровая. Помимо прочего перед сравнением
происходит математическая операция. 

Хочу для себя прояснить пару-тройку моментов:


Будет ли откомпиллированная программа при каждой итерации цикла
производить эту математическую операцию (вычитание единицы из
меременной length) раз за разом или эта операция будет произведена
только один раз перед началом цикла?
Догадается ли компилятор оптимизировать переменную length в
регстровую?
Какой код будет правильнее с точки зрения производительности и
однозначности для компилятора: тот, что выше, или нижеприведённый?
И есть ли вообще разница для современных компиляторов?


Второй вариант кода, в котором обе сравниваемые переменные регистровые и операция
вычитания однозначно производится перед циклом:

double array[100ULL];

register const size_t length = 10ULL - 1ULL;

for(register size_t i = 0ULL; i < length; i++){
    array[i] = (double)i;
}


Трансляция кода в asm ситуацию не прояснила, поэтому задаю напрямую.
    


Ответы

Ответ 1



Забудьте об этом слове register, оно давно не имеет никакого смысла. Ответы на вопросы 1-2 зависят от компилятора; наверное, можно найти старый тупой компилятор, который не сумеет выполнить такие простые оптимизации.. А разумный - вообще может использовать какие-нибудь векторные команды процессора или что еще. Так что ответ на вопрос 3 - нет, такой простой цикл будет оптимизирован любым более-менее разумным компилятором. Кстати, VC++2017 просто развернул этот цикл в mov eax, 1 xor ebx, ebx cvtsi2sd xmm1, rbx movsd QWORD PTR array$[rsp], xmm1 xorps xmm1, xmm1 cvtsi2sd xmm1, rax mov eax, 2 cvtsi2sd xmm2, rax mov eax, 3 ..... movsd QWORD PTR array$[rsp+128], xmm2 movsd QWORD PTR array$[rsp+136], xmm1 xorps xmm1, xmm1 cvtsi2sd xmm1, rax movsd QWORD PTR array$[rsp+144], xmm1 ругнувшись при этом на слово register как давно не поддерживаемое... Результат видите сами. Очень старенький OpenWatcom пошел по циклическому пути - L$1: cmp eax,13H jae L$2 mov dword ptr 320H[esp],eax xor ecx,ecx add edx,8 mov dword ptr 324H[esp],ecx inc eax fild qword ptr 320H[esp] fstp qword ptr -8[esp+edx] jmp L$1 но, как видите, вычислив 19 сразу, и работая только с регистрами...

Ответ 2



gcc 6 сосчитал при компиляции и использовал сравнение счётчика с константой: for(register size_t i = 0ULL; i < length - 1ULL; i++){ 583: 31 c0 xor eax,eax int main (void) { 585: 53 push rbx 586: 48 81 ec 20 03 00 00 sub rsp,0x320 58d: 48 89 e3 mov rbx,rsp array[i] = (double)i; 590: 66 0f ef c0 pxor xmm0,xmm0 594: f2 48 0f 2a c0 cvtsi2sd xmm0,rax 599: f2 0f 11 04 c3 movsd QWORD PTR [rbx+rax*8],xmm0 for(register size_t i = 0ULL; i < length - 1ULL; i++){ 59e: 48 83 c0 01 add rax,0x1 5a2: 48 83 f8 13 cmp rax,0x13 5a6: 75 e8 jne 590 5a8: 4c 8d a3 98 00 00 00 lea r12,[rbx+0x98] } Ровно то же самое он выдал без объявления i как register.

Ответ 3



Не надейтесь на компилятор, а оптимизируйте сами. Меньше пользуйтесь глобальными переменными. Самый популярный цикл в коде: double array[100ULL]; size_t length = 20ULL; for(size_t i = 0ULL; i < length - 1ULL; i++){ array[i] = (double)i;} пользуется глобальной переменной lenght. Этот доступ вы можете сами убрать лично: double array[100ULL]; size_t length = 20ULL; for(size_t i = length; i > 0; ){ -- i ; array[i] = (double)i;} В этом цикле будет только быстрая проверка на ноль. Уже быстрее. Дальше постоянно используется массив array с индексацией (сложение указателя с числом умноженный на размер double). Этот лишний код все убирают используя цикл по указателю. double array[100ULL]; size_t length = 20ULL; double * arrayj = &(array[length]) ; for(size_t i = length; i > 0; ){ -- i ; -- arrayj ; (*arrayj) = (double)i;} В данном цикле используется только вычитание и сравнение с нулём. Попробуйте все примеры переносить в ассемблер и сравнить коды. Тогда вы поймёте кто плохо оптимизирует программы : компилятор или вы.

вторник, 28 января 2020 г.

Управление памятью в C#

#c_sharp #производительность #memory


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


Ответы

Ответ 1



Можно, но в ограниченных пределах, а вообще не нужно. Если используете классы, реализующие IDisposable, не забывайте про Dispose() (либо про конструкцию using)

Ответ 2



Я перечитал статью, на которую указывает ТС. Статья датирована 2006-м годом, с тех времён алгоритмы сборки мусора сильно изменились. Я скомпилировал и запустил проекты на тестовой 32-битной машине. (Заметьте, что на 64-битной машине гораздо больше доступного адресного пространства, и сборщик мусора будет работать гораздо меньше. У кого под рукой 64-битная машина, проверьте, пожалуйста, сравните и отпишитесь!) C++: В исходниках исправил 10000 на PermanentObjectsCount в строке 102, была явная ошибка. Скомпилировал Release, запуск с командной строки 5 раз, с разными опциями. Параметры теста: PermanentObjectsCount: количество «мёртвых» объектов, которые лишь занимают память и мешают алгоритмам аллокации QUICK_HEAP_OPERATOR_NEW: макрос, подключающий кастомный аллокатор памяти Результаты: PermanentObjectsCount = 1, QUICK_HEAP_OPERATOR_NEW включено: Timestamp: 2.332243/2.285392/2.310033/2.329071/2.362159, среднее 2,3237796 ObjCount=10001001 PermanentObjectsCount = 1, QUICK_HEAP_OPERATOR_NEW выключено Timestamp: 3.123940/3.130930/3.113295/3.139964/3.104077, среднее 3.1224412 ObjCount=10001001 PermanentObjectsCount = 10 * 1000 * 1000, QUICK_HEAP_OPERATOR_NEW включено: Timestamp: 7.175451/7.234413/7.241763/7.197331/7.226205, среднее 7.2150326 ObjCount=20001000 PermanentObjectsCount = 10 * 1000 * 1000, QUICK_HEAP_OPERATOR_NEW выключено: Timestamp: 8.823886/8.751605/8.908837/8.825099/8.806487, среднее 8.8231828 ObjCount=20001000 Теперь C#: Переконфигурировал проект на использования .NET 4.5. Скомпилировал Release, запуск с командной строки 5 раз, с разными опциями. Параметры теста: PermanentObjectsCount: количество «мёртвых» объектов, которые лишь занимают память и мешают сборщику мусора Установки сборщика мусора в app.config по умолчанию ( отключено): PermanentObjectsCount = 1 00:00:01.6406347/1.5898657/1.6479692/1.6528195/1.5796367, среднее 1.62218516 Количество созданных объектов: 10001001 Количество сборок мусора поколения 0: 571 Количество сборок мусора поколения 1: 145 Количество сборок мусора поколения 2: 3 PermanentObjectsCount = 10 * 1000 * 1000 00:00:15.5065372/16.4615645/16.5447826/15.4274263/16.3169154, среднее 16.0514452 Количество созданных объектов: 20001000 Количество сборок мусора поколения 0: 758 Количество сборок мусора поколения 1: 256 Количество сборок мусора поколения 2: 10 Запросил серверный сборщик мусора в app.config (): PermanentObjectsCount = 1 00:00:01.9130864/1.8731294/1.8649931/1.6231794/1.8787851, среднее 1.83063468 Количество созданных объектов: 10001001 Количество сборок мусора поколения 0: 243 Количество сборок мусора поколения 1: 43 Количество сборок мусора поколения 2: 3 PermanentObjectsCount = 10 * 1000 * 1000 00:00:14.2030724/13.8427775/14.0393846/14.5345637/14.2312238, среднее 14.1702044 Количество созданных объектов: 20001000 Количество сборок мусора поколения 0: 249 Количество сборок мусора поколения 1: 54 Количество сборок мусора поколения 2: 5 Выводы: Сборщик мусора тогда и сборщик мусора сейчас — разные вещи. В довольно редком для реальных программ случае наличия 10 миллионов статических объектов сборщик мусора .NET существенно ускорился, и теперь отстаёт от нативного аллокатора не в 86 раз, а всего вдвое. Серверный аллокатор немного лучше справляется с нагрузкой. В случае, когда работа управления памятью не затруднена, аллокатор .NET выигрывает более чем в полтора раза. Кастомный аллокатор для нативного кода ускоряет работу, но я бы не стал использовать непроверенный аллокатор в серьёзном коде: был бы он лучше встроенного в C++, уж разработчики компилятора без сомнений заменили бы свой аллокатор на этот.

Ответ 3



Если вы работаете с COM объектами, то им нужно вызывать метод Marshal.ReleaseComObject

воскресенье, 26 января 2020 г.

Оптимизирует ли это код?

#ресурсы #c #оптимизация #cpp #производительность


Имеется 2 цикла:
for (i = 0; i < a + 1; i++) { }

и
b = a + 1;
for (i = 0; i < b; i++) { }

Вычисляет ли процессор по новой при каждом прохождении цикла значение a + 1?
То есть, по другому говоря, с точки зрения экономии ресурсов код №2 лучше №1?    


Ответы

Ответ 1



Вот результат gcc-4.6.1 на x86-64: #include int main() { int a = 10; for (int i = 0; i < a + 1; i++) { printf("iteration: %d\n", i); } } Собираем gcc -O2 -g -Wall -std=c99 -o test test.c В gdb видим вот такое: 0x400440
push %rbx 0x400441 xor %ebx,%ebx 0x400443 nopl 0x0(%rax,%rax,1) 0x400448 mov %ebx,%edx 0x40044a xor %eax,%eax 0x40044c mov $0x40063c,%esi 0x400451 mov $0x1,%edi 0x400456 add $0x1,%ebx 0x400459 callq 0x400430 <__printf_chk@plt> 0x40045e cmp $0xb,%ebx 0x400461 jne 0x400448 0x400463 xor %eax,%eax 0x400465 pop %rbx 0x400466 retq Т.е. видим, что компилятор сам все прекрасно довел до константы 11 (см. main+30). С другой стороны, вызовы функций стоит делать руками. Вот такое gcc уже не потянул, printf в функции сбил его с толку: #include int foo() { printf("called foo\n"); return 10; } int main() { for (int i = 0; i < foo() + 1; i++) { printf("iteration: %d\n", i); } } Там уже call на каждую итерацию. Premature optimization is the root of all evil —Donald Knuth

Ответ 2



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

Ответ 3



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

пятница, 24 января 2020 г.

Почему хранимая процедура при вызове из C# кода выполняется в 18 раз дольше чем на сервере?

#c_sharp #sql #производительность


Есть хранимая процедура в MS SQL базе. В SQL Server Managment Studio она выполняется
всего за 10 секунд. Но если я запускаю эту же процедуру из C# (ASP.NET), то время выполнения
почему-то увеличивается до 180 секунд!

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

using (SqlConnection connection = new SqlConnection())
{
   connection.ConnectionString = "XXXX";
   string procedure = "dbo.sp_ProcedureName";

   using (SqlCommand command = new SqlCommand(procedure, connection))
   {
      command.CommandType = CommandType.StoredProcedure;
      command.CommandTimeout = XXX;

      connection.Open();
      command.ExecuteNonQuery();
   }
}


В чем может быть причина? Почему так сильно увеличилось время выполнения и как это
можно исправить или минимизировать?

p.s. База на том же сервере стоит, что и код, при обращении к удаленному, длительность
увеличивается секунд на 12. 
    


Ответы

Ответ 1



По совету пользователя Alexander Petrov начал знакомиться со статьей Slow in the Application, Fast in SSMS, внес небольшое изменение в хранимую процедуру и время выполнения стало практическое такое же как во время выполнения в SQL Server Managment Studio, сократившись со 180 сек. до 13. Попробую объяснить, как я это понял, и если ошибаюсь, поправьте пожалуйста. Итак, тормозило все из-за того, что в внутри основной процедуры сначала вычислялся параметр, а потом этот параметр использовался для вызова других 5 процедур. Удалось отказаться от вычисляемого параметра в пользу константы и SQL смог оптимально построить план выполнения для вызываемых процедур. А в Managment Studio это значения вычислялось в процессе выполнения и план так же создавался "на лету", отсюда и более быстрое выполнение. Буду копать дальше, т.к. не всегда будет возможность отказаться от вычисляемого параметра.

Влияет ли блок try на производительность кода в С++, если исключений не возникает?

#cpp #исключения #производительность #try_catch


Прошу прощения если я задаю глупый вопрос, но мне нужно знать точно. Если я пишу
подобный код на С++:

int main() try {
.....
}
catch (const std::bad_alloc& e) {
    ...
}
...
catch (...) {
    cout << "Was throw exception" << endl;
    system("pause");
}


Будет ли скомпилированный в блоке try код отличным от такого же кода без блока try?
Может ли блок try оказывать какое либо влияние на производительность помимо ситуаций
когда происходят исключения? 
    


Ответы

Ответ 1



Откровенно говоря, не очень понятно, как именно провести эксперимент... Так что это не более чем иллюстрация, по которой трудно делать выводы. На такой функции-пустышке на VC++ 2017 попробовал - int main(int argc, const char * argv[]) { { muTimer mt; for(int i = 0; i < 1000000; ++i) { try { f(i); } catch(...) {} } cout << mt.stop().duration() << endl; } { muTimer mt; for(int i = 0; i < 1000000; ++i) { f(i); } cout << mt.stop().duration() << endl; } } int total; void f(int i) { total += i; } Получилось примерно 4 миллисекунды на 0.6. Но, думаю, что при серьезных функциях соотношение будет куда ближе к 1:1 :)

воскресенье, 12 января 2020 г.

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

#highload #производительность #limits #linux


Есть linux, пользователи пользуются ресурсами данной машины. Но делают это, не понимая,
что сервер один на всех )) и грузят его максимально тем самым мешая друг другу.Дано:
Виртуалка CentOS6.3 x64, 4GB RAMВот имею примерно такой вывод atop по самым пожирателям.NPROCS
  SYSCPU    USRCPU   VSIZE    RSIZE   RDDSK    WRDSK  RNET   SNET   CPU  RUID 1/155
   0.05s     1.88s   14.6G   877.6M      0K       0K     ?      ?   39%   user156 
  0.06s     0.45s   12.8G   603.1M      0K      32K     ?      ?   19%   user257  
 0.01s     0.04s   12.0G   594.8M      0K      24K     ?      ?   11%   user3 3   
0.00s     0.00s  401.9M    6632K      0K       0K     ?      ?    0%   user4Хочу в
/etc/sucurity/limits.conf зарезать немного их аппетиты, но пока выходит не очень.хочу
кол-во процессов на юзера сделать soft/hard - 50/65, уменьшить значение VSIZE (virtual
memory?), RSIZE (real memory?), и чтобы не жрали USRCPU (значение задается в минутах,
т.е. как я понимаю сейчас значение 1.88s = это 1 минута 88сек??? а почему не 2.28s
в любом случае процессор надо лимитировать)Все пользователи входят в группу @domainusers,
при попытке дать лимиты этой группе понял что это неверно т.к. все лимиты делятся пропорционально
на зашедших в систему юзеров и в итоге хватает ресурсов только на одного.. ))Остается
только пологинно перечислять всех в limits.confuser1  soft         nproc   50user1
 hard         nproc   65user1  soft         cpu     1user1  hard         cpu     2user1
 soft         as   1500000user1  hard         as   2000000user1  soft         rss 
400000user1  hard         rss  500000....user2 ....p.s. "as" как говорит (гугл) это
и есть виртуальная памятьВсе ли я верно делаю?P.S. эксперименты с limits.conf не внушают
мне надежды. Когда лимиты подходят к своему пределу система становиться нестабильна
и доходит до выпадания в корку. К примеру тупо не хватает свободных процессов, чтобы
открыть вкладку и, к примеру, браузер рушиться в этот момент какая-нибудь дебаг-тулуза
хочет отправить отчет и тоже валится и все крайне нестабильно начинает существовать.P.P.S.
Начал разбирать тему с cgroups/cgrules - этот механизм вроде более жизнеспособен.    


Ответы

Ответ 1



Вариант: PAM и его limits.conf

Ответ 2



cgroup самый правильный и скорее всего единственный инструмент в linux-e для этого

суббота, 11 января 2020 г.

Какой вариант проверки условия наиболее производителен?

#java #производительность #if #benchmark


int a = 0;
String b = "0";



вариант:

if(String.valueOf(a).equals(b)) {}

вариант:

if(Integer.valueOf(b) == a) {}


    


Ответы

Ответ 1



Немного результатов измерений: # JMH version: 1.20 # VM version: JDK 1.8.0_151, VM 25.151-b12 # CPU: Intel Core i7-6700 3.40 GHz (4 cores, 8 threads) Benchmark Mode Cnt Score Error Units MyBenchmark.stringValueOfInteger avgt 15 26.363 ± 0.112 ns/op MyBenchmark.integerValueOfString avgt 15 13.990 ± 0.129 ns/op MyBenchmark.integerToString avgt 15 26.247 ± 0.260 ns/op MyBenchmark.integerPlusString avgt 15 15.541 ± 0.227 ns/op Код бенчмарка: @State(Scope.Benchmark) @BenchmarkMode(Mode.AverageTime) @OutputTimeUnit(TimeUnit.NANOSECONDS) public class MyBenchmark { private int a; private String b; @Setup(Level.Invocation) public void setup() { a = 0; b = "0"; } @Benchmark public void stringValueOfInteger(Blackhole blackhole) { blackhole.consume(String.valueOf(a).equals(b)); } @Benchmark public void integerValueOfString(Blackhole blackhole) { blackhole.consume(Integer.valueOf(b) == a); } @Benchmark public void integerToString(Blackhole blackhole) { blackhole.consume(Integer.toString(a).equals(b)); } @Benchmark public void integerPlusString(Blackhole blackhole) { blackhole.consume(("" + a).equals(b)); } } Пояснения к результату: самый быстрый вариант (~14 нс на операцию): Integer.valueOf(b) == a (да, Integer.parseInt(b) == a) показывает примерно тот же результат) чуть менее быстрый (~15.5 нс на операцию): ("" + a).equals(b) разница в ~10 нс, пожалуй, не особо важна для вашего приложения

Ответ 2



Пример кода: int[] a = new int[1000000]; String[] b = new String[1000000]; for(int i = 0; i < 1000000; i++){ a[i] = i; b[i] = "8"; } long start = System.currentTimeMillis(); for(int i = 0; i < 1000000; i++){ if(String.valueOf(a[i]).equals(b[i])){ } } System.out.println(System.currentTimeMillis() - start); start = System.currentTimeMillis(); for(int i = 0; i < 1000000; i++){ if(Integer.valueOf(b[i]) == a[i]){ } } System.out.println(System.currentTimeMillis() - start); Результаты в мс (4 запуска): 1 вариант: 203 | 156 | 484 | 140 2 вариант: 32 | 47 | 47 | 32