Так, але...
Це, звичайно, можливо , але зазвичай це безглуздо (для будь-якої програми, яка не використовує мільярди цих цифр):
#include <stdint.h> // don't want to rely on something like long long
struct bad_idea
{
uint64_t var : 40;
};
Тут var
дійсно буде ширина 40 біт за рахунок набагато менш ефективного коду, що генерується (виявляється, що "багато" - це дуже неправильно - виміряні накладні витрати складають лише 1-2%, див. Таймінги нижче), і зазвичай безрезультатно. Якщо вам не потрібне інше 24-бітове значення (або 8 і 16-бітове значення), яке ви хочете упакувати в ту саму структуру, вирівнювання втратить усе, що ви можете отримати.
У будь-якому випадку, якщо у вас їх не мільярди, фактична різниця у споживанні пам'яті не буде помітною (але додатковий код, необхідний для управління бітовим полем, буде помітний!).
Примітка:
Питання тим часом оновлено, щоб це справді відображало потрібні мільярди чисел, тому це може бути життєздатною справою, передбачаючи, що ви вживаєте заходів, щоб не втратити прибуток через вирівнювання конструкції та відступів, тобто або зберігаючи щось інше в решті 24 біта або зберігаючи свої 40-бітові значення в структурах по 8 або їх кратних).
Заощаджувати три байти в мільярд разів варто, оскільки для цього потрібно буде помітно менше сторінок пам’яті, що призведе до меншої кількості помилок кеш-пам’яті та TLB, і перш за все до несправностей сторінок (помилка однієї сторінки важить десятки мільйонів інструкцій).
Хоча у наведеному фрагменті не використовуються решта 24 біти (він лише демонструє частину "використовувати 40 бітів"), буде потрібно щось подібне до наступного, щоб дійсно зробити підхід корисним у сенсі збереження пам’яті - передбачається, що у вас дійсно є інші "корисні" дані, які потрібно помістити в діри:
struct using_gaps
{
uint64_t var : 40;
uint64_t useful_uint16 : 16;
uint64_t char_or_bool : 8;
};
Розмір та вирівнювання структури будуть дорівнювати 64-бітному цілому числу, тому нічого не витрачається даремно, якщо ви, наприклад, зробите масив з мільярда таких структур (навіть без використання розширень, специфічних для компілятора). Якщо у вас немає використання 8-бітового значення, ви також можете використовувати 48-бітове та 16-бітове значення (що дає більший запас переповнення).
Крім того, ви можете, за рахунок зручності використання, додати 8 40-бітових значень у структуру (найменше спільне кратне 40 і 64 - 320 = 8 * 40). Звичайно, тоді вашого коду, який отримує доступ до елементів масиву структур, стане набагато більше складнішим (хоча, можливо, можна реалізувати такий, operator[]
який відновлює функціональність лінійного масиву і приховує складність структури).
Оновлення:
Написав набір швидких тестів, щоб просто побачити, які накладні витрати мають поля полів (і перевантаження оператора посиланнями на польові поля). Опублікований код (через довжину) на gcc.godbolt.org , тестовий результат на моїй машині Win7-64:
Running test for array size = 1048576
what alloc seq(w) seq(r) rand(w) rand(r) free
-----------------------------------------------------------
uint32_t 0 2 1 35 35 1
uint64_t 0 3 3 35 35 1
bad40_t 0 5 3 35 35 1
packed40_t 0 7 4 48 49 1
Running test for array size = 16777216
what alloc seq(w) seq(r) rand(w) rand(r) free
-----------------------------------------------------------
uint32_t 0 38 14 560 555 8
uint64_t 0 81 22 565 554 17
bad40_t 0 85 25 565 561 16
packed40_t 0 151 75 765 774 16
Running test for array size = 134217728
what alloc seq(w) seq(r) rand(w) rand(r) free
-----------------------------------------------------------
uint32_t 0 312 100 4480 4441 65
uint64_t 0 648 172 4482 4490 130
bad40_t 0 682 193 4573 4492 130
packed40_t 0 1164 552 6181 6176 130
Що можна помітити, так це те, що зайві накладні витрати на польові поля нехтують, але перевантаження оператора посиланням на розрядність як зручність є досить різким (приблизно в 3 рази) при лінійному доступі до даних у кеш-формі. З іншого боку, для довільного доступу це ледве навіть важливо.
Ці терміни дозволяють припустити, що просто використання 64-розрядних цілих чисел було б краще, оскільки вони все-таки швидші в цілому, ніж бітові поля (незважаючи на збільшення кількості пам'яті), але, звичайно, вони не враховують вартість несправностей сторінок із набагато більшими наборами даних. Це може виглядати зовсім по-іншому, коли у вас закінчиться фізична оперативна пам’ять (я цього не тестував).