Vsevolod
со всеми этими delay слотами и прочим говном, которое интел своим спекулятором сам делает
Good
да, он менее релаксед чем ppc (и тем более альфа), но намного более релаксед чем х86
Vsevolod
гм, вроде, ненамного, надо бы освежить память
Good
мипсы на уровне арма, имхо
Good
бранч делей слот это вообще не из той оперы )
Vsevolod
да, был неправ насчет aarch64
Good
но вообще, мой поинт в том, что надо сначала обеспечить корректность (желательно минимально необходимыми средствами) а уже потом думать о производительности
Vsevolod
https://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html
Good
а дефолтный seq_cst решает эту проблему очень грубо )
Tim
Всем привет. Я об одну задачу мозг сломал - может подскажет кто решение. Хочется сделать шаблонный класс, у которого есть обязательный набор параметров_типов и один опциональный параметр_типа в конце - чтобы если он был указан, то в классе бы создавалось паблик поле этого типа. Пока что написал такое (я в шаблонах новичок, может я вообще глупость в коде написал, но хотя бы будет понятно чего я хочу), но оно не компилится (пишет main.cpp(17): error C2976: 'MyClass' : too few template arguments): template<typename MainClass, typename PayloadClass, int PayloadNum> class MyClass { MainClass field; PayloadClass payload[PayloadNum]; }; template<typename MainClass, typename PayloadClass> class MyClass<MainClass, PayloadClass, 1>; template<typename MainClass> class MyClass<MainClass, int, 0>; int main(int argc, char** argv) { MyClass<int> m; // <--- too few template arguments std::cout << sizeof(m) << std::endl; return 0; } вариант с std::array<PayloadClass, PayloadNum> не нравится - так как когда PayloadNum равен нулю, оно всё равно занимает 4 байта( Хотя бы подскажите в какую сторону смотреть - а там я разберусь)
RattenKong 🦧🐀🦍️
Всем привет. Я об одну задачу мозг сломал - может подскажет кто решение. Хочется сделать шаблонный класс, у которого есть обязательный набор параметров_типов и один опциональный параметр_типа в конце - чтобы если он был указан, то в классе бы создавалось паблик поле этого типа. Пока что написал такое (я в шаблонах новичок, может я вообще глупость в коде написал, но хотя бы будет понятно чего я хочу), но оно не компилится (пишет main.cpp(17): error C2976: 'MyClass' : too few template arguments): template<typename MainClass, typename PayloadClass, int PayloadNum> class MyClass { MainClass field; PayloadClass payload[PayloadNum]; }; template<typename MainClass, typename PayloadClass> class MyClass<MainClass, PayloadClass, 1>; template<typename MainClass> class MyClass<MainClass, int, 0>; int main(int argc, char** argv) { MyClass<int> m; // <--- too few template arguments std::cout << sizeof(m) << std::endl; return 0; } вариант с std::array<PayloadClass, PayloadNum> не нравится - так как когда PayloadNum равен нулю, оно всё равно занимает 4 байта( Хотя бы подскажите в какую сторону смотреть - а там я разберусь)
ты забыл остальные аргументы для шаблона указать
Tim
так я хочу чтобы я мог указывать либо 2 параметра либо 1. И когда их 2, то чтобы в классе присутствовало поле payload этого типа
Berkus
тем более, что на x86 даже acquire/release не добавляет lock префикса
у x86 одна из самых сильных memory orderings, теперь попробуй то же самое на альфе
Berkus
а писать ожидая что все будет собираться только на х86 как минимум тупо
Berkus
бранч делей слот это вообще не из той оперы )
это спарковская хуета изначально
Tim
Сделай 2 шаблонных класса, один с полем, другой без
так а весь остальной код дублировать придётся?
Berkus
CRTP в зубы
Tim
унаследуй
тогда у меня с каждым объектом будет таскаться указатель на таблицу виртуальных функций, нет?
Berkus
таблица появляется когда ты вводишь виртуальные функции
Tim
о, спасибо) я думал они всегда таскаются)
Vsevolod
@berkus альфа сдохла, опомнись
Vsevolod
как и memory_order_consume, хех
Max
таблица появляется когда ты вводишь виртуальные функции
К стати, если есть хоть один виртуальный метод, будет ли влияние на перформанс при вызове невиртуальных методов?
Vsevolod
с чего вдруг?
Vsevolod
более того, сейчас даже виртуальные методы зачастую зовутся без vtable, если компилятор достаточно уверен, что этот метод может быть отрезолвлен при компиляции
Max
с чего вдруг?
навеяно давним вопросом https://stackoverflow.com/questions/667634/what-is-the-performance-cost-of-having-a-virtual-method-in-a-c-class
Vsevolod
от размера объекта скорость вызова невиртуального метода не зависит
Berkus
она вообще не от чего не зависит это просто load this + call
Vsevolod
угу
Vsevolod
но она может зависеть от аггресивности инлайнинга и размера icache. в случае c++ это очень даже актуальная проблема
Berkus
да, инлайн тут влияет
Tim
нештяк, парни, спасибо) так заработало: template<typename MainClass> class TaskWithoutPayload { MainClass main_field; }; template<typename MainClass, typename PayloadClass> class TaskWithPayload : public TaskWithoutPayload<MainClass> { PayloadClass payload; };
Tim
так а чем плохо?
Max
так а чем плохо?
> Хочется сделать шаблонный класс, у которого есть обязательный набор параметров_типов и один опциональный параметр_типа в конце - чтобы если он был указан, то в классе бы создавалось паблик поле этого типа. Сама постановка задачи. Зачем?
Tim
ну типа я сделал достаточно общий движок, и хочу дать пользователям своего кода возможность опционально навешивать пайлоад на таски (например, приоритет - его уже по факту надо добавить, но я хочу это сделать при создании объекта). И чтобы если пользователю не нужен пэйлоад - это не занимало бы места; а если пэйлоад нужен - то чтобы он лежал рядышком с другими полями и занимал места по минимуму, так как таких объектов будет оч много.
Tim
при инстанцировании одним из параметров указываешь класс, чьи методы будут дёргаться и туда передаваться ссылка на таски. А там уже работаешь с пэйлоадом, т.к. это паблик поле.
Tim
так я ж и сделал в итоге с наследованием - вон выше кусок кода
Tim
class TaskWithPayload : public TaskWithoutPayload<MainClass>
Max
Вот я убрал шаблоны. Что изменилось? class TaskWithoutPayload { MainClass main_field; }; class TaskWithPayload : public TaskWithoutPayload { PayloadClass payload; };
Pepe 🐸
ну тогда это не относится к ускорению от concurrency
Pepe 🐸
ты просто говорил про параллельность
Max
Хотя правильней всего будет такой вариант class Payload { MainClass main_field; }; class TaskWithoutPayload : public Payload { NotPayloadClass blabla; }; class TaskWithPayload : public Payload { PayloadClass payload; };
Tim
Вот я убрал шаблоны. Что изменилось? class TaskWithoutPayload { MainClass main_field; }; class TaskWithPayload : public TaskWithoutPayload { PayloadClass payload; };
видимо ничего не изменилось. это ж просто минимальный пример для демонстрации проблемы. Я изначально не хотел делать наследование т.к. думал там сразу появится указатель на вирт таблицу. Вот получившийся боевой фулл: #ifndef _BG_TASK_H_ #define _BG_TASK_H_ #include <cstdint> #include <string> #include "ToStringTemplate.h" template <typename IdClass> class BgTask { private: IdClass id; uint32_t seconds_since_epoch; // 4 bytes uint8_t parents_done_num; // 1 byte public: BgTask() : seconds_since_epoch(0), parents_done_num(0){}; // for unordered_map BgTask(const IdClass & id_) : id(id_), seconds_since_epoch(0), parents_done_num(0){}; ~BgTask(){}; IdClass GetId() const { return id; }; std::string GetIdAsStr(){ return to_string(id); }; uint8_t GetParentsDoneCounter() const { return parents_done_num; }; void SetParentsDoneCounterTo1(){ parents_done_num = 1; }; void IncrementParentsDoneCounter(){ parents_done_num++; }; uint32_t GetSeconds() const { return seconds_since_epoch; }; void SetSeconds(uint32_t seconds){ seconds_since_epoch = seconds; }; }; template<typename IdClass, typename PayloadClass> class BgTaskWithPayload : public BgTask<IdClass> { public: PayloadClass payload; }; #endif // _BG_TASK_H_
Berkus
видимо ничего не изменилось. это ж просто минимальный пример для демонстрации проблемы. Я изначально не хотел делать наследование т.к. думал там сразу появится указатель на вирт таблицу. Вот получившийся боевой фулл: #ifndef _BG_TASK_H_ #define _BG_TASK_H_ #include <cstdint> #include <string> #include "ToStringTemplate.h" template <typename IdClass> class BgTask { private: IdClass id; uint32_t seconds_since_epoch; // 4 bytes uint8_t parents_done_num; // 1 byte public: BgTask() : seconds_since_epoch(0), parents_done_num(0){}; // for unordered_map BgTask(const IdClass & id_) : id(id_), seconds_since_epoch(0), parents_done_num(0){}; ~BgTask(){}; IdClass GetId() const { return id; }; std::string GetIdAsStr(){ return to_string(id); }; uint8_t GetParentsDoneCounter() const { return parents_done_num; }; void SetParentsDoneCounterTo1(){ parents_done_num = 1; }; void IncrementParentsDoneCounter(){ parents_done_num++; }; uint32_t GetSeconds() const { return seconds_since_epoch; }; void SetSeconds(uint32_t seconds){ seconds_since_epoch = seconds; }; }; template<typename IdClass, typename PayloadClass> class BgTaskWithPayload : public BgTask<IdClass> { public: PayloadClass payload; }; #endif // _BG_TASK_H_
#ifndef _BG_TASK_H_ #define _BG_TASK_H_ это можно выкинуть и поменять на #pragma once
Tim
#ifndef _BG_TASK_H_ #define _BG_TASK_H_ это можно выкинуть и поменять на #pragma once
а если я под линуксом это буду собирать - эта прагма там сработает?
Berkus
видимо ничего не изменилось. это ж просто минимальный пример для демонстрации проблемы. Я изначально не хотел делать наследование т.к. думал там сразу появится указатель на вирт таблицу. Вот получившийся боевой фулл: #ifndef _BG_TASK_H_ #define _BG_TASK_H_ #include <cstdint> #include <string> #include "ToStringTemplate.h" template <typename IdClass> class BgTask { private: IdClass id; uint32_t seconds_since_epoch; // 4 bytes uint8_t parents_done_num; // 1 byte public: BgTask() : seconds_since_epoch(0), parents_done_num(0){}; // for unordered_map BgTask(const IdClass & id_) : id(id_), seconds_since_epoch(0), parents_done_num(0){}; ~BgTask(){}; IdClass GetId() const { return id; }; std::string GetIdAsStr(){ return to_string(id); }; uint8_t GetParentsDoneCounter() const { return parents_done_num; }; void SetParentsDoneCounterTo1(){ parents_done_num = 1; }; void IncrementParentsDoneCounter(){ parents_done_num++; }; uint32_t GetSeconds() const { return seconds_since_epoch; }; void SetSeconds(uint32_t seconds){ seconds_since_epoch = seconds; }; }; template<typename IdClass, typename PayloadClass> class BgTaskWithPayload : public BgTask<IdClass> { public: PayloadClass payload; }; #endif // _BG_TASK_H_
BgTask() : seconds_since_epoch(0), parents_done_num(0){}; // for unordered_map delegating ctor? нет, см ниже
Berkus
видимо ничего не изменилось. это ж просто минимальный пример для демонстрации проблемы. Я изначально не хотел делать наследование т.к. думал там сразу появится указатель на вирт таблицу. Вот получившийся боевой фулл: #ifndef _BG_TASK_H_ #define _BG_TASK_H_ #include <cstdint> #include <string> #include "ToStringTemplate.h" template <typename IdClass> class BgTask { private: IdClass id; uint32_t seconds_since_epoch; // 4 bytes uint8_t parents_done_num; // 1 byte public: BgTask() : seconds_since_epoch(0), parents_done_num(0){}; // for unordered_map BgTask(const IdClass & id_) : id(id_), seconds_since_epoch(0), parents_done_num(0){}; ~BgTask(){}; IdClass GetId() const { return id; }; std::string GetIdAsStr(){ return to_string(id); }; uint8_t GetParentsDoneCounter() const { return parents_done_num; }; void SetParentsDoneCounterTo1(){ parents_done_num = 1; }; void IncrementParentsDoneCounter(){ parents_done_num++; }; uint32_t GetSeconds() const { return seconds_since_epoch; }; void SetSeconds(uint32_t seconds){ seconds_since_epoch = seconds; }; }; template<typename IdClass, typename PayloadClass> class BgTaskWithPayload : public BgTask<IdClass> { public: PayloadClass payload; }; #endif // _BG_TASK_H_
BgTask() : seconds_since_epoch(0), parents_done_num(0){}; // for unordered_map BgTask(const IdClass & id_) : id(id_), seconds_since_epoch(0), parents_done_num(0){}; это практически требует инлайн инициализации мемберов класса uint32_t seconds_since_epoch {0}; // 4 bytes uint8_t parents_done_num {0}; // 1 byte .... BgTask() = default; BgTask(const IdClass & id_) : id(id_){};
Tim
BgTask() : seconds_since_epoch(0), parents_done_num(0){}; // for unordered_map delegating ctor? нет, см ниже
мне буквально позавчера то ли компилятор то ли статический анализатор писали, что конструкторы должны инициализировать все поля, поэтому плохо вызывать delegating ctor для инициализации только части полей
Pepe 🐸
ну так в этом и есть смысл лок фри структур, ибо дизайнить струткуры без дедлоков тяжело
Berkus
видимо ничего не изменилось. это ж просто минимальный пример для демонстрации проблемы. Я изначально не хотел делать наследование т.к. думал там сразу появится указатель на вирт таблицу. Вот получившийся боевой фулл: #ifndef _BG_TASK_H_ #define _BG_TASK_H_ #include <cstdint> #include <string> #include "ToStringTemplate.h" template <typename IdClass> class BgTask { private: IdClass id; uint32_t seconds_since_epoch; // 4 bytes uint8_t parents_done_num; // 1 byte public: BgTask() : seconds_since_epoch(0), parents_done_num(0){}; // for unordered_map BgTask(const IdClass & id_) : id(id_), seconds_since_epoch(0), parents_done_num(0){}; ~BgTask(){}; IdClass GetId() const { return id; }; std::string GetIdAsStr(){ return to_string(id); }; uint8_t GetParentsDoneCounter() const { return parents_done_num; }; void SetParentsDoneCounterTo1(){ parents_done_num = 1; }; void IncrementParentsDoneCounter(){ parents_done_num++; }; uint32_t GetSeconds() const { return seconds_since_epoch; }; void SetSeconds(uint32_t seconds){ seconds_since_epoch = seconds; }; }; template<typename IdClass, typename PayloadClass> class BgTaskWithPayload : public BgTask<IdClass> { public: PayloadClass payload; }; #endif // _BG_TASK_H_
std::string GetIdAsStr(){ return to_string(id); }; это вообще зачем?
Tim
чтобы в json отдавать как строку. а хранить оптимально. например если там mongo_id
Pepe 🐸
ну так в этом и есть смысл лок фри структур, ибо дизайнить струткуры без дедлоков тяжело
разница с параллельным программированием, что в паралл. прог. ты сам контролируешь весь пайплайн, поэтомц если у тебя дедлок возможен то видимо уже что то не так
Berkus
видимо ничего не изменилось. это ж просто минимальный пример для демонстрации проблемы. Я изначально не хотел делать наследование т.к. думал там сразу появится указатель на вирт таблицу. Вот получившийся боевой фулл: #ifndef _BG_TASK_H_ #define _BG_TASK_H_ #include <cstdint> #include <string> #include "ToStringTemplate.h" template <typename IdClass> class BgTask { private: IdClass id; uint32_t seconds_since_epoch; // 4 bytes uint8_t parents_done_num; // 1 byte public: BgTask() : seconds_since_epoch(0), parents_done_num(0){}; // for unordered_map BgTask(const IdClass & id_) : id(id_), seconds_since_epoch(0), parents_done_num(0){}; ~BgTask(){}; IdClass GetId() const { return id; }; std::string GetIdAsStr(){ return to_string(id); }; uint8_t GetParentsDoneCounter() const { return parents_done_num; }; void SetParentsDoneCounterTo1(){ parents_done_num = 1; }; void IncrementParentsDoneCounter(){ parents_done_num++; }; uint32_t GetSeconds() const { return seconds_since_epoch; }; void SetSeconds(uint32_t seconds){ seconds_since_epoch = seconds; }; }; template<typename IdClass, typename PayloadClass> class BgTaskWithPayload : public BgTask<IdClass> { public: PayloadClass payload; }; #endif // _BG_TASK_H_
uint32_t GetSeconds() const { return seconds_since_epoch; }; void SetSeconds(uint32_t seconds){ seconds_since_epoch = seconds; }; зачем тогда приватное поле?
Berkus
видимо ничего не изменилось. это ж просто минимальный пример для демонстрации проблемы. Я изначально не хотел делать наследование т.к. думал там сразу появится указатель на вирт таблицу. Вот получившийся боевой фулл: #ifndef _BG_TASK_H_ #define _BG_TASK_H_ #include <cstdint> #include <string> #include "ToStringTemplate.h" template <typename IdClass> class BgTask { private: IdClass id; uint32_t seconds_since_epoch; // 4 bytes uint8_t parents_done_num; // 1 byte public: BgTask() : seconds_since_epoch(0), parents_done_num(0){}; // for unordered_map BgTask(const IdClass & id_) : id(id_), seconds_since_epoch(0), parents_done_num(0){}; ~BgTask(){}; IdClass GetId() const { return id; }; std::string GetIdAsStr(){ return to_string(id); }; uint8_t GetParentsDoneCounter() const { return parents_done_num; }; void SetParentsDoneCounterTo1(){ parents_done_num = 1; }; void IncrementParentsDoneCounter(){ parents_done_num++; }; uint32_t GetSeconds() const { return seconds_since_epoch; }; void SetSeconds(uint32_t seconds){ seconds_since_epoch = seconds; }; }; template<typename IdClass, typename PayloadClass> class BgTaskWithPayload : public BgTask<IdClass> { public: PayloadClass payload; }; #endif // _BG_TASK_H_
uint8_t GetParentsDoneCounter() const { return parents_done_num; }; void SetParentsDoneCounterTo1(){ parents_done_num = 1; }; void IncrementParentsDoneCounter(){ parents_done_num++; }; это чот какой-то очень вычурный footgun, для чего?
Berkus
всё так) спасибо)
в некоторых json-ах например ID надо будет числом а не строкой, так что это просто лишнее увеличение интерфейса
Tim
в некоторых json-ах например ID надо будет числом а не строкой, так что это просто лишнее увеличение интерфейса
ну да, но тогда можно вызывать GetId(). Вообще я подумал - я всё же оставлю GetIdAsStr() - мне как-то удобнее мыслить когда это метод, и to_string скрыт под капотом. Вон в scala и rust вообще у чисел есть методы))
Berkus
а тут чем плохо? Вроде всё ровно - сеттер начального значения, инкременты и геттер
то же самое что и публичная переменная, не? смысл то какой делать ее 1 и потом по одному увеличивать до нужного значения? что этот интерфейс решает? почему он публичный?
Tim
просто не люблю делать паблик поля)
Berkus
просто не люблю делать паблик поля)
эффект то тот же, больше кода - такой же функционал. вопрос - зачем
Tim
эффект то тот же, больше кода - такой же функционал. вопрос - зачем
для порядку. когда всё везде одинаково оформлено лично мне проще ориентироваться. А когда тут геттеры-сеттеры, а тут паблик поля - это ещё и в голове держать надо)