0

I have a Linux code.

I would like to pre-allocate 10000 items of different types as circular array. I always know which object type it is.

Since biggest object takes 54 bytes - I want to allocate 10000 x 54 chunk of memory.

Whats the correct pointer arithmetic to retrieve reference to an object with index i ?

x64 architecture

uint8_t cache[10000 * 54];

MyType* o = static_cast<MyType*>(cache + i * 54);
o.Prop1 = 10;

is this right?

EDIT: I need most efficient solution

EDIT2: these are instances of classes not structs (if that makes difference for aligning)

EDIT3: 54 byte is red herring, consider any "appropriate" aligned size, also I compile it with g++ as C++20 on CentOS9

Boppity Bop
  • 9,613
  • 13
  • 72
  • 151
  • The indexing looks correct to me. We would need a [mre] to tell you if the rest of theusage is correct or not. – NathanOliver May 16 '22 at 15:21
  • 2
    seems okay, except are the biggest object is okay with 2-byte alignment? also since you never called a constructor MyType had better not need it – user253751 May 16 '22 at 15:21
  • While the indexing is correct, ```uint8_t``` is not allowed to alias other objects, you should use raw ```char```s or ```std::byte```. – Jonathan S. May 16 '22 at 15:22
  • 1
    @JonathanS. That's not quite the case now. C++20 introduces implicit lifetime types and they are "created" as soon as suitable storage is created. – NathanOliver May 16 '22 at 15:24
  • Even if the objects are created correctly (implicitly or explicitly), the cast can't work. It should be `reinterpret_cast` followed by a call to `std::launder`. – user17732522 May 16 '22 at 15:25
  • @NathanOliver Ah, I forgot about that! Thanks! I'll edit it out. – Jonathan S. May 16 '22 at 15:26
  • Why is this not an array of `MyType*`? You can't have an array of polymorphic types unless you use a pointer (or shared pointer) to the base type. See related answers: [In C++, polymorphism and arrays don't mix.](https://stackoverflow.com/questions/13718527/how-to-make-an-array-with-polymorphism-in-c/13718565#13718565), [You can't treat arrays polymorphically](https://stackoverflow.com/questions/1411844/polymorphism-pointers-to-arrays/1411891#1411891), [wrong type](https://stackoverflow.com/questions/13048301/pointer-to-array-of-base-class-populate-with-derived-class/13048403#13048403). – Wyck May 16 '22 at 15:33
  • 2
    Because the types are obviously not related. There's no polymorphism involved here. – Blindy May 16 '22 at 15:35
  • 2
    Why is this not a union or variant then? – Wyck May 16 '22 at 15:41
  • 1
    That's a much better question, a `union` would go a long way to simplifying this code. – Blindy May 16 '22 at 15:48
  • I need most efficient solution. and i expected object fields aligned automatically.. is it not the case? – Boppity Bop May 16 '22 at 15:57
  • 1
    @BoppityBop `these are instances of classes not structs` Structs are classes. There is no difference between classes declared with `struct` keyword other than the default access control which is public with `struct` and private with `class`. – eerorika May 16 '22 at 16:07

2 Answers2

3

Use std::array<std::variant<Type1, Type2, Type3, ...>, 100000> cache;

Goswin von Brederlow
  • 11,875
  • 2
  • 24
  • 42
  • is it as efficient as say static_cast? or union or anything else? – Boppity Bop May 16 '22 at 15:55
  • @BoppityBop `std::variant` stores type information and only has type-checked accessors. If you don't need the type-safety because you have type information stored somewhere else a `union` will do. – user17732522 May 16 '22 at 16:13
  • @BoppityBop, you can't `static_cast<>` that array to your objects, that's what `static_cast<>` is meant to explicitly prevent. What you're doing is a `reinterpret_cast<>`. – Blindy May 16 '22 at 16:54
  • erm.. how it works now - I got a wrapper class which has `void* Data;` which holds any type of object. then I need to access it as a concrete object I do: `static_cast(Data)` and it works!.. still my question - how variant compares to anything else in terms of cpu cycles? – Boppity Bop May 16 '22 at 17:32
0

The pointer arithmetic is correct, however be very careful about those structure sizes. Unless you prevent padding (with like #pragma pack, for example), the compiler could easily throw a curve ball your way.

Also because your allocation is unaligned, you will be paying a moderate performance tax on every access inside that structure. If memory isn't an issue, you should 64-bit align your objects.

Blindy
  • 65,249
  • 10
  • 91
  • 131
  • Never ever use `#pragma pack` for anything you read or write more than once. The compiler has to access the data byte-by-byte on many architectures for packed data which is horribly slow. – Goswin von Brederlow May 16 '22 at 15:45
  • I disagree completely, having a known pack value is essential for this exercise where you rely on known *all* the types' sizes, to determine the largest allocation needed. Also you make it sound like a packing size isn't used regardless of whether you manually define it or not. A packing value is always used, you just don't know it unless you specify it yourself. – Blindy May 16 '22 at 15:47
  • ok. i dont have pragma pack. i was expecting the data will be aligned by compiler so i never paid attention.. should i? am confused – Boppity Bop May 16 '22 at 15:55
  • P.S. these are instances of classes not structs (if that makes difference) – Boppity Bop May 16 '22 at 16:05
  • @Blindy What are you talking about? There is no pack value or packing size. `#pragma pack` removes all alignment and padding restrictions, period. It produces code for the worst possible case for accessing those structures. – Goswin von Brederlow May 16 '22 at 16:46
  • Ah, okay, I see. You don't know what `#pragma pack` does. Well, I can only suggest your compiler documentation of choice really. – Blindy May 16 '22 at 16:48
  • @BoppityBop It does for older c++ versions for union or variant as that simply didn't work except for a subset of classes. So set the option for a modern c++ version in your compiler. – Goswin von Brederlow May 16 '22 at 16:48
  • @BoppityBop, it is aligned by the compiler, however you came up with this value of 54 bytes for the largest size, when in fact the size depends on your packing value. As long as you understand the implications of your hard coded number and its interaction with the structure packing (class and struct are identical in C++), you're good, however I suggest a more compiler-driven approach to this (perhaps throwing them all in an `union` and getting the `sizeof` of that). – Blindy May 16 '22 at 16:50
  • @Blindy It seems I know better than you what packed does. And by the way, without packed using a maximum size of 54 will be UB/IDB for any struct containing types larger than int16_t on cpus with more than 16bit. So both packed and non packed are basically non starters. – Goswin von Brederlow May 16 '22 at 17:00
  • forget the 54. i have spare RAM. it can be 1024 if anything... I just want an efficient solution. I havent used c++ for 20 yrs if that helps :) – Boppity Bop May 16 '22 at 17:28
  • @GoswinvonBrederlow it doesn't seem that way at all to anyone but you, and I'd appreciate it if you stopped addressing me with non-solicited "help". – Blindy May 16 '22 at 17:39
  • @BoppityBop sure, you can use larger numbers if you want, but you still have to choose a certain number. What I'm suggesting is asking the compiler for that number instead. Efficiency has no influence here, one number or another will only affect correctness. – Blindy May 16 '22 at 17:41
  • 1
    @BoppityBop just use a variant or union and let the compiler figure out the proper size. – Goswin von Brederlow May 16 '22 at 17:47
  • yeah I am trying to ask what kind of performance hit variant or union have. I dont care for size. I care for speed. see my comments under another answer. – Boppity Bop May 16 '22 at 17:48
  • @Blindy - size doesnt matter. i need to know which way is most efficient - a cast, variant, union or anything else... if you look up - the size convo was thrown around without my participation :) – Boppity Bop May 16 '22 at 17:50
  • 1
    Cast and union are identical in terms of speed, a union is just a cast. – Blindy May 16 '22 at 18:07