Array besar berukuran 1mega menyebabkan CPU tinggi?

Saya memiliki aplikasi server multithread. Aplikasi ini menerima data dari soket kemudian menangani data tersebut seperti membongkar paket, menambah antrian data, dll, fungsinya seperti di bawah ini. Fungsi ini sering dipanggil. Ada pernyataan pilih dan jika ditemukan ada data maka akan memanggil fungsi ini untuk menerima):

         //the main function used to receive 
         //file data from clients
         void service(void){
              while(1){
                   ....
                   struct timeval timeout;
                   timeout.tv_sec = 3;

                   ...
                   ret = select(maxFd+1, &read_set, NULL, NULL, &timeout);
                   if (ret > 0){
                       //get socket from SocketsMap
                       //if fd in SocketsMap and its being set
                       //then receive data from the socket
                       receive_data(fd);
                   }
              }
         } 

         void receive_data(int fd){
              const int ONE_MEGA = 1024 * 1024;

              //char *buffer = new char[ONE_MEGA]; consumes much less CPU
              char buffer[ONE_MEGA]; // cause high CPU 
              int readn = recv(fd, buffer, ONE_MEGA, 0);

              //handle the data
         }

Saya menemukan cara di atas memakan terlalu banyak CPU -- biasanya 80% hingga 90%, tetapi jika saya membuat buffer dari heap, CPU hanya 14%. Mengapa?

[perbarui]
Menambahkan lebih banyak kode

[update2]
Yang paling menarik adalah saya juga menulis server dan klien penerima data sederhana lainnya. Server hanya menerima data dari soket lalu membuangnya. Kedua jenis pengalokasian ruang ini bekerja hampir sama, tidak ada perbedaan besar dalam penggunaan CPU. Dalam aplikasi server multithread yang bermasalah, saya bahkan mereset ukuran tumpukan proses menjadi 30M, menggunakan array masih menimbulkan masalah, tetapi mengalokasikan dari heap menyelesaikannya. Saya tidak tahu kenapa.

Mengenai "sizeof(buffer)", terima kasih telah menunjukkan hal ini, tetapi saya 100% yakin itu bukan masalahnya, karena dalam aplikasi saya, saya tidak menggunakan sizeof(buffer), melainkan ONE_MEGA (1024*1024) .

Ngomong-ngomong, ada satu hal lagi yang perlu disebutkan meski saya tidak yakin itu berguna atau tidak. Mengganti array dengan array yang lebih kecil seperti "char buffer[1024]; juga mengurangi penggunaan CPU secara drastis.

[update3]
Semua soket berada dalam mode non-pemblokiran.


person Wallace    schedule 29.07.2013    source sumber
comment
Tidak masuk akal bagi saya.   -  person Mats Petersson    schedule 29.07.2013
comment
hai Petersson, maaf saya tidak begitu mengerti mengapa Anda berkata demikian. Ini mungkin fakta bahwa mengalokasikan ukuran tumpukan array yang besar sering kali menyebabkan CPU tinggi, tetapi dapatkah Anda membantu menjelaskannya?   -  person Wallace    schedule 29.07.2013
comment
Apa proses yang Anda lakukan saat Anda melakukan strace? Apakah /bin/time secara konsisten menunjukkan jumlah kesalahan laman yang berbeda untuk kedua versi?   -  person Useless    schedule 29.07.2013
comment
Menurut pengalaman saya, menggunakan tumpukan dalam jumlah besar seharusnya tidak membuat sesuatu menggunakan lebih banyak CPU. Tapi izinkan saya melakukan beberapa eksperimen...   -  person Mats Petersson    schedule 29.07.2013
comment
itu menerima data. Saya menggunakan top untuk melihat penggunaan CPU dan ternyata biasanya mengkonsumsi 80% cpu, tetapi jika saya menggunakan buffer = new char[one_mega] sebagai gantinya, proses yang sama melakukan operasi yang sama hanya mengkonsumsi 14%.   -  person Wallace    schedule 29.07.2013
comment
Seperti halnya masalah kinerja lainnya, apakah Anda mengkompilasi dengan pengoptimalan yang diaktifkan sebelum membandingkan kedua versi?   -  person syam    schedule 29.07.2013
comment
Juga, apakah Anda mengosongkan buffer Anda? Jika tidak, bisa jadi 14% Anda mencerminkan pertukaran...   -  person Mats Petersson    schedule 29.07.2013
comment
Apakah Anda mengubah baris recv untuk pengujian alokasi dinamis? Jika tidak, sizeof(buffer) diteruskan ke recv akan mengatakan bahwa Anda ingin membaca sizeof(char*) (mungkin 4 atau 8 byte) daripada 1Mb   -  person simonc    schedule 29.07.2013
comment
@syam, keduanya menggunakan level optimasi O3   -  person Wallace    schedule 29.07.2013
comment
Ya, tentu saja alokasi di stack jauh lebih cepat daripada alokasi di heap, tapi mengapa Anda mengalokasikan setiap kali Anda menerima, mengapa tidak mengalokasikan satu buffer dan menyimpannya selamanya?   -  person PlasmaHH    schedule 29.07.2013
comment
@PlasmaHH: Saya tidak percaya mengalokasikan sekali atau berkali-kali akan membuat banyak perbedaan - setelah Anda membagi tumpukan, itu mungkin akan memberi Anda jumlah memori yang sama setiap kali [dengan asumsi tidak terlalu banyak alokasi lain terjadi, tentu saja]. Dan itu akan memberikan penggunaan cpu yang LEBIH TINGGI jika alokasi heapnya lambat.   -  person Mats Petersson    schedule 29.07.2013
comment
@PlasmaHH terima kasih atas sarannya, saya seharusnya menggunakannya dengan cara ini. SAYA AKAN!   -  person Wallace    schedule 29.07.2013
comment
Dugaan saya adalah bahwa select() kembali bahkan ketika belum ada data apa pun untuk dibaca, dan kemudian panggilan recv() non-pemblokiran Anda mengembalikan EWOULDBLOCK, dan kemudian Anda kembali ke select() lagi, yang segera bangun lagi, dan seterusnya -- sehingga sibuk melakukan perulangan dan menghabiskan CPU. Mungkin ada baiknya untuk memeriksa ulang apakah Anda memanggil FD_ZERO dan FD_SET dengan benar, dan menguji FD_ISSET sebelum memanggil receiver_data(), dan juga mencetak nilai yang dikembalikan oleh recv() untuk melihat apakah itu benar-benar memberi Anda data saat itu dipanggil atau tidak.   -  person Jeremy Friesner    schedule 31.07.2013


Jawaban (3)


Saya baru saja menulis ini:

#include <iostream>
#include <cstdio>

using namespace std;

static __inline__ unsigned long long rdtsc(void)
{
    unsigned hi, lo;
    __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
    return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}

const int M = 1024*1024;

void bigstack()
{
    FILE *f = fopen("test.txt", "r");
    unsigned long long time;
    char buffer[M];

    time = rdtsc();
    fread(buffer, M, 1, f);
    time = rdtsc() - time;
    fclose(f);
    cout << "bs: Time = " << time / 1000 << endl;
}


void bigheap()
{
    FILE *f = fopen("test.txt", "r");
    unsigned long long time;
    char *buffer = new char[M];

    time = rdtsc();
    fread(buffer, M, 1, f);
    time = rdtsc() - time;
    delete [] buffer;
    fclose(f);
    cout << "bh: Time = " << time / 1000 << endl;
}



int main()
{
    for(int i = 0; i < 10; i++)
    {
    bigstack();
    bigheap();
    }
}

Outputnya kira-kira seperti ini:

bs: Time = 8434
bh: Time = 7242
bs: Time = 1094
bh: Time = 2060
bs: Time = 842
bh: Time = 830
bs: Time = 785
bh: Time = 781
bs: Time = 782
bh: Time = 804
bs: Time = 782
bh: Time = 778
bs: Time = 792
bh: Time = 809
bs: Time = 785
bh: Time = 786
bs: Time = 782
bh: Time = 829
bs: Time = 786
bh: Time = 781

Dengan kata lain, mengalokasikan dari tumpukan heap sama sekali tidak ada bedanya. Sejumlah kecil "kelambatan" pada awalnya berkaitan dengan "pemanasan cache".

Dan saya cukup yakin bahwa alasan kode Anda berperilaku berbeda di antara keduanya adalah hal lain - mungkin yang dikatakan simonc: sizeof buffer apakah masalahnya?

person Mats Petersson    schedule 29.07.2013
comment
wow, Anda menulis aplikasi pengujian dengan sangat cepat, luar biasa! Menyetel ulang array ke ukuran 1024byte juga mengurangi penggunaan CPU secara drastis! Apakah informasi ini berguna? - person Wallace; 29.07.2013
comment
Saya pikir waktu terlama adalah menghasilkan file 1MB untuk benar-benar dibaca... ;) - Saya tidak bisa menjelaskan mengapa menggunakan buffer yang lebih kecil membuat banyak perbedaan. Bisakah Anda mengonfirmasi bahwa Anda menggunakan ONE_MEGA sebagai ukuran saat Anda menggunakan new untuk buffer? - person Mats Petersson; 29.07.2013
comment
ya, saya 100% yakin, saya menghabiskan sepanjang sore bermain-main dengan cuplikan kode. Saya tidak bisa menjelaskan mengapa array menyebabkan aplikasi mengkonsumsi lebih banyak CPU. Hal pertama yang saya pikirkan adalah ukuran tumpukan, saya ragu tumpukan itu tidak cukup jadi saya mereset ukuran tumpukan menjadi 30M menggunakan setrlimit(), tetapi hasilnya sama. - person Wallace; 29.07.2013
comment
Yah, saya tidak melihat alasan mengapa recv harus jauh berbeda dari fread dalam penanganan memorinya - mungkin diperlukan waktu lebih lama atau lebih pendek untuk benar-benar menyalin data yang diterima, tetapi selain itu, saya tidak mengerti mengapa saya benchmark tidak akan cocok, cukup dekat, dengan apa yang dilakukan sistem Anda. Dengan asumsi sistem Anda adalah sistem x86, Anda dapat menjalankan kode saya (Anda harus membuat file test.txt Anda sendiri dengan sekitar 1 juta data di dalamnya). - person Mats Petersson; 29.07.2013
comment
ini adalah aplikasi server untuk meneruskan file dari clientA ke ClientB, ClientC,... Pertama ia membaca data file dari soket clientA, kemudian menulisnya ke file lokal dan akhirnya meneruskannya ke klien lain. Sekarang saya menemukan operasi membaca soket (membaca data dari soket dan membuangnya) menghabiskan terlalu banyak penggunaan CPU.. - person Wallace; 29.07.2013
comment
Mungkin tembakan buta tetapi coba periksa apakah melintasi batas ukuran buffer ukuran 4k membuat perbedaan. - person BeginEnd; 29.07.2013
comment
Untuk buffer 1MB, akan ada 255 hingga 257 halaman yang melintasi batas, dan biasanya, buffer yang dialokasikan oleh new juga tidak selaras dengan halaman. - person Mats Petersson; 29.07.2013
comment
ya, saya tahu tapi dia menulis Mengganti array dengan yang lebih kecil seperti char buffer[1024]; juga mengurangi penggunaan cpu secara drastis jadi menurut saya dia dapat menemukan ukuran ketika penggunaan CPU tinggi. Mungkin karena petunjuk atau tidak, tetapi Anda tidak akan tahu apakah dia tidak mau memeriksanya. - person BeginEnd; 29.07.2013
comment
Ya, saya tidak tahu. Saya hanya mengatakan bahwa persilangan 255 atau 257 halaman seharusnya hanya menghasilkan sedikit perbedaan, jika ada. - person Mats Petersson; 29.07.2013
comment
@BeginEnd Saya bisa memeriksa batas ukuran ini besok. - person Wallace; 29.07.2013
comment
Petersson, saya sangat menghargai bantuan Anda dalam pertanyaan ini, saya melihat Anda meninggalkan beberapa komentar di postingan saya yang lain. Saya akan menerima komentar Anda pada postingan saya yang lain sebagai jawaban postingan ini, jika Anda tidak keberatan memperbarui komentar Anda pada postingan ini. Sekali lagi terima kasih, petersson. - person Wallace; 29.07.2013
comment
Terima saja jawaban ini, dan beri suara positif pada yang lain? Menyimpan konten yang sama dalam dua jawaban berbeda. - person Mats Petersson; 29.07.2013
comment
Saya tidak melihat alasan untuk percaya bahwa recv melalui jaringan dan fread dari file lokal akan memiliki perilaku serupa. fread memiliki lapisan buffering tambahan, selain kemungkinan pembacaan sebagian dengan recv pada soket aliran. - person Ben Voigt; 30.07.2013
comment
@BenVoigt: Dan itu akan mempengaruhi berapa banyak CPU yang digunakan, berdasarkan dari mana alokasinya berasal? Tentu saja panggilan tersebut berperilaku berbeda dalam beberapa aspek. Namun tidak dalam cara memori tersebut digunakan. Meskipun data di-cache, Oh, saya sudah membaca ini sebelumnya tidak di-cache - setiap pembacaan akan tetap mengisi buffer mode pengguna dengan cara yang sama. Cara data masuk ke sistem seharusnya tidak membuat perbedaan [ingat, OP menggunakan recv setelah sistem mengatakan Anda punya beberapa data untuk ditangani]. Tolong beri tahu saya di mana kesalahan pemikiran saya di sini? - person Mats Petersson; 30.07.2013

Jika semuanya sama, memori tetaplah memori dan tidak masalah apakah buffer Anda ada di heap atau di stack.

Namun yang jelas semuanya tidak sama. Saya menduga alokasi buffer 1M pada tumpukan MENGGANGGU/TUMBUH dengan ruang tumpukan yang dialokasikan ke utas LAINNYA. Artinya, untuk menumbuhkan tumpukan memerlukan relokasi tumpukan thread saat ini, atau merelokasi tumpukan thread lainnya. Ini membutuhkan waktu. Waktu ini tidak diperlukan saat mengalokasikan dari heap atau jika alokasi tumpukan cukup kecil agar tidak mengganggu, seperti pada contoh 1K.

Dengan asumsi Anda menggunakan implementasi thread yang kompatibel dengan Posix, lihatlah

pthread_create
pthread_attr_getstack
pthread_attr_setstack

untuk memberi thread dengan buffer 1M lebih banyak ruang tumpukan pada waktu pembuatan thread.

-Jeff

person Jeff N    schedule 29.07.2013
comment
Saya juga mencurigai hal ini dan mengatur ulang ukuran tumpukan proses menjadi 30M, hanya untuk ternyata tidak berhasil. Aplikasi ini memiliki lebih dari 60 thread yang berjalan di dalamnya. - person Wallace; 31.07.2013

Anda mengabaikan nilai kembalian dari recv. Itu tidak baik. Pembacaan sebagian adalah fakta kehidupan, dan sangat mungkin terjadi jika Anda melewati buffer yang begitu besar. Saat Anda mulai memproses bagian buffer yang tidak berisi data valid, hal yang tidak terduga dapat terjadi.

Ukuran frame maksimum untuk protokol yang paling umum digunakan adalah 64kB. Bahkan mungkin (meskipun tidak mungkin) bahwa sesuatu dalam sistem hanya menggunakan 16 bit terendah dari ukuran buffer, yang kebetulan Anda setel ke nol. Hal ini akan menyebabkan recv segera kembali tanpa melakukan apa pun, mengakibatkan perulangan tanpa akhir dan penggunaan CPU yang tinggi.

Tentu saja semua ini tidak akan berbeda dengan buffer yang dialokasikan secara dinamis, tetapi jika Anda juga menggunakan sizeof (buffer) dan berakhir dengan kode pengguna heap yang hanya membaca potongan berukuran pointer sekaligus, hal ini bisa saja terjadi. berbeda.

person Ben Voigt    schedule 30.07.2013