Pull to refresh
14
0
Send message
Да, а потом, как раз, можно посмотреть по сторонам и оценить, как это реализовали другие )
6000 строк с макросами и шаблонами… и его я должен заинклюдить в каждом своем translation unit…

Вероятно, многие из хедеров которые вы используете даже больше, а, кроме того, 6000 строк будет только один раз при первом вхождении, разве нет?
И еще я пока не понял по API, можно ли как-то динамически изменить уровень вывода в лог?

Да, можно.
Во-первых в командной строке (но это без динамики, только при старте), ну и во-вторых на лету тоже:
Скрытый текст
int main(int argv, char* argc[]) 
{
    /* Копия конфигурации по умолчанию */
    el::Configurations defaultConf;
    /* Изменим в ней флаг для вывода инфо сообщений */
    defaultConf.set(el::Level::Info, 
            el::ConfigurationType::Enabled, "false");
    /* Применим к логгеру по умолчанию */
    el::Loggers::reconfigureLogger("default", defaultConf);
    LOG(INFO)<<"Не будет выведено";
    LOG(WARNING)<<"Будет выведено";

    return 0;
}

Вероятно, на тот случай, если у вас будет крэш до того, как Вы что-то выведете в журнал (т.е. до первого обращения к библиотеке).
Насчет C++11, ругается из-за хедера type_traits, который сразу проверяет поддержку нового стандарта
syslog поддерживает там, где есть syslog.h
В плане кода с вашей стороны почти никакой разницы: что типа макросов SYSLOG(LEVEL) вместо LOG(LEVEL)
Вот здесь написано отчасти про их внутреннее устройство и про решение ABA проблемы, кстати тоже. Только вот момент насчёт выигранных четырех бит, как-то не до конца понятен…
Извините за этот наш косяк…
Сначала всё было нормально, но потом поспешили исправить то, о чем говорил lek, и насмешили накосячили.
Спасибо за вашу внимательность.
Мы, вот, всё пытаемся воспроизвести ваш сценарий — пока безуспешно… но попутно кое-что заметили плохое ))

После замечаний от lek, из конструктора шаблона, который здесь на странице пропал вызов InitializeSListHead, в архиве с изначальной версией такой проблемы нет. А отсутствие инициализации головы, как раз, приводит к падению на пустом стеке. (Сейчас исправили здесь в статье)

Вы какой версией пользовались?
Не могли бы вы показать конкретнее?

У нас вот в таком случае:
UMS::SList<std::function<void()>> stack;
...
stack.push([]{}) // producer
...
stack.pop(value) // consumer
value()          //


ничего не падает
Результаты работы теста c mutex при малом числе потоков действительно удивляют: даже на этих грубых числах уже видно, что числа с mutex, как минимум сравнимы, а то и меньше, чем у lock-free.
А, вот, насчет 10 мс рассогласования на старте вы совершенно правы, и об этом мы написали чуть выше. Можно было бы избежать этого, например, изменив внешний цикл на do-while, чтобы потребители сразу пробовали брать задания.

Однако, этот фрагмент кода, касается лишь самого теста. Нигде в тексте статьи мы не апеллируем к абсолютным числам по тестам. Проводится лишь сравнение реализаций. А поскольку каждый из 3(4) тестов имеет эти 10 мс, и время работы 1 теста составляет ~10-40 секунд, то они в этом плане абсолютно сравнимы и отмена 10мс не изменит соотношения.
Это совсем не верно. Посмотрите, пожалуйста, на код функции run_test.

1. Стартуют все производители и начинают накидывать задания
2. Стартуют все потребители и уже начинают потреблять задания
3. Ждем окончания работы производителей
4. Ждем окончания работы работы потребителей

Задержка между (1) и (2) только на время запуска (!) потоков, но никак не их работу. Лучше бы было только стартовать каждый из типов попеременно, но это не даст никакой заметной разницы.
Тогда каждый поток должен знать об общем количестве потоков каждого типа, значит ему это нужно давать в аргументе. Это меняет структуру организации worker потоков. Лучше ли это?

И главное для чего? Избежать 1 Wait-а на поток? Общее заданий >>>> числа потоков и один wait на поток ничего не стоит, потому что мы можем потерять на нём 1 раз 10 мс, и то без нагрузки на CPU — это же не sleep…
Вроде бы нет… вот результаты в сыром виде (но надо иметь в виду, что было только по 100К заданий и один проход, так что числа грубые):

Сырые числа
mutex   2       2       937  
mutex   2       4       1250 
mutex   2       8       43203
mutex   2       16      42421
mutex   2       32      36796
mutex   2       64      42812
mutex   4       2       1250 
mutex   4       4       1523 
mutex   4       8       43046
mutex   4       16      24921
mutex   4       32      38984
mutex   4       64      33789
mutex   8       2       41757
mutex   8       4       43125
mutex   8       8       74589
mutex   8       16      84453
mutex   8       32      82656
mutex   8       64      84746
mutex   16      2       42177
mutex   16      4       42333
mutex   16      8       82900
mutex   16      16      84736
mutex   16      32      85175
mutex   16      64      85458
mutex   32      2       43120
mutex   32      4       43554
mutex   32      8       85083
mutex   32      16      84453
mutex   32      32      84531
mutex   32      64      85634
mutex   64      2       43215
mutex   64      4       43437
mutex   64      8       84768
mutex   64      16      85146
mutex   64      32      85366
mutex   64      64      85651
А это невозможно, потому что hEvtDone ставится после выхода их блокирующего вызова producer_threads.join_all() в методе run_test. Это гарантирует окончание работы всех производителей до того, как consumer-ы пройдут Wait на событии и приступят к своим вторым циклам по чистке стека.
Кажется, что предложенный вами критерий «локальный счетчик количества извлеченных элементов ==iterations» насильственно закрепощает потоки потребители обрабатывать ровно столько заданий, сколько вбросил побратим-производитель в случае p=c, а если p!=c, то это вообще неверно. Так выходит?

И, кроме того, дело-то в том, что исходно обсчитывался квадрат {p X c}, где p почти везде не равно c, так что случай p=c, как бы вырожденный.
Если возникает «ситуация, когда евент сработал, и элементов к тому времени уже нет.», то ничего страшного не будет — это значит, что consumer-ы уже всё разобрали и условие во втором цикле consumer «while (stack.pop(value))» нас тут же выбросит на конец потока. На самом деле, это как раз неплохой сценарий: в том смысле что consumer-ы быстро справились и не надо чистить
Ответ добавили в конец статьи.
Нужен критерий остановки для потребителей. Само по себе отсутствие заданий в стеке не означает, что их больше не будет, т.к. вероятно какие-то производители просто запаздывают и положат задания позднее. Поэтому сначала мы ждем пока все производители отработают и до тех пор честно пробуем найти еще задания в стеке, а вот когда они закончили работу, тогда пустой стек можно считать концом.
Часто проблемы с переносом на Windows бывают, например, c pthread. Хотя есть пару реализаций под WinAPI, но выглядят они как-то не очень.
А так, некоторые вещи из posix поддерживаются в VS SDK, поэтому малая часть проблем с переносом уходит. Вот здесь у них, например, гид по переносу на Win32

А вообще, то что называется кросс-платформенным решением часто не является таковым в полной мере, потому что именно Windows и не поддерживается… И не потому что это невозможно портировать. Так что, скорее, это правильно называть кросс-nix-овые продукты.

А вот многие вещи от Гугла или тот же SQLite, как раз, в большей степени кросс-платформенные: собираются на чём хочешь.
1

Information

Rating
Does not participate
Registered
Activity