Malloc vs baru - padding berbeda

110

Saya meninjau kode C ++ orang lain untuk proyek kami yang menggunakan MPI untuk komputasi kinerja tinggi (10 ^ 5 - 10 ^ 6 core). Kode ini dimaksudkan untuk memungkinkan komunikasi antara (berpotensi) mesin yang berbeda pada arsitektur yang berbeda. Dia menulis komentar yang mengatakan sesuatu di sepanjang baris:

Kami biasanya menggunakan newdan delete, tapi di sini saya menggunakan mallocdan free. Hal ini diperlukan karena beberapa kompiler akan memasukkan data secara berbeda saat newdigunakan, yang menyebabkan kesalahan dalam mentransfer data antara platform yang berbeda. Ini tidak terjadi dengan malloc.

Ini tidak sesuai dengan apa pun yang saya ketahui dari pertanyaan standar newvs.malloc

Apa perbedaan antara new / delete dan malloc / free? mengisyaratkan gagasan bahwa kompilator dapat menghitung ukuran suatu objek secara berbeda (tetapi mengapa hal itu berbeda dengan menggunakan sizeof?).

malloc & penempatan baru vs. baru adalah pertanyaan yang cukup populer tetapi hanya berbicara tentang newmenggunakan konstruktor di mana malloctidak, yang tidak relevan dengan ini.

bagaimana malloc memahami keselarasan? mengatakan bahwa memori dijamin selaras dengan baik newatau mallocyang saya pikirkan sebelumnya.

Dugaan saya adalah bahwa dia salah mendiagnosis bugnya sendiri di masa lalu dan menyimpulkannya newdan mallocmemberikan jumlah padding yang berbeda, yang menurut saya mungkin tidak benar. Tetapi saya tidak dapat menemukan jawabannya dengan Google atau dalam pertanyaan sebelumnya.

Bantu saya, StackOverflow, Anda satu-satunya harapan saya!

hcarver
sumber
33
1 untuk penelitian berbagai utas SO saja!
iammilind
7
+1 Mudah salah satu pekerjaan penelitian "bantu-diri-sendiri-sebelum-saya-bertanya-orang lain" terbaik yang pernah saya lihat di SO dalam waktu PANJANG. Seandainya saya bisa memberi suara positif ini beberapa kali lagi.
WhozCraig
1
Apakah kode transfer mengasumsikan bahwa data disejajarkan dengan cara tertentu, misalnya dimulai pada batas delapan byte? Ini bisa berbeda antara mallocdan new, karena newdi beberapa lingkungan mengalokasikan blok, menambahkan beberapa data ke awal dan mengembalikan penunjuk ke lokasi tepat setelah data ini. (Saya setuju dengan yang lain, di dalam blok data, mallocdan newharus menggunakan jenis bantalan yang sama.)
Lindydancer
1
Wow, saya tidak menyangka pertanyaan ini akan sepopuler ini! @Lindydancer, saya rasa tidak ada batasan 8-byte yang diasumsikan. Hal yang menarik sekalipun.
hcarver
1
Salah satu alasan untuk menggunakan satu metode alokasi di atas yang lain adalah ketika "orang lain" melakukan pelepasan objek. Jika "orang lain" ini menghapus objek menggunakan free, Anda harus mengalokasikan menggunakan malloc. (Masalah pad adalah ikan haring merah.)
Lindydancer

Jawaban:

25

IIRC ada satu hal yang pilih-pilih. mallocdijamin untuk mengembalikan alamat yang sesuai untuk semua tipe standar. ::operator new(n)hanya dijamin untuk mengembalikan alamat yang diselaraskan untuk semua tipe standar tidak lebih dari n , dan jika Tbukan tipe karakter maka new T[n]hanya diperlukan untuk mengembalikan alamat yang diselaraskan T.

Tetapi ini hanya relevan ketika Anda memainkan trik khusus implementasi seperti menggunakan beberapa bit bawah penunjuk untuk menyimpan bendera, atau mengandalkan alamat untuk memiliki lebih banyak penyelarasan daripada yang benar-benar dibutuhkan.

Ini tidak memengaruhi padding dalam objek, yang harus memiliki tata letak yang persis sama terlepas dari cara Anda mengalokasikan memori yang ditempati. Jadi sulit untuk melihat bagaimana perbedaan tersebut dapat mengakibatkan kesalahan dalam mentransfer data.

Apakah ada tanda-tanda pendapat penulis komentar tersebut tentang objek di stack atau di global, apakah menurutnya objek "empuk seperti malloc" atau "empuk seperti baru"? Itu mungkin memberi petunjuk dari mana ide itu berasal.

Mungkin dia bingung, tapi mungkin kode dia bicarakan adalah lebih dari perbedaan lurus antara malloc(sizeof(Foo) * n)vs new Foo[n]. Mungkin lebih seperti:

malloc((sizeof(int) + sizeof(char)) * n);

vs.

struct Foo { int a; char b; }
new Foo[n];

Artinya, mungkin dia mengatakan "Saya menggunakan malloc", tetapi berarti "Saya mengemas data secara manual ke lokasi yang tidak selaras daripada menggunakan struct". Sebenarnya malloctidak diperlukan untuk mengemas struct secara manual, tetapi gagal untuk menyadari itu adalah tingkat kebingungan yang lebih rendah. Penting untuk menentukan tata letak data yang dikirim melalui kabel. Implementasi yang berbeda akan memberikan data yang berbeda ketika struct digunakan.

Steve Jessop
sumber
Terima kasih atas poin tentang penyelarasan. Data yang dimaksud adalah array karakter, jadi saya curiga itu bukan hal penyelarasan di sini, atau hal yang terstruktur - meskipun itu juga pemikiran pertama saya.
hcarver
5
@Hbcdev: baik chararray tidak pernah empuk sama sekali, jadi saya akan tetap dengan "bingung" sebagai penjelasannya.
Steve Jessop
5

Rekan Anda mungkin telah new[]/delete[]memikirkan cookie ajaib (ini adalah informasi yang digunakan implementasi saat menghapus larik). Namun, ini tidak akan menjadi masalah jika alokasi yang dimulai pada alamat yang dikembalikan new[]digunakan (berlawanan dengan pengalokasi).

Pengepakan tampaknya lebih mungkin. Variasi dalam ABI dapat (misalnya) menghasilkan jumlah byte tambahan yang berbeda yang ditambahkan di akhir struktur (ini dipengaruhi oleh penyelarasan, pertimbangkan juga array). Dengan malloc, posisi struktur dapat ditentukan dan dengan demikian lebih mudah dibawa ke ABI asing. Variasi ini biasanya dicegah dengan menentukan kesejajaran dan pengemasan struktur transfer.

justin
sumber
2
Inilah yang pertama kali saya pikirkan, masalah "struct lebih besar daripada jumlah bagian-bagiannya". Mungkin dari sinilah idenya berasal.
hcarver
3

Tata letak objek tidak bisa bergantung pada apakah itu dialokasikan menggunakan mallocatau new. Keduanya mengembalikan jenis penunjuk yang sama, dan saat Anda meneruskan penunjuk ini ke fungsi lain, mereka tidak akan tahu bagaimana objek dialokasikan. sizeof *ptrhanya tergantung pada deklarasi ptr, bukan bagaimana itu ditetapkan.

Barmar
sumber
3

Saya pikir Anda benar. Pengisian dilakukan oleh kompiler bukan newatau malloc. Pertimbangan padding akan berlaku bahkan jika Anda mendeklarasikan array atau struct tanpa menggunakan newatau mallocsama sekali. Bagaimanapun, sementara saya dapat melihat bagaimana implementasi yang berbeda dari newdan mallocdapat menyebabkan masalah saat memporting kode antar platform, saya benar-benar gagal untuk melihat bagaimana mereka dapat menyebabkan masalah dalam mentransfer data antar platform.

john
sumber
Saya sebelumnya berasumsi Anda dapat mempertimbangkan newsebagai pembungkus yang bagus untuk malloctetapi tampaknya dari jawaban lain itu tidak sepenuhnya benar. Konsensus tampaknya bahwa padding harus sama dengan keduanya; Saya pikir masalah dengan mentransfer data antar platform hanya muncul jika mekanisme transfer Anda cacat :)
hcarver
0

Ketika saya ingin mengontrol tata letak struktur data lama saya, dengan kompiler MS Visual yang saya gunakan #pragma pack(1). Saya kira arahan precompiler seperti itu didukung untuk sebagian besar kompiler, seperti misalnya gcc .

Ini memiliki konsekuensi menyelaraskan semua bidang struktur satu di belakang yang lain, tanpa ruang kosong.

Jika platform di ujung lain melakukan hal yang sama (yaitu menyusun struktur pertukaran datanya dengan bantalan 1), maka data yang diambil di kedua sisi akan sesuai. Jadi saya tidak pernah bermain-main dengan malloc di C ++.

Paling buruk saya akan mempertimbangkan membebani operator baru sehingga melakukan beberapa hal rumit, daripada menggunakan malloc langsung di C ++.

Stephane Rolland
sumber
Situasi apa yang ada saat Anda ingin mengontrol tata letak struktur data? Hanya penasaran.
hcarver
Dan apakah ada yang tahu tentang kompiler yang mendukung pragma packatau serupa? Saya menyadari itu tidak akan menjadi bagian dari standar.
hcarver
gcc mendukungnya misalnya. dalam situasi apa saya membutuhkan itu: berbagi data biner antara dua bentuk pelat yang berbeda: berbagi aliran biner antara windows dan palmOS, antara windows dan linux. tautan tentang gcc: gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html
Stephane Rolland
0

Ini adalah tebakan liar saya tentang dari mana benda ini berasal. Seperti yang Anda sebutkan, masalahnya ada pada transmisi data melalui MPI.

Secara pribadi, untuk struktur data saya yang rumit yang ingin saya kirim / terima melalui MPI, saya selalu menerapkan metode serialisasi / deserialisasi yang mengemas / membongkar semuanya ke dalam / dari array karakter. Sekarang, karena padding kita tahu bahwa ukuran struktur bisa lebih besar dari ukuran membernya dan dengan demikian kita juga perlu menghitung ukuran unpadded dari struktur data sehingga kita tahu berapa byte yang dikirim / diterima.

Misalnya jika Anda ingin mengirim / menerima std::vector<Foo> Amelalui MPI dengan teknik tersebut, salah jika menganggap ukuran array karakter yang dihasilkan A.size()*sizeof(Foo)secara umum. Dengan kata lain, setiap kelas yang mengimplementasikan metode serialize / deserialize, juga harus mengimplementasikan metode yang melaporkan ukuran array (atau lebih baik lagi menyimpan array dalam container). Ini mungkin menjadi alasan di balik bug. Namun, dengan satu atau lain cara, itu tidak ada hubungannya dengan newvs mallocseperti yang ditunjukkan di utas ini.

mmirzadeh.dll
sumber
Menyalin ke array karakter bisa menjadi masalah - mungkin beberapa inti Anda menggunakan arsitektur little-endian, dan beberapa big-endian (mungkin tidak mungkin, tapi mungkin). Anda harus melakukan XDR-encode mereka atau sesuatu, tetapi Anda bisa menggunakan tipe data MPI yang ditentukan pengguna. Mereka dengan mudah memperhitungkan padding. Tapi saya dapat melihat apa yang Anda katakan tentang kemungkinan penyebab kesalahpahaman - itulah yang saya sebut sebagai masalah "struct lebih besar dari pada jumlah bagiannya".
hcarver
Ya, mendefinisikan tipe data MPI adalah cara lain / benar untuk melakukan ini. Poin bagus tentang kesabaran. Meskipun, saya sangat meragukan hal itu akan terjadi pada cluster yang sebenarnya. Bagaimanapun, saya pikir jika mereka mengikuti strategi yang sama, ini mungkin menyebabkan bug ...
mmirzadeh
0

Dalam c ++: new kata kunci digunakan untuk mengalokasikan beberapa byte tertentu dari memori sehubungan dengan beberapa struktur data. Misalnya, Anda telah menentukan beberapa kelas atau struktur dan Anda ingin mengalokasikan memori untuk objeknya.

myclass *my = new myclass();

atau

int *i = new int(2);

Tetapi dalam semua kasus Anda memerlukan tipe data yang ditentukan (class, struct, union, int, char dll ...) dan hanya byte memori yang akan dialokasikan yang diperlukan untuk objek / variabelnya. (yaitu; kelipatan dari tipe data itu).

Tetapi dalam kasus metode malloc (), Anda bisa mengalokasikan byte memori apa pun dan Anda tidak perlu menentukan tipe data setiap saat. Di sini Anda dapat mengamatinya dalam beberapa kemungkinan malloc ():

void *v = malloc(23);

atau

void *x = malloc(sizeof(int) * 23);

atau

char *c = (char*)malloc(sizeof(char)*35);
Rahul Raina
sumber
-1

malloc adalah jenis fungsi dan baru adalah jenis tipe data di c ++ di c ++, jika kita menggunakan malloc daripada kita harus dan harus menggunakan typecast jika tidak kompiler memberi Anda kesalahan dan jika kita menggunakan tipe data baru untuk alokasi memori daripada kita tidak perlu untuk typecast

hk_043
sumber
1
Saya pikir Anda harus mencoba untuk lebih memperdebatkan jawaban Anda.
Carlo
Ini sepertinya tidak menjawab pertanyaan mereka melakukan hal yang berbeda dengan paddings, yang sebenarnya saya tanyakan di atas.
hcarver