Bool сколько памяти занимает
In C++ you can "pack" the data by using bit-fields. struct Packed < unsigned int flag1 : 1; unsigned int flag2: 1; >; . Most compilers will allocate a full unsigned int , however they deal with the bit-twiddling by themselves when you read / write. Also they deal by themselves with the modulo operations. That is a unsigned small : 4 attribute has a value between 0 and 15, and when it should get to 16, it won't overwrite the preceding bit :)
But note / beware that it's not thread-safe for different threads to write adjacent bitfields in the same object. It is thread-safe for them to write separate bool members of a struct/class. This means compilers are allowed to implement bitfield writes by loading the containing word, doing some bit-manipulation, then just storing the whole word (not doing an atomic CAS). Related: C++ memory model and race conditions on char arrays - that's why word-addressable machines can't use 1-byte char in a C11 or C++11 implementaiton.
13 Answers 13
Because the CPU can't address anything smaller than a byte.
I think bt addresses a byte offset and then tests the bit at a given offset, regardless, when specifying an address you go in bytes. bit offset literals would get a bit wordy (excuse the pun).
@six: You can load the beginning of an array in one register and then the relative "bit offset" into a second. The bit offset is not limited to "within one byte", it can be any 32 bit number.
Well, yes and no. We do have bitfields, and we could have a bitfield pointer, that is address + bit number. Obviously, such a pointer would not be convertible to void* because of the extra storage requirement for the bit number.
@gEdringer if you're trying to cram as much information into sub-byte fields as you can fit, there are always bitfields.
Historically, a byte was the number of bits used to encode a single character of text in a computer and it is for this reason the basic addressable element in many computer architectures.
So byte is the basic addressable unit, below which computer architecture cannot address. And since there doesn't (probably) exist computers which support 4-bit byte, you don't have 4-bit bool etc.
However, if you can design such an architecture which can address 4-bit as basic addressable unit, then you will have bool of size 4-bit then, on that computer only!
"you will have int of size 4-bit then, on that computer only" - no you won't, because the standard forbids CHAR_BIT from being less than 8. If the addressable unit on the architecture is less than 8 bits, then a C++ implementation will just have to present a memory model that's different from the underlying hardware's memory model.
you can't have a 4-bit bool either, because the char is the smallest addressable unit in C++, regardless of what the architecture can address with its own opcodes. sizeof(bool) must have a value of at least 1, and adjacent bool objects must have their own addresses in C++, so the implementation just has to make them bigger and waste memory. That's why bit fields exist as a special case: the bitfield members of a struct aren't required to be separately addressable, so they can be smaller than a char (although the whole struct still can't be).
@ Steve Jessop : that seems interesting. could you please give me the reference from the language specification where it says char is the smallest addressable unit in C++?
closest specific statement is probably 3.9/4: "The object representation of an object of type T is the sequence of N unsigned char objects taken up by the object of type T, where N equals sizeof(T)". Obviously sizeof(bool) can't be 0.5 :-) I suppose an implementation could legally provide sub-byte pointers as an extension, but "ordinary" objects like bool, allocated in ordinary ways, have to do what the standard says.
Back in the old days when I had to walk to school in a raging blizzard, uphill both ways, and lunch was whatever animal we could track down in the woods behind the school and kill with our bare hands, computers had much less memory available than today. The first computer I ever used had 6K of RAM. Not 6 megabytes, not 6 gigabytes, 6 kilobytes. In that environment, it made a lot of sense to pack as many booleans into an int as you could, and so we would regularly use operations to take them out and put them in.
Today, when people will mock you for having only 1 GB of RAM, and the only place you could find a hard drive with less than 200 GB is at an antique shop, it's just not worth the trouble to pack bits.
Except when dealing with Flags. Things like Setting multiple options on something. eg. 00000001 + 00000100 = 00000101.
@Atomix: I almost never do this anymore. If I need two flags, I create two boolean fields. I used to write code where I'd pack flags like that and then write "if flags & 0x110 != 0 then" or the like, but this is cryptic and these days I generally make separate fields and write "if fooFlag || barFlag" instead. I wouldn't rule out the possibility of cases where packing flags like that is better for some reason, but it's no longer necessary to save memory like it used to be.
Actually, it is quite worth your trouble to pack bits, if you want your computation to be fast - on that large amount of data you store in memory. Packing booleans isn't just for smaller storage - it means you can read your boolean input arrays 8 times faster (in terms of bandwidth) as when they're unpacked, and that's often quite significant. Also, you can use bit operations, like popc (population count) which speeds up your work on the CPU itself.
Truly huge number of booleans is what you work with every day if you do: DBMSes, machine learning, scientific simulations, and a whole host of other things. And - just working on them means copying them - from memory into cache. A million bools is nothing, think billions.
@PeterCordes Yes, absolutely, if I had a set of booleans that were logically the "same idea" so that I naturally think of them as an "array" in some sense, and if I'm then going to mask or filter them or otherwise perform bitwise operations on them, then packing them into bytes might make good sense. As I said earlier, I'm hard pressed to think of the last time I worked on an application where those conditions applied, but you give a couple of good examples, and I'm sure with a little imagination one could think of others.
The easiest answer is; it's because the CPU addresses memory in bytes and not in bits, and bitwise operations are very slow.
However it's possible to use bit-size allocation in C++. There's std::vector specialization for bit vectors, and also structs taking bit sized entries.
Not sure I would agree that bitwise operations are slow. ands, nots, xors etc are very fast. It is typically the implementation of the bitwise operations that are slow. At the machine level they are quite fast. Branching. now that is slow.
Just to make it more clear, if you create a vector of booleans and put 24 booleans into it, it will be taking 3 bytes only (3*8). If you put another boolean in, it will take another byte. Yet, if you push another boolean, it won't take any extra bytes because it uses the "free" bits in the last byte
The bit vectors do not create bit-sized allocations. they create byte-sized allocations. It is not possible to allocate a single bit.
Reading a single bit in a bit vector requires three operations: shift, and, and another shift again. Writing is two. Whereas individual bytes can be accessed with a single one.
Because a byte is the smallest addressible unit in the language.
But you can make bool take 1 bit for example if you have a bunch of them eg. in a struct, like this:
You could have 1-bit bools and 4 and 2-bit ints. But that would make for a weird instruction set for no performance gain because it's an unnatural way to look at the architecture. It actually makes sense to "waste" a better part of a byte rather than trying to reclaim that unused data.
The only app that bothers to pack several bools into a single byte, in my experience, is Sql Server.
You can use bit fields to get integers of sub size.
Though it is usually used to map structures to exact hardware expected bit patterns:
bool can be one byte -- the smallest addressable size of CPU, or can be bigger. It's not unusual to have bool to be the size of int for performance purposes. If for specific purposes (say hardware simulation) you need a type with N bits, you can find a library for that (e.g. GBL library has BitSet class). If you are concerned with size of bool (you probably have a big container,) then you can pack bits yourself, or use std::vector that will do it for you (be careful with the latter, as it doesn't satisfy container requirments).
Think about how you would implement this at your emulator level.
Because in general, CPU allocates memory with 1 byte as the basic unit, although some CPU like MIPS use a 4-byte word.
However vector deals bool in a special fashion, with vector one bit for each bool is allocated.
I believe even the MIPS cpu will give you access to an individual byte, although there is a performance penalty.
@PaulTomblin: you're correct, DEC Alpha is the only ISA in recent memory with byte-addressable memory but without byte actual byte load/store instructions. (See Can modern x86 hardware not store a single byte to memory? for details).
The byte is the smaller unit of digital data storage of a computer. In a computer the RAM has millions of bytes and anyone of them has an address. If it would have an address for every bit a computer could manage 8 time less RAM that what it can.
Even when the minimum size possible is 1 Byte, you can have 8 bits of boolean information on 1 Byte:
Julia language has BitArray for example, and I read about C++ implementations.
Bitwise operations are not 'slow'.
And/Or operations tend to be fast.
The problem is alignment and the simple problem of solving it.
CPUs as the answers partially-answered correctly are generally aligned to read bytes and RAM/memory is designed in the same way.
So data compression to use less memory space would have to be explicitly ordered.
У Вас когда-либо случались такие ситуации, когда Ваше Java приложение трещит по швам? В моём случае это случилось из-за нехватки доступной оперативной памяти. И, естественно, обнаружилась нехватка в самый неподходящий момент: на носу очередной долгожданный релиз, один из серверов остановлен для обновления кода и данных и реинкарнация старого кода уже невозможна, в ближайшие дни запланировано несколько совещаний и собеседований, что сильно отвлекает от процесса оптимизации — в общем, ЧП не прошло незамеченным.
К слову сказать, сделай я правильный backup и экстренные работы по восстановлению жизнеспособности прошли бы гораздо более спокойно, но это была бы уже совсем другая история. Итак в моём распоряжении есть код, которому не хватает 15Gb оперативной памяти для нормального функционирования и очень длительный и дорогостоящий процесс запуска (около 5 часов), в ходе работы которого можно только сидеть со скрещенными пальцами и надеятся, что в этот раз заветные слова OutOfMemoryException не появятся в консоли удалённого сервера.
Не буду описывать всех ухищрений, которые пришлось проделать, чтобы восстановить остановленный сервер в течении трёх дней, но одним своим мини открытием поделюсь — boolean — это не тот тип данных, который Вы хотите использовать в высоконагруженных системах. Внимание вопрос:
Как Вы думаете, сколько памяти занимает boolean например на Ubuntu server x64?
Правильным ответом будет: неизвестно и зависит только от реализации JVM.
Рассмотрим распространённую Sun JVM и прочтем в спецификации виртуальной машины, что boolean типа в ней нет как такового, вместо него используется int! А это означает, что для хранения значения типа «да\нет» используется ровно 32 бита, независимо от архитектуры процессора. Правда в том же разделе мы видим, что произведена оптимизация для работы с массивами boolean, которые преобразуются в массив байт, что даёт прирост доступной памяти в 4 раза. И всё же платить за хранение нолика или еденички семью лишними битами — иногда просто кощунство и издевательство над серверами (особенно при размерах массивов в 500 миллионов элементов).
Спасением в таких случаях будет класс BitSet, который ведёт себя подобно массиву boolean, но упаковывает данные так, что для одного бита выделяется всего один бит памяти (с небольшими издержками для всего массива). BitSet хранит внутри себя массив типа long, а при запросе или установке значения определенного бита — высчитывает индекс нужного long и пользуясь побитовыми операциями и операциями сдвига производит вычисления над единственным битом.
Существует еще более интересная реализация BitSet, OpenBitSet — Apache реализация, которая используется для Lucene. Она гораздо быстрее, но упускает некоторые проверки, проводимые в оригинальном BitSet. Что использовать — решать Вам.
В предыдущей статье много комментаторов были не согласны в необходимости наличия знаний о размере объектов в java. Я категорически не согласен с этим мнением и поэтому подготовил несколько практических приемов, которые потенциально могут пригодится для оптимизации в Вашем приложении. Хочу сразу отметить, что не все из данных приемов могут применяться сразу во время разработки. Для придания большего драматизма, все расчеты и цифры будут приводится для 64-х разрядной HotSpot JVM.
Денормализация модели
Итак, давайте рассмотрим следующий код:
А теперь проведем денормализацию:
Казалось бы — избавились от композиции и все. Но нет. Объект класса Cursor2 потребляет приблизительно на 30% меньше памяти чем объект класса Cursor (по сути Cursor + Position). Такое вот не очевидное следствие декомпозиции. За счет ссылки и заголовка лишнего объекта. Возможно это кажется не важным и смешным, но только до тех пор, пока объектов у Вас мало, а когда счет идет на миллионы ситуация кардинально меняется. Это не призыв к созданию огромных классов по 100 полей. Ни в коем случаем. Это может пригодится исключительно в случае, когда Вы вплотную подошли к верхней границе Вашей оперативной памяти и в памяти у Вас много однотипных объектов.
Используем смещение в свою пользу
Допустим у нас есть 2 класса:
- Бывает возникает ситуации когда думаешь — «стоит ли добавить еще одно поле в класс или сэкономить и высчитать его позже на ходу?». Иногда глупо жертвовать процессорным временем ради экономии памяти, учитывая что никакой экономии может и не быть вовсе.
- Иногда можем добавить поле не тратя память, а в поле хранить дополнительные или промежуточные данные для вычислений или кеша (пример поле hash в классе String).
- Иногда нету никакого смысла использовать byte вместо int, так как за счет выравнивания разница все равно может нивелироваться.
Примитивы и оболочки
Еще раз повторюсь. Но если в Вашем классе поле не должно или не может принимать null значений смело используйте примитивы. Потому что очень уж часто встречается что-то вроде:
Помните, примитивы в среднем занимают в 4 раза меньше памяти. Замена одного поля Integer на int позволит сэкономить 16 байт памяти на объект. А замена одного Long на long — 20 байт. Также снижается нагрузка на сборщик мусора. Вообщем масса преимуществ. Единственная цена — отсутствие null значений. И то, в некоторых ситуациях, если память сильно уж нужна, можно использовать определенные значения в качестве null значений. Но это может повлечь доп. расходы на пересмотр логики приложения.
Boolean и boolean
Отдельно хотел бы выделить эти два типа. Все дело в том, что это самые загадочные типы в java. Так как их размер не определен спецификацией, размер логического типа полностью зависит от Вашей JVM. Что касается Oracle HotSpot JVM, то у всех у них под логический тип выделяется 4 байта, то есть столько же сколько и под int. За хранение 1 бита информации Вы платите 31 битом в случае boolean. Если говорить о массиве boolean, то большинство компиляторов проводит некую оптимизацию и в этом случае boolean будут занимать по байту на значение (ну и не забываем про BitSet).
Ну и напоследок — не используйте тип Boolean. Мне трудно придумать ситуацию, где он реально может потребоваться. Гораздо дешевле с точки зрения памяти и проще с точки зрения бизнес логики использовать примитив, который бы принимал 2 возможных значения, а не 3, как в случае в Boolean.
Сериализация и десериализация
Предположим у Вас есть сериализированая модель приложения и на диске она занимает 1 Гб. И у Вас стоит задача восстановить эту модель в памяти — попросту десериализовать. Вы должны быть готовы к тому, что в зависимости от структуры модели, в памяти она будет занимать от 2Гб до 5Гб. Да да, все опять из-за тех же заголовков, смещений и ссылок. Поэтому иногда может быть полезным содержать большие объемы данных в файлах ресурсов. Но это, конечно, очень сильно зависит от ситуации и это не всегда выход, а иногда и попросту невозможно.
Порядок имеет значение
Допустим у нас есть два массива:
Казалось бы — никакой разницы. Но на самом деле это не так… С точки зрения потребления памяти — разница колоссальна. В первом случае мы имеем 2 ссылки на массив из тысячи элементов. Во втором случае у нас есть тысяча ссылок на массивы c двумя элементами! С точки зрения памяти во втором случае количество потребляемой памяти больше на 998 размеров ссылок. А это около 7кб. Вот так на ровном месте можно потерять достаточно много памяти.
Сжатие ссылок
- Все объекты у которых есть ссылка, теперь занимают на 4 байта меньше на каждую ссылку.
- Сокращается заголовок каждого объекта на 4 байта.
- В некоторых ситуациях возможны уменьшенные выравнивания.
- Существенно уменьшается объем потребляемой памяти.
- Количество возможных объектов упирается в 2^32. Этот пункт сложно назвать минусом. Согласитесь, 4 млрд объектов очень и очень не мало. А еще учитывая, что минимальный размер объекта — 16 байт.
- Появляются доп. расходы на преобразование JVM ссылок в нативные и обратно. Сомнительно, что эти расходы способны хоть как-то реально повлиять на производительность, учитывая что это буквально 2 регистровые операции: сдвиг и суммирование. Детали можно найти тут
Заключение
Надеюсь мне удалось Вас убедить. Часть из этих приемов мне довелось повидать на реальных проектах. И помните, как говаривал Дональд Кнут, преждевременная оптимизация — это корень всех бед.
Для всех примитивных типов данных, кроме типа boolean , в Java однозначно определен размер.
Размер типа boolean , насколько я понимаю, зависит от реализации конкретной виртуальной машины.
Вопрос: почему для типа boolean однозначно не определили размер?
Встречный вопрос. А с какими целями это, по-вашему, может быть надо? Полагаю, что ответ "определять размер однозначно было просто незачем" вас не устроит.
Потому что boolean - 1 бит. А адресовать один бит нельзя. А количество бит/байт которые можно адресовать (читай, минимальный размер памяти, который можно прочитать) в теории зависит от реальной машины, на которой исполняется java. Думаю причина в этом.
7 Answers 7
Because every C++ data type must be addressable.
How would you create a pointer to a single bit? You can't. But you can create a pointer to a byte. So a boolean in C++ is typically byte-sized. (It may be larger as well. That's up to the implementation. The main thing is that it must be addressable, so no C++ datatype can be smaller than a byte)
"byte" addressing is an architectural choice (hw level): one could very well design a system with a different "unit of addressing". For common processors, addressing a "byte" anyhow ends-up fetching more than a "byte" from external memory: this is due to efficiency reasons.
@jldupont: There are a few systems where pointer addresses are finer grained than bytes (I've programmed on the old TI TMS34010/20 before, which uses bit-wise pointers), but they are EXCEEDINGLY rare.
Not sure what you mean. Every object must be addressable, that is, it must be possible to retrieve the address of an object. The object doesn't have to store its own address. A char is typically 8 bits wide, enough to store any of 256 characters, but each char also has an address defined by where in memory it is located. That is why you can create a pointer to a char.
If I may contribute a dodgy analogy: there are eight floors in my building, but the Post Office doesn't acknowledge that they are different addresses. So if I want an address all to myself, then I have to rent the whole building, even though I actually fit on one floor. I'm not using the other seven floors to "store an address", I'm just forced to waste them because of the Post Office rule that addresses refer to buildings, not floors. C++ objects must have an address to themselves - no post rooms to sort the mail after delivery ;-)
Memory is byte addressable. You cannot address a single bit, without shifting or masking the byte read from memory. I would imagine this is a very large reason.
A boolean type normally follows the smallest unit of addressable memory of the target machine (i.e. usually the 8bits byte).
Access to memory is always in "chunks" (multiple of words, this is for efficiency at the hardware level, bus transactions): a boolean bit cannot be addressed "alone" in most CPU systems. Of course, once the data is contained in a register, there are often specialized instructions to manipulate bits independently.
For this reason, it is quite common to use techniques of "bit packing" in order to increase efficiency in using "boolean" base data types. A technique such as enum (in C) with power of 2 coding is a good example. The same sort of trick is found in most languages.
Updated: Thanks to a excellent discussion, it was brought to my attention that sizeof(char)==1 by definition in C++. Hence, addressing of a "boolean" data type is pretty tied to the smallest unit of addressable memory (reinforces my point).
Если поведение однозначно определено, всё остальное вторично.
Тип boolean прекрасно определяется множеством своих допустимых значений. Математически оно ограничивает минимальный размер значения одним битом.
Но использовать именно один бит (и ни битом более) эффективно далеко не всегда, поскольку на популярных архитектурах нельзя адресовать отдельные биты. А потому значения отдельных boolean 'ов нельзя быстро сохранить в отдельные биты оперативной памяти — необходимо использовать комбинацию побитовых операций, что почти наверняка будет медленнее, чем запись целого отдельного регистра в оперативную память (размер которого может быть различным на разных платформах!).
Но при этом, к примеру, я вполне себе представляю, как на x86 компилятор может использовать в нативном коде в качестве какого-нибудь конкретного boolean -значения один бит регистра флагов, при условии, что это значение никогда не попадает в оперативную память (отдельно от других). Поэтому любые ограничения на размер более одного бита тоже могут потенциально мешать.
И эта свобода представления позволяет авторам виртуальных машин Java использовать любые реализации, которые они считают наиболее эффективными в каждом конкретном контексте. Как видно из примеров выше, любые ограничения будут только мешать.
In C++, I'm wondering why the bool type is 8 bits long (on my system), where only one bit is enough to hold the boolean value ?
I used to believe it was for performance reasons, but then on a 32 bits or 64 bits machine, where registers are 32 or 64 bits wide, what's the performance advantage ?
Or is it just one of these 'historical' reasons ?
last time someone thought what you're thinking, we ended up with std::vector
jldupont, I think you misread me. I was asking for a system, where sizeof(bool) would be 4. I could swear that msvc had 32-bit bools, but I just tried and it doesn't.
To be fair, the problem with vector
@avakar - you might be confusing the C++ bool data type with the Windows BOOL type which is typedefed to long . So sizeof(bool) != sizeof(BOOL) , which I'm sure causes a lot of confusion (and probably a fair number of bugs). Particularly since there are also boolean and BOOLEAN typedefs in Windows, which are aliases for unsigned char . Also, note that while it's common for bool to be 1 byte, the C++ standard has a note that specifically indicates that sizeof(bool) can be larger.
3 ответа 3
Читайте также: