1. C++ / Говнокод #10937

    −37

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    static QPainterPath::ElementType qpaintengineex_line_types_16[] = {
        QPainterPath::MoveToElement, QPainterPath::LineToElement,
        QPainterPath::MoveToElement, QPainterPath::LineToElement,
        QPainterPath::MoveToElement, QPainterPath::LineToElement,
        ... еще 12 строк ...
        QPainterPath::MoveToElement, QPainterPath::LineToElement
    };
    
    static QPainterPath::ElementType qpaintengineex_rect4_types_32[] = {
        QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 1
        QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 2
        ... еще 29 строк ...
        QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 31
        QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, // 32
    };

    Qt 4.x.x, src/gui/painting/qpaintengineex.cpp
    Как я понял, используется для ускорения функций drawLines и clip (дабы не выделять память и не заполнять path каждый раз).

    Запостил: bormand, 13 Июня 2012

    Комментарии (37) RSS

    • показать все, что скрытовот из-за подобного код становится потоко-небезопасным.
      а заведомо в 1 потоке вполне оправдано.
      Ответить
      • > вот из-за подобного код становится потоко-небезопасным.
        Схуя? (Простите, других слов для ответа не нашлось).
        Ответить
        • Может быть, но только не в данном случае, а тогда, когда данные могут меняться.
          Ответить
          • У каждого паттерна или приема есть свои условия применимости. Ведь даже молоток может быть опасным, если им бить по голове ;-)
            Ответить
        • поля статические, если будет заполнение из разных потоков...
          хотя, работа с гуи из нескольких потоков сама по себе тот еще ГК.
          Ответить
          • > если будет заполнение из разных потоков...
            Ну, в данном случае, их используют только для чтения...
            > хотя, работа с гуи из нескольких потоков сама по себе тот еще ГК
            В Qt оно и не работает. Максимум можно порисовать на QImage из соседнего потока.
            Ответить
            • наверное, я слишком много работаю с кодом, где нужно следить за потоко-безопасностью.
              вот и рефлекс уже выработался %( условный.
              Ответить
              • Threads are evil ;-)

                Хотя если выработать для себя список правил по написанию безопасного кода, то не все так и ужасно.

                P.S. Не особо люблю потоки, мне больше нравятся процессы или асинхронная обработка, хотя, зачастую, выбора нет.
                Ответить
                • ни разу не зло
                  поток - друг человека
                  и тем лучше, когда друзей много
                  Ответить
                  • friend WINAPI DWORD ThreadProc( LPVOID pParam );

                    Проблема может быть с многопоточностью, если статические переменные локальные. Но и то, для POD - маловероятно.
                    Ответить
                  • Треды - друзья человека ровно до тех пор, пока мы не выйдем за пределы уютной модели, в которой расшаренные данные полностью или по частям защищены mutex'ами, r/w-lock'ами и семафорами.

                    За пределами этой модели треды выпускают свои клыки, и пытаются откусить программисту ногу.

                    P.S. Я не утверждаю, что опытный программист не справится с тредами, и что они абсолютное зло. Я бы, скорее, назвал их злом необходимым.
                    Ответить
                    • Написание быстрого потокобезопасного кода, даже имея блокирующие очереди, mutex'ы, countDownLatch'и, etc. - сродни чёрной магии. Требуются годы опыта и детальное знание железа и модели памяти. В этом смысле да, потоки - зло.
                      Ответить
                      • > Написание быстрого потокобезопасного кода
                        Тут ключевое слово "быстрого". Для типичного потокобезопасного кода особого опыта не нужно. Справится любой программист, который выучил простые правила.

                        А вот быстрый код, выходящий за рамки мутексов, очередей и семафоров, это точно черная магия, за которую без опыта лучше не браться.
                        Ответить
                        • Да, я использовал слово "быстрого" сознательно.
                          Если синкать всё подряд, от потоков будет больше вреда, чем пользы - слишком много времени будет уходить на переключение контекста. В той же Java есть неплохие стредства для работы, но если начать разбираться, как эти средства реализованы, мозг закипит довольно быстро. Да и в этих средствах, написанных настоящими профессионалами, порой находят ошибки.
                          Ответить
                          • Если начать разбираться в деталях реализации, то мозг закипает даже от обычного мьютекса, куда уж там lock-free структуры ;)
                            Ответить
                    • если модель не рассчитана на многопоточность, тому должны быть веские причины
                      на дворе 2012 год, программист обязан хорошо подумать над тем, как он собирается использовать 4-6-8 ядер на пользовательской машине
                      и не столько уж это доставляет проблем, всего то надо выбрать удобный фреймворк
                      Ответить
                      • Не всем программам нужна многопоточность - "несколько потоков, исполняемых в общем адресном пространстве". Многим задачам больше подходит многопроцессная модель.
                        Ответить
                        • таких задач единицы
                          в 99% случаев потоков хватает
                          не хочется общего адресного пространства - не пользуйся общими переменными, раз так беспокоит "черная магия" написать одну строчку boost::mutex::scoped_lock lk(mymutex_); внутри блока {} / функции
                          да и thread local сносно работает

                          когда пишешь приложение, работающее одновременно с n сетевыми подключениями, m фоновыми задачами, то выполнять все их в основном потоке (даже с помощью iocp/waitformultipleobjects) - только создавать лишние лаги для основного потока, взаимодействующего с консолью
                          никто не говорит о параллельном расчете обратной матрицы на 200 потоках, обыденная повседневная многопоточность не требует сверхусилий, и даже наоборот, серьезно облегчает решение проблем с отзывчивостью - есть побочные потоки, они что то вошкаются со своими задачами, когда у них готов реальный результат, они об этом просто сообщают основному потоку, который принимает решение где и когда с этим результатом работать

                          удобство и масштабируемость такого решение в т.ч. ложится на плечи фреймворка, и если это всё делать руками, то конечно лениво и "многопоточность - зло"

                          просто прошли годы, а ватсон без трубки я без asio уже не могу
                          Ответить
                          • > "черная магия" написать одну строчку
                            Простая задачка: есть направленный граф задач, вершины - задачи (пусть будет экземпляр runnable), рёбра - зависимости между задачами. Нужно выполнить весь граф с учётом зависимостей (т.е. запуск задачи невозможен, пока задачи-зависимости не выполнены), по максимуму используя возможность параллелезации.
                            Даже в такой несложной задаче одной строчкой "чёрной магии" не обойтись.
                            Ответить
                            • такую задачу можно решить математически - написав приличный алгоритм обхода этого графа, в чем я не специалист ни разу, и в лоб -
                              есть K задач (вершин), M потоков, ограничений на К и М не накладывается,
                              состояние всех задач хранится в общем массиве, к которому ко всему прочему прилагается нечто, способное сигнализировать о моменте, что состояние массива изменилось (это может быть сделано с помощью condition variable, либо с помощью коллбека как я покажу ниже)
                              каждая задача знает набор, от которого она зависит, и по каждой реакции на вышеописанное событие ей следует посмотреть в этот массив, способна ли она начать свою работу. если не способна - делает return из коллбека

                              если делать конкретно на asio, то должен быть некий master object, в котором собственно и будет храниться массив выполненности задач, и который будет самостоятельно ждать события и самостоятельно же запускать задачи на выполнение обычным myioservice_.post(...thistask...).
                              событие о том, что задача закончилась - состояние массива требуется изменить - просто задача последнием действием делает коллбек в мастер обжект
                              с таким решением количество потоков задается в параметрах командной строки и хорошо работает как на 1 потоке, так и на 100

                              так что фактически с помощью asio не придется писать вообще ни одного мутекса
                              Ответить
                              • и главное, это всё сразу будет кроссплатформенно и совершенно бесплатно
                                покупайте нашу поебень
                                Ответить
                              • > не придется писать вообще ни одного мутекса
                                я не уверен, но вроде бы коллбэк может быть вызван сразу из нескольких потоков, и весьма вероятно будет нужна блокировка. Если только коллбэк - это не функтор, помещаемый в очередь сообщений для "master object" и выполняемый синхронно.
                                Ответить
                                • > Если только коллбэк - это не функтор, помещаемый в очередь сообщений
                                  Имхо достаточно просто очереди сообщений с идентификатором задачи и ее результатом.
                                  Ответить
                                • именно второе
                                  чудес ведь не бывает, т.е. внутри асио конечно будут и мутексы, да и синхронизацию мастеробжекта (прием коллбека) надо бы сделать через strand - обёртка в асио, заставляющая все коллбеки внутри него исполняться строго неконкурентно
                                  просто все вопросы балансировки по M потокам перекладываются на плечи фреймворка, что гуд
                                  необходимо понимать, что все операции, связанные с помещением i-го коллбека в очередь j-го потока, не очень бесплатные, и потому оверхед стоит оценить по отношению к времени, которое требуется для каждой задачи (т.е. если они микросекундные, то в один поток последовательно получится быстрее, если они секундные и больше, то вполне имеет смысл параллелиться, конечно)
                                  Ответить
                            • Берем мультимап, в котором будут храниться ссылки на невыполненные задачи, а ключом будет id первой задачи, от которой она зависит. Защищаем мап мутексом. Дальше нам понадобится блокирующая очередь, в которую сложим ссылки на задачи, которые ни от чего не зависят.

                              Запускаем пачку рабочих тредов.

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

                              Как насчет такого решения?
                              Ответить
                              • На первый взгляд должно работать. Попробую проверить на досуге реализацию на богомерзкой жабе. Сам решал способом, похожим на тот, что был предложен @defecate-plusplus (цепочка ListenableFuture<ExecutionStatus>).
                                Ответить
                      • > использовать 4-6-8 ядер
                        Характерно, что о многопоточности массы задумались только с появлением соотв. аппаратных возможностей на лоу-энд писюках.

                        http://hashcode.ru/questions/114371
                        Ответить
                • Модель clojure, к примеру, выглядит очень прилично: множество потоков, работающих с STM. Память общая, но запись в неё защищена системой транзакций. Довольно удобно, особенно по сравнению с ручными блокировками.
                  Хотя сейчас в моду больше входит модель акторов с асинхронной передачей сообщений.
                  Ответить
                  • STM очень хорошая модель, но к сожалению пока медленная.
                    Ответить
                • а правил немного:
                  1. стараться не юзать глобальные и статик-переменные
                  2. если уж так нужно, то обращения только в критических секциях
                  3. иные потоко-небезопасные вещи тоже.
                  Ответить
                  • Это только если нужны очень тривиальные вещи. Когда нужно что-то серьёзное, правил не существует.
                    Ответить
                  • 1. По возможности минимизировать расшаренное состояние
                    2. Защитить все что осталось критическими секциями\мутексами\рвлоками.
                    ...
                    4. Профит

                    Но, к сожалению для скоростного приложения Роман прав. Правил нет. Только треш угар и содомия ;)
                    Ответить
    • черепашкографика, однако.
      Ответить
    • интересно, qpaintengineex_rect4... почему rect, если
      QPainterPath::MoveToElement, QPainterPath::LineToElement, QPainterPath::LineToElement, QPainterPath::LineToElement
      не обязательно является rect'ом?
      Ответить
      • Ну, видимо, потому что они всегда используют его для прямоугольников. Это же внутренний массив, часть реализации. А используется он вот в таком коде (форматирование немного примял, чтобы занимало меньше места):
        if (rects.size() <= 32) {
            qreal pts[2*32*4];
            int pos = 0;
            for (QVector<QRect>::const_iterator i = rects.constBegin(); i != rects.constEnd(); ++i) {
                qreal x1 = i->x();
                qreal y1 = i->y();
                qreal x2 = i->x() + i->width();
                qreal y2 = i->y() + i->height();
                pts[pos++] = x1; pts[pos++] = y1;
                pts[pos++] = x2; pts[pos++] = y1;
                pts[pos++] = x2; pts[pos++] = y2;
                pts[pos++] = x1; pts[pos++] = y2;
           }
            QVectorPath vp(pts, rects.size() * 4, qpaintengineex_rect4_types_32);
            clip(vp, op);
        } else {
            ... случай для больших регионов, в котором создается массив, и
            заполняется этими LineTo и MoveTo в цикле...
        }
        Ответить
    • Сначала добейся
      Ответить

    Добавить комментарий