Страницы

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

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

среда, 15 апреля 2020 г.

Оптимизация php и mysql

#php #оптимизация #mysql

                    
И опять вопросы по работе с mysql и php:
1) Есть два запроса:
mysql_query('select * from `table` where `id`='.$id.' and `user_id`='.$_SESSION['id']);
mysql_query('select * from `table` where `id`='.$id.' and `user_id`='.$_SESSION['id'].'limit
0, 1');

Первичный ключ - id. Имеет ли смысл писать limit 0, 1 в конце запроса или это не
ускорит запрос?
2) В случае уже полученных данных:
$ar = array();
$res = mysql_query('select * from `table` where `id`<30');
while($ar = mysql_fetch_assoc($res)){}

Что лучше использовать: mysql_num_rows($res) или sizeof($ar) ?
3) Зачем нужен mysql_fetch_array, если есть mysql_fetch_assoc и mysql_fetch_row?
По идее, эти две функции по отдельности работают быстрее?
4) При организации, допустим, блогов, разумно ли вынести посты блогов в отдельные
файлы, а комментарии оставить в БД? Просто тогда получается, что при выводе последних
блогов одновременно будет вестись работа как с БД, так и с файлами, что мне не нравится.
Тем более, что анонс все равно придется писать в БД.
5) Определение глобальных переменных в функции - довольно медленная вещь. Можно ли
ускорить работу функции, загнав ссылки на нужные переменные в массив и определив в
функции глобальным только новый массив? То есть, было:
function f() {
    global $ar1, $ar2, $ar3;
}

Стало:
$all = array('ar1' => &$ar1, 'ar2' => &$ar2, 'ar3' => &$ar3);
function f() {
    global $all;
    $ar1 = $all['ar1'];
}

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


Ответы

Ответ 1



1) Имеет смысл сделать так: mysql_query('select * from `table` where `id`='.$_SESSION['id'].' limit 0,1'); Только исключите в запросах *, тогда будет экономия уже не на спичках :) 2) Согласен с @mozzart 3) mysql_fetch_array работает быстрее, чем assoc, т.к. добавляет только индексы в массив. 4) Согласен с @mozzart 5) Скорее вот так быстрее: function (&$ar1, &$ar2, &$ar3) { ... } Пример: class Api { private $db; public function test() { // Этой функции Вы собирались передать get и post $this->db; // объект бд } } или так: class Api extends DB { public function test() { $this->query(); } } class DB { public function query() { ... } }

Ответ 2



1) Смысл есть 2) Если нужно просто получить количество записей, то логичнее и оптимизированней использовать mysql_num_rows, если все записи получены в массив, то тогда конечно - sizeof 3) Не проверял... 4) Если хранение файлов подразумевает организацию кэширование, то тогда да. Грубо говоря, работа с файлами происходит намного быстрее чем с БД 5) Первый вариант быстрее

понедельник, 13 апреля 2020 г.

Как вывести информацию о нагрузке сервера?

#php #оптимизация

                    
Здравствуйте, у меня два вопроса. 

Вопрос первый - вот "гулял" по интернету и увидел следующие. На одном сайте в низу
его страницы была информации как загрузка сайта грузит систему. Там было написано процент
и оперативную память какую он берет на себя (там было около 22 мб). К сожалению, ссылку
куда-то дел и не могу найти куда. Суть вопроса, есть ли такие функции в php, которые
показывает нагрузку, или они через microtime() сделали условие, при котором будет от
1-100% показываться? 
Второй вопрос: видел, что только в классах объявляют переменную (БЕЗ УКАЗАНИЕ ТИПА),
нельзя ли как-то сделать так, чтобы указывать тип изначально, как на языке С++...
    


Ответы

Ответ 1



1.1. Средняя загрузка процессора. В комментариях вроде есть аналог для серверов на WIN. 1.2. Пик выделенной памяти под ПШП, работает только в *никс. 2. PHP не типизированный язык, задавать типы в нем нельзя.

Java I/O для объёмных данных

#оптимизация #файлы #java

                    
Нужен совет опытных по поводу производительности Java I/O и много другого. Задачка
такая - в нескольких потоках клиент шлет http post запрос весом до 1 мб каждый, в ответ
приходят еще больший по весу (2-4 мб) ответ в формате JSON, который нужно спарсить
и аккуратно завернуть в .csv файл. Количество запросов может доходить до десятков тысяч,все
ответы сервера пишутся в 1 файл. Сервер один и тот же. (что делать с Keep-Alive ? )
. Соответственно возникает вопрос - а какие инструменты при этом использовать ?

Соединение: HttpClient / HttpUrlConnection /Netty/etc
Чтение ответа : BufferedReader/Scanner/etc
Чем Json парсить ? jackson/gson/simple json
Чем писать в файл, и нужно ли вообще писать во время работы или же лучше кидать в
какой нибудь ArrayList, потом спарсить и записать.

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


Ответы

Ответ 1



Здравствуйте @Михаил М. сказать, что Вы как-то смутно задали вопрос это тоже самое что вообще ничего не сказать). производительности Java I/O - во первых уточнили бы какую яву собираетесь использовать. в любом случае у Вас будет многопоточность сразу советую смотреть в сторону java concurrency apis. думаю появится много проблемм и ожиданий в очередях пока Вы будете парсить такие обьемы а потом еще и записывать в один файл. - это какая-то жесть(возможно я не правильно понял задачу) тем более что запросов до десятков тысяч. что значит что делать с keep-alive ?, если я правильно понимаю у каждого request/response - свое время жизни и регулироваться они будут самостоятельно. ну да и по порядку: 1 - для запросов мне в мобильных разработках помогла и хорошо себя зарекомендовала вот эта либа http-request - там сразу на страничке все описано в доль и поперек 2 - здесь тоже все может выполнить предыдущая либа. 3 - gson - себя зарекомендовал с очень хорошей стороны, хоть и не без косяков)) 4 - я не понимаю почему все в один файл, возможно стоило вначале описать функциональность которую Вы хотите в итоге добиться, я бы хронил уже готовые для записи .csv в мапке и завел бы отдельный поток который бы и записывал их поочередно. это спосет Вас от многопоточного доступа к файловой системе. и позволит как минимум всем потокам по очереди получать право на запись в мапку, и постоянное право у записывающего потока на чтение из нее. касаемо первых двух пунктов могу сказать еще только что если пользоваться стандартными средствами то я бы использовал BufferedInputStream и HttpUrlConnection

Ответ 2



1.) Для объемных файлов конкретный api особо не влияет на скорость передачи, все упрется в возможности железа. Заморачиваться с nio стоит, если есть например много легких соединений, и есть проблемы с переключениями контекстов. Так что выбирайте удобный, отлаженный api. Я бы взял apache http client 4. 2) BufferedReader - хорошее решение 3) Я бы взял jackson - мощный и быстрый. Gson скорее более специализированное решение для конвертации между бинами и json. 4) Писать лучше во время работы из другого потока, будет pipelinig. И запись конечно должна быть буферизованной, например через BufferedWriter. Keep alive лучше выключить. Файлы большие, время установки соединений ничтожно меньше времени передачи. Только морока будет с настройкой сервера. Еще советую на клиенте и сервере установить размеры tcp буфферов в один мегабайт, алгоритм нигла оставить включенным, scaling tcp окна тоже оставить включенным. И еще общий совет: делай каждую стадию отдельной группой потоков, потоки соединяй очередями. Это называется SEDA. Так ты сможешь получить максимальную пропускную способность. Кстати, увеличение длин очередей увеличивается пропускную способность, но ухудшает latency. Имей в виду. Автор ответа - Денис Боровиков, [http://vk.com/id36765][1]

MODx Revo ускорение

#modx #веб_программирование #оптимизация #revo

                    
Вопрос про ускорение и оптимизацию. Сейчас у меня сайт близится к окончанию на Modx
revo. Но страницы бывает подгружаются по 5-6 секунд, если без кеширования, с кешированием
за 2 сек. Посещения - 0. Ресурсов штук 30. Использовал стандартные снипеты. Но не могу
понять, почему сайт может так тормозить даже без продакшна. Писал сайт на EVO, летает,
даже быстрее того когда на рево включен кэш. Может из-за того что я использовал там
самописные сниппеты? Есть ли какая - нибудь волшебнная кнопка, которая позволит увеличить
скорость сайта в несколько раз? (извиняюсь за детский сад))
Понимаю, нужно продумывать ускорение на месте в каждом случае по разному. Но все
таки ожидаешь от нового движка с новостным сайтом, что он не будет так тупить без особой
нагрузки, даже с учетом мелких допущенных ошибок в коде    


Ответы

Ответ 1



"Использовал стандартные снипеты" - стандартные снппеты ооочень медленные. Хочешь скорости - используй pdoTools. Ну и pthumb вместо phpthumbof. Для 90% случаев - этого хватает за глаза, если нет - ищи сниппеты которые тормозят и разбирайся конкретно с ними.

Ответ 2



Есть некоторые проблемы со скоростью, соглашусь и с Vasis. стандартные сниппеты ооочень медленные Мне помог переезд БД на SSD. Значительно ускорилась работа. Требует вложений в хостинг. Как более дешевый, но трудноватый вариант — установить кеширующий nginx

Ответ 3



В первую очередь проверь, не вызываются ли у тебя сниппеты рекурсивно (getResources для списка новостей -> getResources в чанке каждой новости и тд). После проверь, нет ли у тебя явного указания НЕ КЭШИРОВАТЬ вывод сниппетов ( например: [[!getResources]] ). Запрещать кэширование можно только в случаях, которые действительно этого требуют. Поменьше используй всяких стандартных сниппетов наподобие [[If]] Не стреляй из пушки по воробьям :) Если нужна какая-то простая операция от сложного сниппета (вырожденный пример - использовать getResources для получения и вывода TV у товара) - вынеси этот функционал в свой сниппет. Ну и наконец, попробуй перенести чанки и сниппеты из базы на диск. По умолчанию, они находятся в базе.

Ответ 4



Если нужно реальное ускорение, надо забыть про if is then else - т.е. убери всю логику, сделанную на этом сниппете IF. На соседнем ресурсе разогрелась нешуточная война двух лагерей, суть в том, что ModX, встретив такие обертки, сначала обрабатывает все вложенные сниппеты и затем уже проверяет условие!! Т.е. как бы это не звучало консервативно, все-таки для очень разных страниц правильнее использовать разные шаблоны! А не так, как некоторые хвастаются - "У меня ОДИН шаблон на всё про всё!" )))

Устранение излишних запросов на сайте

#php #оптимизация #mysql

                    
Приветствую
if(!isset($_SESSION['zapros'])){
    //тут у нас запрос к базе, который возвращает массив $massiv;
    $_SESSION['zapros'] = $massiv;
}

Знаю, так делать неправильно, совать в сессию все запросы. Но в силу того, что 'мне
хватает' - использую. Вопрос прост, как на сайте организовать уменьшение запросов от
одного пользователя? Мой способ, из эры динозавров - сунуть в сессию. Но правильно
ли это? Есть модная штука memcache, но никто не объяснит чем оно лучше той же сессии..
вот допустим у сессии лимит 128мб на всех пользователей (зависит от хостинга), а у
мем кэша есть недостатки?
Поможете разобраться?
p.s. да, знаю можно вообще заранее подготовленные данные из файла читать. меня интересует
чисто кэширование запросов    


Ответы

Ответ 1



Memcache >>>>>>>>>>>>> сессия. Как можно догадаться, мемкэш держит все в памяти, а сессия сохраняется на диск - скорости будут просто несопоставимы. Тем более, не будете же вы для каждого пользователя писать список десяти последних новостей, например? Ну и первое, что вас должно было смутить: сессия - это штука, созданная исключительно для работы с пользователем. По-хорошему кэширование должно быть встроено уже на уровне дб или модели, когда опциональным аргументом метода указывается, что запрос может быть закеширован.

Ответ 2



По-умолчанию MySQL умело кеширует на своей стороне запросы, если скорость с сервером MySQL высокая, то этого обычно достаточно. Если же нужно кешировать на стороне PHP, то лучше всего использовать хорошие ORM к примеру Doctrine.

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

Алгоритм не работает так как нужно

#c_sharp #оптимизация #генетический_алгоритм


Вот задача:


  Вы работаете с компанией по доставке товаров, которая ежедневно пользуется платной
  автомобильной дорогой. 
  
  Плата за путешествие взимается на 10-и пунктах оплаты
  расположенных вдоль дороги. Водителям компании необходимо преодолеть весь путь,
  оплатив комиссию за проезд на каждом из пунктов.
  
  Сложность состоит в том, что по правилам, комиссию можно оплачивать только одной
  единственной монетой. В случае, если ее номинал выше, чем стоимость проезда,
  водитель сдачу не получает и остаток сгорает. Если же монета, наоборот, не полностью
  покрывает стоимость проезда, то вашей компании насчитывается долг. 
  
  При этом
  стоимость проезда на каждом из пунктов абсолютно произвольно изменяется в конце дня,
  и может варьироваться в диапазоне от 1-ой до 10-и копеек включительно. Также
  известно, что несколько пунктов оплаты могут выставлять одну и ту же стоимость
  проезда, а общая сумма проезда через все пункты будет всегда больше 55-и копеек.
  
  Каждому водителю в начале пути выдается 10 монет, по одной монете каждого
  достоинства (т.е. одна монета достоинством в копейку, одна монета достоинством в две
  копейки, одна - три, и так далее, до десяти копеек включительно). 
  
  Используя генетический
  алгоритм, вам необходимо найти такую стратегию оплат путешествия, при которой долг
  водителя в конце пути будет минимальным. Алгоритм будет применяться компанией в
  начале каждого дня, и использовать данные по новым, только что установленным,
  размерам комиссий на пунктах оплат для получения новой стратегии для водителей.
  
  Входящие параметры:
  Массив из десяти произвольных чисел от 1 до 10, представляющих собой размеры
  комиссий на каждом из пунктов. Числа в массиве могут повторятся, и их сумма будет
  всегда больше чем 55.
  
  Выходные данные:
  Массив из десяти чисел, представляющих собой достоинства монет, расположенные в
  порядке, оптимальном для оплат на каждом из пунктов (так чтобы долг компании после
  всех оплат был минимальным).


Я скорее всего справился. В смысле алгоритма, но он не работает.

Вот код:

    class Program
{
    static void Main(string[] args)
    {
        Random rand = new Random();
        //bool weNeedContinue = true;          
        int[] propuski1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] bestWay = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] propuski = PropuskiPrice(propuski1);
        int[] car0 = {0,0,0,0,0,0,0,0,0,0};
        int[] car1 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] car2 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] car3 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] car4 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] car5 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] car6 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] car7 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] car8 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] car9 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        GenerateCar(car0,bestWay);
        int[] moneyChange = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };                    
        for(int i = 0;i <10;i++)
        {
            GettingBest(moneyChange, car0, propuski, bestWay);
            GenerateCar(car0, bestWay);
            GenerateCar(car1, bestWay);

            GenerateCar(car2, bestWay);
            GenerateCar(car3, bestWay);
            GenerateCar(car4, bestWay);
            GenerateCar(car5, bestWay);
            GenerateCar(car6, bestWay);
            GenerateCar(car7, bestWay);
            GenerateCar(car8, bestWay);
            GenerateCar(car9, bestWay);

            //tournament begin
            int[] car23 = GenerateWinner(car2, car3, propuski);
            int[] car45 = GenerateWinner(car4, car5, propuski);
            int[] car67 = GenerateWinner(car6, car7, propuski);
            int[] car89 = GenerateWinner(car8, car9, propuski);

            int[] car2345 = GenerateWinner(car23, car45, propuski);
            int[] car6789 = GenerateWinner(car67, car89, propuski);

            int[] winner = GenerateWinner(car2345, car6789, propuski);
            int sumWinner = 0;
            for (int j = 0;j < 10; j++)
            {
                Console.WriteLine("stoimost proezda na punkte " + j + " " + propuski[j]);
                Console.WriteLine("deneg v karmane nomer " + j + " " + winner[j]);
                sumWinner = sumWinner + (winner[j] - propuski[j]);
            }
            Console.WriteLine("summa dolga= "+sumWinner);



            Console.ReadKey();
        }          

    }

    public static int[] PropuskiPrice(int[] a)
    {
        Random rand = new Random();
        int summa = 0;
        for (var i = 0; i < a.Length; i++)
        {
            a[i] = rand.Next(10) + 1;
            summa += a[i];
            if (i == 9 && summa <= 55)
            {
                PropuskiPrice(a);
            }
        }
        return a;
    }

    /////////////////////////////////////////////////////
    ////Создаем водителя/////////////////////////////////
    /////////////////////////////////////////////////////


    public static int[]  GenerateCar(int[] a,int[] c)
    {
        Random rand = new Random();

        for (int i = 0; i < a.Length; i++)
        {
            if(c[i] != 0)
            {
                a[i] = c[i];
                continue;
            }
            int b = rand.Next(1, 11);
            if (!a.Contains(b))
            {
                a[i] = b;
            }
            else
                i--;
            if(i == a.Length - 1 )
            {

            }
        }
        return a;
    }

    ////////////////////////////////////////////////////
    ////INIT////////////////////////////////////////////
    ////////////////////////////////////////////////////

    static void GettingBest(int[] change,int[] car,int[] propuski,int[] bestOne)
    {
        int changeSum = 0;
        for (int i = 0; i< 10; i++)
            {
                change[i] = car[i] - propuski[i];
                changeSum += change[i];
                if (change[i] == 0)
                {
                    bestOne[i] = car[i];
                Console.WriteLine("Карман" + car[i]);
                Console.WriteLine("Цена" + propuski[i]);
                Console.WriteLine("Лучший путь"+ bestOne[i]);
                }
            }




    }


    public static int[] GenerateWinner(int[] a, int[] b,int[] prises)
    {
        int[] changeA = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] changeB = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        int[] result = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
        for (int i = 0;i < 10; i++)
        {
            changeA[i] = a[i] - prises[i];
            changeB[i] = b[i] - prises[i];
            if(Math.Abs(changeA[i]) > Math.Abs(changeB[i]))
            {
                result[i] = b[i];
            }
            else
            {
                result[i] = a[i];
            }
        }
        return result;
    }

}


В итоге, вот эту часть:

     for(int i = 0;i <10;i++)
        {
            GettingBest(moneyChange, car0, propuski, bestWay);
            GenerateCar(car0, bestWay);
            GenerateCar(car1, bestWay);

            GenerateCar(car2, bestWay);
            GenerateCar(car3, bestWay);
            GenerateCar(car4, bestWay);
            GenerateCar(car5, bestWay);
            GenerateCar(car6, bestWay);
            GenerateCar(car7, bestWay);
            GenerateCar(car8, bestWay);
            GenerateCar(car9, bestWay);

            //tournament begin
            int[] car23 = GenerateWinner(car2, car3, propuski);
            int[] car45 = GenerateWinner(car4, car5, propuski);
            int[] car67 = GenerateWinner(car6, car7, propuski);
            int[] car89 = GenerateWinner(car8, car9, propuski);

            int[] car2345 = GenerateWinner(car23, car45, propuski);
            int[] car6789 = GenerateWinner(car67, car89, propuski);

            int[] winner = GenerateWinner(car2345, car6789, propuski);
            int sumWinner = 0;


Начиная с 3-го, GenerateCar программа игнорирует. Посмотрите, в чем проблема?
    


Ответы

Ответ 1



Попробовал запустить код. У вас постоянно крутится один и тот же цикл внутри GenerateCar и из него не хочет выходить. Почему? Подумайте какое у вас условие выхода из цикла и почему оно не выполняется. Это первое замечание. Второе - вам нужно почитать очень популярный вопрос на сайте, с которым многие сталкиваются - о том, что Random всегда выдаёт одни и те же данные при одинаковом начальном значении. Подумайте: вы пытаетесь искать разные пути, но каждый раз перебираете одни и те же варианты. Третье. Почему вы генерируете 1..11 а не 0..11? Вы никогда не сгенерируете 0 - а потому вы не выйдете из цикла: int b = rand.Next(1, 11); // maybe should be int b = rand.Next(0, 10); Поправив я сразу выскочил из бесконечного цикла. PS У вас много копипасты. Программист не будет писать car0... car9 и копипастить подряд 10 раз GenerateCar. Нужно писать циклы, нужно использовать списки и массивы... а жать ctrl+c, ctrl+v -- это подход не программиста. В остальном -- нужно понимать, что за алгоритм вы реализуете или быть в теме "генетических алгоритмов". Я например имею только общие понятия о этой теме, поэтому вы в этом коде больше понимаете, чем я. Попробуйте словами описать, что тут к чему в коде.

Ответ 2



Чего-то такое получается Так как у нас по сути позиционная задача, будем использовать такой класс /// /// ДНК с фиксацией /// public class DNA { public bool Locked; public int Value; public override string ToString() { return $"{Value}->{Locked}"; } } т.е. если данный ДНК с нужным значением буден находится в нужной позиции, то для того, чтобы его в дальнейших мутациях не трогать мы полю Lock будем присваивать true. Следующий класс будет у нас геном или хромосомой :) я биологию совсем уже забыл... public class PaymentsState { public enum InitPayments { ForOffspring, ForHighway, ForDriver, } private readonly Random _random; public List Payments { get; private set; } public int Overpayment { get; private set; }//Fitness //ctor public PaymentsState(Random random, InitPayments init = InitPayments.ForOffspring) { _random = random ?? throw new ArgumentNullException(nameof(random)); Payments = new List(10); if (init == InitPayments.ForHighway) { GetPaymentsForHighway(); } else if (init == InitPayments.ForDriver) { GetPaymentsForDriver(); } else { } } /// /// Заполнение списка оплат для Водителя /// все типы монет 1..10 предствлены в отдном экземпляре /// в случайном порядке /// private void GetPaymentsForDriver() { var values = Enumerable.Range(1, 10).OrderBy(n => _random.Next(1, 10)) .ToList(); //заполняем список foreach (var val in values) Payments.Add(new DNA { Value = val, Locked = false }); } /// /// Создание списка оплат для Автострады /// /// private void GetPaymentsForHighway() { //сгенирируем массив из десяти чисел от 1 до 10 рассположенных в случ.порядке List values = Enumerable.Range(1, 10).OrderBy(n => _random.Next(1, 10)) .ToList(); //скорректируем под требование суммы int sum = 0; do { var index = _random.Next(0, 8); //просто копируем соседний элемент values[index] = values[index + 1]; sum = values.Sum(); } while (sum <= 55); //заполняем список foreach (var val in values) Payments.Add(new DNA { Value = val, Locked = false }); } /// /// Подсчет переплаты на основании переданного списка оплат /// /// список оплат у автотрассы /// значение переплаты public int CalcOverpayment(List highwayPayments) { if (highwayPayments.Count == 0) return 0; for (int i = 0; i < highwayPayments.Count; i++) { //если это ранее уже зафиксированный ген, //то пропускаем его if (Payments[i].Locked) continue; int val = highwayPayments[i].Value - Payments[i].Value; //в случае равенства (самая выгодная оплата) if (val == 0) { Payments[i].Locked = true; // фиксируем эту ДНК } else if (val < 0) { //В случае, если ее номинал выше, чем стоимость проезда, //водитель сдачу не получает и остаток сгорает. Overpayment += Math.Abs(val); } else // в случае полож.остатка он идет в долг, т.е. все равно в переплату { Overpayment += val; } } return Overpayment; } /// /// Скрещивание с другим геном и получение потомка /// /// другой PaymentState /// public PaymentsState Crossover(PaymentsState secondParent) { if (secondParent == null) throw new ArgumentNullException(nameof(secondParent)); //Здесь сложное наследование, т.к. каждая //монета должна быть представлена единожды и обязательно //т.е. если берем от мамы в позиции 1 монету 10, //то у папы мы уже не можем взять монету с этим же достоинством 10 //словарь для хранения использованных достоинств монет Dictionary usedCoins = Enumerable.Range(1, 10) .ToDictionary(n => n, n => false); //теперь у потомка должны быть гены от обоих родителей //зафиксированные гены должны занять те же самые позиции и значение //незафиксированные гены должны взять значение из словаря достоинств монет List offspringPayments = Enumerable.Range(1, 10).Select(n => new DNA { Value = 0 }) .ToList(); //определим у мамы(this) зафиксированные ДНК, //т.е. те, кот. не следует изменять var motherLockedDNAs = Payments.Where(p => p.Locked == true); //пробежимся по ним и включим их в словарь использованных монет //и добавим их в список для потомка foreach (DNA locked in motherLockedDNAs) { int index = Payments.IndexOf(locked); offspringPayments[index] = locked; //отмечаем использование монеты usedCoins[locked.Value] = true; } //определим у папы(secondParent) тоже var fatherLockedDNAs = secondParent.Payments.Where(p => p.Locked == true); foreach (DNA locked in fatherLockedDNAs) { //определим индекс который занимает int index = secondParent.Payments.IndexOf(locked); //если у матери такой монеты не было, //и этот индекс еще свободен if (!usedCoins[locked.Value] && offspringPayments[index].Value == 0) { offspringPayments[index] = locked; usedCoins[locked.Value] = true; } } //теперь нужно заполнить оставшиеся гены foreach (var payment in offspringPayments) { if (payment.Value == 0) { int val = usedCoins.First(kv => kv.Value == false).Key; payment.Value = val; usedCoins[val] = true; } } //готовим потомока var offspring = new PaymentsState(_random, InitPayments.ForOffspring); foreach (var payment in offspringPayments) offspring.Payments.Add(payment); return offspring; } /// /// Мутирование гена /// /// public void Mutate(double mutationRate) { //Сложное мутирование, т.к. каждое достоинстово монеты //должно быть обязательно и один раз for (int i = 0; i < Payments.Count; i++) { //если это ранее уже зафиксированный ген, //то пропускаем его if (Payments[i].Locked) continue; if (_random.NextDouble() < mutationRate) { //запоминаем монету int coinInner = Payments[i].Value; //новое случайное значение монеты int coinRandomValue = _random.Next(1, 10); //находим ген монеты с таким же значением DNA dna = Payments.First(p => p.Value == coinRandomValue); //если этот ген имеет статус зафиксированного //то ничего с ним делать не будем if (dna.Locked) continue; //находим индекс этого гена int index = Payments.IndexOf(dna); //запоминаем по этому индексу новый ген Payments[index] = new DNA { Value = coinInner }; //а по текущему индексу полученную из случайного знач. Payments[i] = dna; } } } } Класс автотрассы /// /// Автотрасса /// public class Highway { private readonly Random _random; private readonly PaymentsState _paymentsState; //ctor public Highway(Random random) { _random = random ?? throw new ArgumentNullException(nameof(random)); _paymentsState = new PaymentsState(_random, PaymentsState.InitPayments.ForHighway); } public List Payments => _paymentsState.Payments; public int PaymentSum => Payments.Select(p => p.Value).Sum(); public int MinOverpayment => PaymentSum - 55; } Ну и класс, который будет работать с популяцией и осуществлять для нас др.полезные вещи public class PaymentGeneticAlgorithm { private readonly Random _random; private List _tmpNewPopulation = new List(); private PaymentsState _bestPaymentsState; //лучшая позиция public List Population { get; set; } = new List(); public int Generation { get; private set; } //номер поколения public double MutationRate { get; private set; } //коэффициент мутации //ctor public PaymentGeneticAlgorithm(Random random, int populationSize, double mutationRate = 0.5) { _random = random ?? throw new ArgumentNullException(nameof(random)); MutationRate = mutationRate; for (int i = 0; i < populationSize; i++) { Population.Add(new PaymentsState(_random, PaymentsState.InitPayments.ForDriver)); } //в первый раз у нас лучшим будет просто первый _bestPaymentsState = Population[0]; } //-- public int BestOverpayment => _bestPaymentsState.Overpayment; //значение лучшей переплаты public List BestPayments => _bestPaymentsState.Payments.Select(p => p.Value).ToList(); //лучшие оплаты //-- /// /// Создание нового поколения популяции /// /// public void CreateNewGeneration(List highwayPayments) { //проверка входных данных if (highwayPayments == null) throw new ArgumentNullException(nameof(highwayPayments)); if (highwayPayments.Count <= 0) return; if (Population.Count <= 0) return; //выбор из популяции наиболее пригодного экземпляра _bestPaymentsState = CalculateFitness(highwayPayments); //готовим новую популяцию _tmpNewPopulation.Clear(); //будем скрещивать лучшего с оставшемися в популяции for (int i = 0; i < Population.Count; i++) { //выбор родителя var parent = Population[i]; //производим наследование или скрещивание var child = _bestPaymentsState.Crossover(parent); //подвергнем потомка мутации child.Mutate(MutationRate); //вносим потомка в новую коллекцию _tmpNewPopulation.Add(child); } //заменяем старую популяцию на новую var tmpList = Population; Population = _tmpNewPopulation; _tmpNewPopulation = tmpList; //увеличиваем счетчик поколений Generation++; } /// /// Проверка текущего поколения на пригодность /// Выявление лучшего гена наиболее подходящего под образец /// private PaymentsState CalculateFitness(List highwayPayments) { var best = _bestPaymentsState; for (int i = 0; i < highwayPayments.Count; i++) { var fitness = Population[i].CalcOverpayment(highwayPayments); //если переплата меньше, берем этот вариант if (fitness < best.Overpayment) best = Population[i]; } return best; } } Пользоваться всем этим счастьем можно так class Program { static void Main(string[] args) { Console.WriteLine("==Программа планирования платежей=="); Console.WriteLine(); var random = new Random(); var populationSize = 100; var mutationRate = 0.2; var highway = new Highway(random); var pga = new PaymentGeneticAlgorithm(random, populationSize, mutationRate); Console.WriteLine("Текущая ситуация на трассе"); PrintHighway(highway); Console.WriteLine(); Console.WriteLine("Для начала расчета нажмите любую клавишу"); Console.ReadKey(true); do { pga.CreateNewGeneration(highway.Payments.ToList()); Console.WriteLine("Трасса"); PrintHighway(highway); Console.WriteLine("Платежи"); PrintBestGenes(pga.BestPayments); Console.WriteLine($"Поколение: {pga.Generation}, Переплата: {pga.BestOverpayment}"); Console.WriteLine(new string('-', 80)); Console.WriteLine(); //Thread.Sleep(700); if (pga.Generation > 1000) break; } while (pga.BestOverpayment > highway.MinOverpayment); Console.ReadKey(); } private static void PrintBestGenes(IEnumerable bestGenes) { var payments = bestGenes.ToList(); var names = Enumerable.Range('A', payments.Count).ToList(); Console.Write("|"); Console.Write(new string('=', 5)); for (int i = 0; i < payments.Count; i++) { Console.Write($"[{(char)names[i]}:${payments[i]}]"); Console.Write(new string('=', 3)); } Console.Write(new string('=', 2)); Console.WriteLine("|"); } private static void PrintHighway(Highway highway) { var payments = highway.Payments.ToList(); var names = Enumerable.Range('A', payments.Count).ToList(); Console.WriteLine($"Общая сумма оплаты: {highway.PaymentSum}, минимально возможная переплата: {highway.MinOverpayment}"); Console.Write("|"); Console.Write(new string('=', 5)); for (int i = 0; i < payments.Count; i++) { Console.Write($"[{(char)names[i]}:${payments[i].Value}]"); Console.Write(new string('=', 3)); } Console.Write(new string('=', 2)); Console.WriteLine("|"); } }

Сократить запись функции для оптимизации

#php #функции #оптимизация


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

function test($x){
    if ($x!==0 && $x!==1) {
        return;
    }
    elseif($x == 0) {
        echo 1;
    }
    else
        echo 0;
}

    


Ответы

Ответ 1



function test($x){ if ($x == 1) { echo 0; } elseif ($x == 0) { echo 1; } }

Ответ 2



как сократить количество операторов сравнения и условий, для оптимизации, или это уже итак минимум? Можно существенно сократить код, используя вместо условий массив в комбинации с коалесцентным оператором: function test($x, $a = [1, 0]) { echo $a[$x] ?? null; } Вызов test(0) выведет 1, test(1) - 0. Все остальные варианты - null.

Ответ 3



нашел три варианта решения 1 - echo !$x; 2 - echo 1-$x; 3 - echo ($x ? 0 : 1); если вдруг кому будет необходимо :)

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

Оптимизация больших изображений для сайта

#html #css #css3 #оптимизация #оптимизация_сайтов


На сайте используется ряд изображений с высоким разрешением от 2500px.

pageSpeed конечно же ругается, поэтому пытаюсь использовать современный атрибут srcset
для изображений:



* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

img {
  max-width: 100%;
  margin: auto;
}
A title
Для высоко разрешения так и использую в 2500px, а для обычного делаю эти же картинки в уменьшенным в 2 раза разрешением. Пример записи img для реального проекта: В итоге по всем "возможностям" pagespeed ругается на эти изображения и ругается на img-1-2x.jpg Т.е. на телефонах вместо того чтобы отображать img-1-1x.jpg отображается большая картинка. И такие проблемы только для моб. девайсов: Для десктопа все отлично, скорость 90+! По совету использую Echo.js lazy load, но этот lazy load на телефонах (особенно при плохом интернете) не все фото грузит. Update: Как оказалось запись: A title не валидна! При использовании атрибута sizes запись должна быть подобна этой: A title Просмотрев видеоурок с рекомендацией о сжатии изображения в squoosh.app, сделала изображения с разрешением .webp. Подключила с использованием picture: Изображения в формате .webp весят около 200-300 кб, это при том что в большом разрешении около 2 Мб. В итоге скорость на страницах упала до 10 - 13, и изображения отображаются только в хроме. Стоит последняя версия firefox, но почему-то и в нем не отображаются картинки. caniuse.com/#feat=picture ! Да и не всегда есть возможность оптимизировать все изображения, т.е. менять размеры или разрешение, а таких картинок на сайте много. Вопрос: Почему срабатывает огромное разрешение для телефонов, а не уменьшенное при записи


Ответы

Ответ 1



Делайте картинки в формате webp и добавляйте их в ваш srcset, этого гугл от вас и ждет, о чем вы бы и сами узнали, если бы развернули блок с ошибкой и прочитали что там написано. Для изображений в форматах JPEG 2000, JPEG XR и WebP используется более эффективное сжатие, поэтому они загружаются быстрее и потребляют меньше трафика, чем изображения PNG и JPEG. Вариант с picture наиболее кроссбраузерный. Сначала даете ему на съедение картинку в формате webp (как я вам в примере написал), если он не поддерживает такой тип, то будет грузить jpg. Но гугл пейдж спид это расценит как ХОРОШО.

Ответ 2



Вообще можете тестировать и оптимизировать изображения в gtmetrix, но если поищите можете найти другие сайты для оптимизации, и еще в photoshop сохраните изображение с таким качеством что бы было оптимально размер -> качество. И еще один важный момент. Это для мобильного сайта. Изображения которые не должны быть видны в мобильном. сделайте фоном блока(если это возможно) в место тега img. И в определенный момент когда блок скрывается ОБЯЗАТЕЛЬНО для этого разрешения экрана поставьте для этого блока с фоновым изображением background-image: none;. Вот таким образом в вы достаточно оптимизируете свою страницу.

Ответ 3



А если в src ставить уменьшенную картинку, или заглушку и после загрузки скриптом менять из атрибута, причем можно и с проверкой разрешения окна и для мобильных брать например из data-mobile меньшего размера? Не знаю такой вариант обманет ли pageSpeed. Можно переписать на чистом JS $(function() { $('.big').html(function() { $(this).attr("src", $(this).attr('data')); }); });

четверг, 19 марта 2020 г.

Поиск клеток в радиусе от заданной

#php #оптимизация


Вход - X,Y,R. Соответственно, координаты центра и радиус.
Надо найти все, что внутри круга с такими параметрами.
Есть большое подозрение на быдлокод:
function getCellsByRadius($x,$y,$r){
    for($i=floor($y-$r);$i<=ceil($y+$r);$i++){//высота квадрата со стороной 2r. Искомый
круг точно не больше.
      for($j=floor($x-$r);$j<=ceil($x+$r);$j++){//ширина квадрата со стороной 2r
        $distance = sqrt(pow(($j - $x),2)+pow(($i - $y),2));//расстояние до каждой
точки квадрата
        if($distance<$r){
          $this->cells[$j][$i]='#999';
        }
      }
    }
    $this->cells[$x][$y]='#f00';
  }

Помогите, пожалуйста, разбыдлокодить. =(
P.S. можно проверять расстояние до точек, которые внутри квадрата +-r, но все ромба
x+r,y
x-r,y
x,y+r
x,y-r

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


Ответы

Ответ 1



function getCellsByRadius($x,$y,$r,$ccircle,$ccenter){ $r2 = $r * $r; for($i=floor($y-$r);$i<=ceil($y+$r);$i++){ $dxmax2 = $r2 - ($y - $i) * ($y - $i); if ($dxmax2 < 0) continue; $dxmax = sqrt($dxmax2); $lower = floor($x-$dxmax); $upper = ceil($x+$dxmax); for($j=$lower;$j<=$upper;$j++){ $this->cells[$j][$i]=$ccircle; } } $this->cells[$x][$y]=$ccenter; } (Не уверен, что код правильный, я не знаток php. Может, где-то надо объявить локальные переменные.)

Ответ 2



чисто спортивный интерес. Решил просто матрицу заполнить 1, если в окружности сделал вот так $x=50; $y=25; $r=10; //грубо говоря тело вашей функции for ($i=-$r;$i<=$r;$i++) { $y1=ceil(sqrt(pow($r,2)-pow($i,2))); $cells[$x+$i]=array_fill($y-$y1,2*$y1+1,1); } вывел во так тут просто решил глянуть будет ли похоже на окружность for ($i=-$r;$i<=$r;$i++) { for ($j=-$r;$j<=$r;$j++) { if (array_key_exists($j+$y,$cells[$x+$i])) { echo $cells[$x+$i][$y+$j]; } else { echo'0'; } } echo '
'; } результат 000000000010000000000 000001111111111100000 000011111111111110000 001111111111111111100 001111111111111111100 011111111111111111110 111111111111111111111 111111111111111111111 111111111111111111111 111111111111111111111 111111111111111111111 111111111111111111111 111111111111111111111 111111111111111111111 111111111111111111111 011111111111111111110 001111111111111111100 001111111111111111100 000011111111111110000 000001111111111100000 000000000010000000000 Возможно гдето с округлением напутал, попробуйте. Единственное отличие что я присваиваю 1, а вы цвет ячейки. Единтсвееное что в коде нет - это присваивание цвета точки центра))) тут как говорится проще не сделаешь Вот непонятливый. Сам же в коде делал целые числа, зная что дроби не могут быть. Тогда как ты заставишь компьютер покрасить точку центра с координатами не являющимися целыми? Куда ты в своей окржности вписанной в квадрат 4х4 поставишь центр и покрасишь его другим цветом?ПО твоей логике он будет находится между пикселей что-ли?

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

Как ускорить процесс сортирования дат

#php #сортировка #оптимизация #дата


функция pc_date_sort(), приведен
ная в примере  показывает, как сортировать даты.

function pc_date_sort($a, $b) {
    list($a_month, $a_day, $a_year) = explode('/', $a);
    list($b_month, $b_day, $b_year) = explode('/', $b);
    if ($a_year > $b_year ) return 1;
    if ($a_year < $b_year ) return -1;
    if ($a_month > $b_month) return 1;
    if ($a_month < $b_month) return -1;
    if ($a_day > $b_day ) return 1;
    if ($a_day < $b_day ) return -1;
    return 0;
}
$dates = array('12/14/2000', '08/07/1999', '08/10/2001');
usort($dates, 'pc_date_sort');
echo '
';
print_r($dates);


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

Помогите пожалуйста оптимизировать код
Как избежать ненужной работы? 
    


Ответы

Ответ 1



использовать стандартный встроенный класс. Это раньше приходилось свои велосипеды городить с датами. встроенный datetime написан на C, он будет быстрее, он оттестирован и будет безопасней он предлагает больше возможностей чем свой велосипед дополнить его вы можете обернув в свой класс/функцию, унаследовав (не проверял) createFromFormat() создаст объекты из вашего формата дат без всяких дополнительных ухищрений и ручных explode-ов строчек

Ответ 2



Я добавлю свой вариант, но скорей всего буду рассматривать ваши предложния.Несмотря что протестировал вроде быстрее ,и памяти жрет меньше. Для того чтобы избежать ненужной работы, можно кэшировать сравниваемые значе ния, как показано в примере function pc_array_sort($array, $map_func, $sort_func = '') { $mapped = array_map($map_func, $array); // cache $map_func() values if ('' == $sort_func) { asort($mapped); // функция asort() быстрее функции usort() } else { uasort($mapped, $sort_func); // необходимо сохранить ключи } while (list($key) = each($mapped)) { $sorted[] = $array[$key]; // используем отсортированные ключи } return $sorted; } Чтобы избежать ненужной работы, функция pc_array_sort() использу ет временный массив $mapped для кэширования возвращаемых значе ний. Затем она сортирует массив $mapped, используя или порядок сор тировки по умолчанию, или определенную пользователем процедуру сортировки. Важно, что она использует сортировку, сохраняющую связи ключ/значение. По умолчанию она использует функцию asort(), потому что она быстрее, чем функция uasort(). (Медленность функции uasort() это всетаки значительный довод в пользу функции pc_array_sort().) Наконец, она создает отсортированный массив $sort ed, при этом отсортированные ключи в массиве $mapped выступают в ка честве индексов значений исходного массива. Для небольших массивов или коротких функций сортировки функция usort() работает быстрее, но как только число сравнений вырастает, функция pc_array_sort() обгоняет функцию usort(). Ну или сравнивать в пeрвом примере как и посоветовали следующим образом $date1 = DateTime::createFromFormat('!Y/m/d', '2012/10/17'); $date2 = DateTime::createFromFormat('!Y/m/d', '2012/10/17'); var_dump($date1 == $date2); //will be true var_dump($date1 > $date2); //will be false var_dump($date1 < $date2); //will be false

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

hasAttribute -VS- getAttribute

#javascript #оптимизация #img #attribute #properties


Тут есть решённая задача - http://plnkr.co/edit/FaKoF0M4QQB1OSdtpClb?p=preview

Суть в том, чтобы сделать предзагрузку картинок по мере прокрутки страницы.

Функцию showVisible - сделал слегка по-своему:

function showVisible() {
  var imgs = document.getElementsByTagName("img");

  for (var i = 0; i < imgs.length; i++) {


    if (isVisible(imgs[i]) && imgs[i].hasAttribute("realsrc")) {   // <--
      imgs[i].src = imgs[i].getAttribute("realsrc");
      imgs[i].setAttribute("realsrc", "");
    }

  }
}


Проблема возникает в проверке:

 if (isVisible(imgs[i]) && imgs[i].hasAttribute("realsrc"))


а именно в методе hasAttribute -
из-за него крашатся все картинки оказавшиеся в поле видимости.
Проблему решает замена hasAttribute на getAttribute. Так и хочется сказать ё**ный
кот, да как так то? Кто-то может объяснить почему возникает подобная дичь?
    


Ответы

Ответ 1



Метод hasAttribute в вашем случае вернет всегда значение true, вне зависимости от того, что у вас в аттрибуте, ведь согласно коду, аттрибут есть всегда, а вот значение не всегда у него есть. А метод getAttribute в одном случае вернет вам адрес ссылки, а в другом случае вернет пустое значение. Что в нашем случае означает: var tmp = imgs[i].getAttribute("realsrc"); // вернуло 'https://js.cx/lazyimg/3-2.jpg' Значит при проверке !tmp - это false. В противоположном случае у нас будет пустое значение, не undefined а просто ничего. Аттрибут есть, а значения нет. var tmp = imgs[i].getAttribute("realsrc"); // вернуло '' - ничего нет Значит при проверке !tmp - это true. Поэтому вот так и получается, что условие всегда true - отсюда и беда.

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

Скорость работы apply в Pandas

#python #pandas #оптимизация #dataframe #group_by


Имеется dataframe из двух столбцов.
Группируя по первому, суммирую значения по втором. Делаю двумя способами

df.groupby('col1')['col2'].sum()


Скорость выполнения: 0,01 сек

df.groupby('col1')['col2'].apply(lambda x: x.sum())


Скорость выполнения: 4,15 сек

Почему такая разница в скорости? 
Как ускорить работу, если нужна будет не просто сумма, а более сложная функция?
Например, 

df.groupby('col1')['col2'].apply(lambda x: ','.join([str(i) for i in x]))

    


Ответы

Ответ 1



Series.apply() и DataFrame.apply(...) - являются "не совсем векторизированными" функциями, которые чаще всего значительно медленнее своих векторизированных аналогов. К сожалению, серебрянной пули универсального и быстрого решения не существует. Так что подход обычно следующий: если есть вектроизированная функция для решения нашей конкретной задачи, то используем ее сравниваем скорость работы .apply() и обычного list comprehension и выбираем самый быстрый вариант. NOTE: при работе со строками (object dtype) list comprehension часто оказывается быстрее .apply() и иногда быстрее соответствующих векторизированных Series.str. методов если скорость не устраивает то пробуем один из подходов, рекоммендованных разработчиками Pandas: Cython Numba pd.eval()

понедельник, 24 февраля 2020 г.

Оптимизация MySQL где много условий where

#mysql #оптимизация


Сайт доска объявлений недвижимости, много фильтров (15-20) по параметрам недвижимости.
Каждый фильтр это одно where условие в select, в итоге если выбрать 4-5 фильтров,
получаем 4-5 условий в селекте. Есть текстовые поля, например улица, район. Подскажите
пожалуйста как правильно такое оптимизировать? Ставить индексы на все поля?
    


Ответы

Ответ 1



Вам нужно посмотреть поглубже - как работают индексы. MySQL выбирает только один индекс - по которому будет идти выборка. Индекс является доп-колонкой в таблице - не страшно для чтения если индексов очень много. Правда запись будет помедленнее - но обычно терпимо. Если во where у нас 5 условий на 5 полей - максимальный прирост мы получим при добавлении индекса на все 5 полей. В принципе если добавить индексы по наиболее частым выборкам - будет достаточно быстро на объёме до 10 мил. записей. Если больше - нужно шардить(резать на кусочки) таблицу, или использовать NoSql решения - которые ищут очень быстро по любым полям(в том числе не по точному совпадению) на огромных объёмах. Например мы для аналогичной задачи - доска объявлений использовали Apache Solr , влвали базу объяв 10 мил, остались довольны.

Ответ 2



20к записей - это мизер, даже без индексов все будет на приемлемой скорости работать. В идеале смотрите какие запросы будут чаще всего (из каких фильтров состоять), добавляете индексы по таким полям, которые дадут максимальное сокращение выборки. Например, если фильтр по улице из 20к записей выберет 10, то индексы по другим полям не нужны. Однако, если в фильтре улица не участвует, понадобится фильтр по другим полям/комбинациям полей.

Ответ 3



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

Скорость работы dynamic в C#

#c_sharp #оптимизация #clr


Смотрю курсы по C# proffesional от ITVDN. Там пример, первый раз обращаются к полю
dynamic, которое раннее приравнено к ссылке на объект, и показывают, что первое обращение
к такому полю занимает 2 миллиона тиков. Я повторяю это на своем компьютере, но у меня
всего 2 тысячи тиков. Примерно в 10 раз больше, чем следующие обращения к dynamic.
Соль то в том, что dynamic запоминает объект, но в первый раз он ничего о нем не знает
и поэтому занимает больше времени. Но 2 тысячи и 2 миллиона для одинаковых функций
- очень большая разница. Может курс устарел и разработчики C# оптимизировали dynamic?
Код:

MyClass c = new MyClass();
dynamic d = c;
long start, end;

while (true)
{
    QueryPerformanceCounter(out start);
    d.m();
    QueryPerformanceCounter(out end);
    Console.WriteLine((end - start) + '\n');
} 

    


Ответы

Ответ 1



На вопрос о разнице в количестве тиков отвечать не буду, потому как неизвестны ни способ измерения, ни конфигурации вашего и автора курса компьютеров, ни версии .NET. Вместо этого отвечу, почему первый вызов метода работает медленнее. Авось кому пригодится. Дополнения к посту приветствуются. Для каждого выражения (операции), использующего объект типа dynamic, компилятор генерирует специальный объект под названием dynamic call site, который представляет это выражение (операцию). После компиляции приведенного в вопросе кода получается примерно следующее (код с измерением опустил): static DynamicCallSite dCallSite; ... MyClass c = new MyClass(); dynamic d = c; while (true) { if (dCallSite == null) { dCallSite = new DynamicCallSite(); } dCallSite.DoInvocation("m", d); } При этом объект dynamic call site генерируется только один раз для каждого выражения. Это первая монетка в копилку "почему первый вызов работает медленнее". Дальше DLR проверяет типа объекта d, обнаруживает, что это C# объект, и вызывает С# компилятор, который, используя метаданные, генерирует expression tree для данного выражения. Expression tree возвращается обратно DLR, компилируется, получившийся делегат вызывается и кэшируется. Т.е. если DLR в дальнейшем встретит такое же выражения для объекта такого же типа, будет вызван закэшированный делегат. Это вторая монетка в копилку "почему первый вызов работает медленнее". Во время второго вызова у нас уже есть dynamic call site, так что DLR просто проверяет тип объекта, достает из кэша скомпилированный делегат и выполняет его.

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

Сократить количество mysql-кода

#mysql #sql #оптимизация


Можно ли как-нибудь сократить количество mysql-кода в следующем запросе?

SELECT IF(EXISTS(SELECT * FROM tbl WHERE `field_1` LIKE '1'), 
         (SELECT field_3 FROM tbl WHERE `field_1` LIKE '1'), 
         (SELECT field_3 FROM tbl WHERE `field_2` LIKE '1'));

    


Ответы

Ответ 1



Если столбцы field_1 и field_2 не уникальны, то подзапросы, в приведенном вами примере, могут вернуть несколько значений. Сам запрос можно сократить до одной выборки из таблицы, что сократит число обращений к таблице: SELECT field_3 FROM tbl WHERE `field_1` = 1 OR (`field_1` != 1 and `field_2` = 1) ORDER BY `field_1` LIMIT 1; Если field_1 и field_2 уникальны, то сортировка и ограничение числа записей не нужно: SELECT field_3 FROM tbl WHERE `field_1` = 1 OR (`field_1` != 1 and `field_2` = 1);

Ответ 2



Судя по всему, у вас во from другие таблицы, поэтому вы решили использовать подзапросы в select. Можно сделать так при условии, что SELECT field_3 FROM tbl WHERE field_1 LIKE '1' возвращает строго 1 строку всегда: SELECT IFNULL((SELECT field_3 FROM tbl WHERE `field_1` = '1'), (SELECT field_3 FROM tbl WHERE `field_2` = '1')); Не стоит делать like, если нет необходимости, у вас тут строгое сравнение с единичкой.

Ответ 3



Сортируем записи, поднимая вверх те, для которых выполняется первое из нужных вам условий, затем второе, и оставляем самую верхнюю (первую) запись выборки. SELECT field_3 FROM tbl WHERE (field_1 LIKE '1' OR field_2 LIKE '1') ORDER BY field_1 LIKE '1' DESC, field_2 LIKE '1' DESC LIMIT 1

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

Сократить количество mysql-кода

#mysql #sql #оптимизация


Можно ли как-нибудь сократить количество mysql-кода в следующем запросе?

SELECT IF(EXISTS(SELECT * FROM tbl WHERE `field_1` LIKE '1'), 
         (SELECT field_3 FROM tbl WHERE `field_1` LIKE '1'), 
         (SELECT field_3 FROM tbl WHERE `field_2` LIKE '1'));

    


Ответы

Ответ 1



Если столбцы field_1 и field_2 не уникальны, то подзапросы, в приведенном вами примере, могут вернуть несколько значений. Сам запрос можно сократить до одной выборки из таблицы, что сократит число обращений к таблице: SELECT field_3 FROM tbl WHERE `field_1` = 1 OR (`field_1` != 1 and `field_2` = 1) ORDER BY `field_1` LIMIT 1; Если field_1 и field_2 уникальны, то сортировка и ограничение числа записей не нужно: SELECT field_3 FROM tbl WHERE `field_1` = 1 OR (`field_1` != 1 and `field_2` = 1);

Ответ 2



Судя по всему, у вас во from другие таблицы, поэтому вы решили использовать подзапросы в select. Можно сделать так при условии, что SELECT field_3 FROM tbl WHERE field_1 LIKE '1' возвращает строго 1 строку всегда: SELECT IFNULL((SELECT field_3 FROM tbl WHERE `field_1` = '1'), (SELECT field_3 FROM tbl WHERE `field_2` = '1')); Не стоит делать like, если нет необходимости, у вас тут строгое сравнение с единичкой.

Ответ 3



Сортируем записи, поднимая вверх те, для которых выполняется первое из нужных вам условий, затем второе, и оставляем самую верхнюю (первую) запись выборки. SELECT field_3 FROM tbl WHERE (field_1 LIKE '1' OR field_2 LIKE '1') ORDER BY field_1 LIKE '1' DESC, field_2 LIKE '1' DESC LIMIT 1

пятница, 14 февраля 2020 г.

Можно ли сохранить экземпляр TDictionary целиком, включая хэши?

#delphi #оптимизация #хеширование #словари #биоинформатика


Учитывая, что теперь можно спокойно выделять большие объемы памяти внутри TMemoryStream,
я вернулся к идее хранения данных геномных исследований, используемых нами внутри TDictionary
в файле для будущего повторного использования. Класс определен так:

type
  PosIndex = packed record
     chr, pos:integer;
  end;
  PosIndexData = record
    gname, rname, promoter: string;
    count:array[0..NOfTissues-1] of byte;
  end;

TPosDict = class (TDictionary)
 private
   procedure SaveToStream(stream: TStream);
   procedure LoadFromStream(stream: TStream);
 public
   procedure SaveToFile(filename:string);
   procedure LoadFromFile(filename:string);
   procedure LoadFromZip(AFileName, InnerName: string);
   procedure SaveToZip(AFileName, InnerName: string);
end;


Предупреждая вопросы и комментарии в стиле "Зачем нужен TDictionary, когда есть базы
данных?", сразу скажу: у нас мобильное (не в плане телефона, а в плане, что оно часто
запускается где попало) приложение, мы не можем использовать стационарный сервер БД,
как коллега в своём вопросе Как оптимизировать таблицы/запрос в MySQL?, а работа с
файловыми БД с нашими объёмами данных, увы, крайне медленна. А вот с TDictionary поиск
происходит пусть не мгновенно, но для нас вполне подходяще по времени.

Запись в поток (этот метод затем используют и SaveToFile и SaveToZip) происходит так:

procedure TPosDict.SaveToStream(stream: TStream);
var
  writer: TWriter;
  ps:PosIndex;
  pid:PosIndexData;
  l:integer;
begin
  writer := TWriter.Create(stream, 4096);
  l:=sizeof(pid.count);
  try
    writer.WriteListBegin;
    for ps in Self.Keys do
      begin
        pid:=Items[ps];
        writer.WriteInteger(ps.chr);
        writer.WriteInteger(ps.pos);
        writer.WriteString(pid.gname);
        writer.WriteString(pid.rname);
        writer.WriteString(pid.promoter);
        writer.Write(pid.count,l);
      end;
    writer.WriteListEnd;
  finally
    writer.Free;
  end;
end;


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

procedure TPosDict.LoadFromStream(stream: TStream);
var
  reader: TReader;
  ps:PosIndex;
  pid:PosIndexData;
  l:integer;

begin
  Clear;
  l:=sizeof(pid.count);
  reader := TReader.Create(stream, 9192);
  try
    reader.ReadListBegin;
    while not reader.EndOfList do
    begin
       ps.chr:=reader.ReadInteger;
       ps.pos:=reader.ReadInteger;
       pid.gname:=reader.ReadString;
       pid.rname:=reader.ReadString;
       pid.promoter:=reader.ReadString;
       reader.Read(pid.count,l);
       Add(ps,pid); // вот это всё тормозит!!!
    end;
    reader.ReadListEnd;
  finally
    reader.Free;
  end;
end;


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


Ответы

Ответ 1



Особенность TDictionary в том, что когда заканчивается ёмкость под хэши, он увеличивает размер и в этот момент происходит перехэширование всей имеющийся (на данный момент) коллекции. Поэтому если заранее примерно известен размер коллекции, то этот размер умноженный на 2-3 можно поставить в capacity.

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

Для чего нужен reserve() в C++?

#cpp #оптимизация #vector #stl


Не могу понять, в чем смысл функции reserve(). Она выделяет память, но не создает
элементов, увеличивает емкость, но не размер. Для чего она нужна если все можно сделать
с помощью resize()?
    


Ответы

Ответ 1



Для оптимизации. resize требует инициализации всех элементов, и делает размер контейнера строго заказанным. Он становится заполненным чем-то - что вам может быть не нужно в данный момент. При этом вам придется отслеживать отдельно реальную заполненность вашего вектора, т.к. size() будет, по сути, врать - говоря, сколько всего элементов в векторе, а не элементов, нужных вам. Если же вы хотите добавлять с помощью resize() равно столько элементов, сколько вам в данный момент нужно - то это просто бестолку потраченное время на инициализацию и не более того... потому что каждый вызов будет вызывать перераспределение памяти и копирование. reserve подготавливает место для последующего заполнения, так что какой-нибудь push_back будет гарантированно (а не амортизированно) выполняться за O(1) - без каких-либо перераспределений памяти, съедающих массу времени... При этом size() будет давать точное количество элементов, итератор end() показывать куда надо... Примерно так. Если не убедил - могу еще немного поубеждать :)

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

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

#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 Так что значения логарифмов у вас все равно для некоторых чисел окажутся неверными.

Оптимизация mysql запросов

#php #оптимизация #mysql


Назрело несколько вопросов по оптимизации mysql запросов.

К примеру, на главной странице форума выводятся последние 5 сообщений. Формат такой:
форумная тема - короткий текст - ник - группа пользователя. До недавнего момента я
бы писал что-то типа такого:

/*
таблица posts: id, user_id, theme_id, text
таблица users: id, nick, group_id
таблица themes: id, name
таблица groups: id, name
*/
$posts = $db->execAndReturnAll('select `id`, `user_id`, `theme_id`, `text` from `posts`
order by `id` desc limit 5');

$users = array();
$themes = array();
$groups = array();

$l = sizeof($posts);
for($i = 0; $i < $l; ++$i)
{
if(!in_array($posts[$i]['user_id']), $users)
    $users[] = $posts[$i]['user_id'];
if(!in_array($posts[$i]['theme_id']), $themes)
    $themes[] = $posts[$i]['theme_id'];
}

$users = $db->execAndReturnAll('select `id`, `nick`, `group_id` from `users` where
`id` in (0,'.implode(',', $users).')');
$themes = $db->execAndReturnAll('select `id`, `name` from `themes` where `id` in
(0,'.implode(',', $themes).')');

$l = sizeof($users);
for($i = 0; $i < $l; ++$i)
{
if(!in_array($users[$i]['group_id']), $groups)
    $groups[] = $users[$i]['group_id'];
}

$groups = $db->execAndReturnAll('select `id`, `name` from `groups` where `id` in
(0,'.implode(',', $groups).')');

Но меня что-то берут сомнения, а целесообразно ли посылать 4 простых запроса к БД
или расходы на передачу запросов туда-обратно себя не окупят и лучше написать один
сложный запрос?

2) Стоит ли перекладывать на mysql всевозможные расчеты или лучше оставить это php?
То есть, что лучше:

$data = $db->execAndReturn('select `width`, `height`, `width`*`height` as `square`
from `rectangles` limit 1');

или

$data = $db->execAndReturn('select `width`, `height`, from `rectangles` limit 1');
$data['square'] = $data['width'] * $data['height'];

3) Есть ли какие-либо инструменты для тестирования скорости запросов к БД?
Заранее спасибо.    


Ответы

Ответ 1



В Вашем случае лучше написать один сложный. По поводу расчетов, mysql сделает их быстрее. Все таки php - это интерпритатор и любые лишние действия - это нагрузка. Но судя по Вашему коду, у Вас не нагруженный проект, так что такие действия (такие, как умножение) делайте там, где Вам удобнее :) Инструменты для тестирования: explain... и $start_time = microtime(true); // выполнение действий $exec_time = microtime(true) - $start_time;

Ответ 2



1) Зависит от того, сколько данных возвращается в ответах на эти запросы и где находится сервер БД (на той же машине или где-то в сети), тогда, возможно, стоит ориентироваться на скорость передачи данных и загрузку канала. UPD->Regarding Alex Silaev Например в данном случае (привожу только маленький пример): 'select `id`, `nick`, `group_id` from `users` where `id` in (0,'.implode(',', $users).')' Лучше поправить в один запрос, который выполнится на стороне MySQL: 'select `id`, `nick`, `group_id` from `users` where `id` in (select `user_id`from `posts` order by `id` desc limit 5)' Как-то так, просто для примера. 2)По моему мнению, стоит. На сегодняшний день существует множество агрегатных функций, которые с БД работают очень быстро и выводят данные с ошеломительной скоростью. Любая статистика выводится безумно быстро. 3) Была парочка инструментов, но уже не помню по склерозу. Вообще часто сталкивался с оптимизацией запросов, которые заносятся в журнал медленных запросов. Можно проанализировать переменные MySQL и статистику, выделив как быстро она (БД) работает, как эффективно используется память и т.п. Но это уже больше дело системного администратора.

Ответ 3



ну вообще по поводу выбора, то второй чуть лучше вариант! А вообще попробуйте поюзать вьюхи)

Ответ 4



phpmyadmin показывает время выполнения запроса, мне кажется очень удобным тестировать запросы в нем

Ответ 5



чем меньше запросов к mysql тем лучше. один сложный запрос всегда лучше, чем несколько простых. особенно нежелательно делать запросы в циклах. а вот где посчитать width*height большой разницы нет. можно с тем же успехом после выборки из базы, сделать цикл и в нем произвести вычисления. конечно, с обычным умножением и mysql справится легко, но бывают и более сложные операции, которые проще вынести на php. пример: $list = array(); $sqlRes = mysql_query("select height,width from ... "); while ($res = mysql_fetch_array($sqlRes)) { $res['square'] = $res['height']*$res['width']; $list[] = $res; }