Linux 頁面記憶體性能與 memcpy
在 Linux 將數據寫入頁面記憶體而不限制程序或將數據同步到磁碟的情況下,我正在對寫入性能進行基準測試。我正在做的簡單實驗如下所示:
long size = 1024; int fd = open ("file", O_CREAT | O_RDWR | O_TRUNC, ...); char *buffer = (char *) malloc (size) **start_time = time.now ();** write (fd, buffer, size); **end_time = time.now ();** close (fd); printf ("write duration %d\n", end_time - start_time);
在這裡,我希望有效地觀察記憶體頻寬,因為數據只複製到作業系統的頁面記憶體,沒有任何東西同步到磁碟。此外,作業系統還沒有啟動後台刷新或限制程序,因為臟率遠低於 background_dirty_ratio。但是,當我將它與記憶體頻寬(相同大小的 memcpy 成本)進行比較時,它比 memcpy 貴得多:
char *buffer1 = (char *) malloc (size) char *buffer2 = (char *) malloc (size) **start_time = time.now ();** memcpy (buffer1, buffer2, size); **end_time = time.now ();** printf ("memcpy duration %d\n", end_time - start_time);
例如在我的系統(Linux 核心版本 4.2,CentOS)上,我看到 memcpy 頻寬接近60GB/s,寫入頻寬接近2GB/s。據我了解,當呼叫 write 系統呼叫時,它只是將數據複製到頁面記憶體中(在記憶體中),並在復製完成後立即返回。所以我希望看到接近記憶體頻寬的頻寬。我還用更大的數據(在作業系統開始限制程序之前)測試了相同的實驗,以降低進行系統呼叫的成本。但是,我仍然看到幾乎相同的結果。有誰知道為什麼我在頁面記憶體上執行寫入時沒有觀察記憶體頻寬?
這裡的主要因素是
memcpy
由 C 庫處理,甚至直接由 C 編譯器write
處理,而係統呼叫由核心處理。因此,您
memcpy
在程序中執行,甚至可能沒有函式呼叫的(微小)成本。write
另一方面,具有系統呼叫的所有成本。特別是在您的測試中,由於寫入大小很小,因此復製本身的成本可能與系統呼叫的成本相形見絀。即使進行較大的測試,系統呼叫成本可能仍然是主要因素。要減少系統呼叫的重量,請嘗試與更大的大小進行比較,必要時更改臟比率配置,或寫入 a
tmpfs
以避免寫入磁碟的成本。您可能還想禁用 KPTI 和其他會增加系統呼叫成本的緩解措施,並可能查看 io_uring。為了更好地了解處理 a 所涉及的工作
write
,您可以在 x86-64 上跟踪適當的系統呼叫__x64_sys_write
;這將顯示呼叫鍊和花費的時間,例如9) | __x64_sys_write() { 9) | ksys_write() { 9) | __fdget_pos() { 9) | __fget_light() { 9) 0.465 us | __fget_files(); 9) 0.943 us | } 9) 1.155 us | } 9) | vfs_write() { 9) | rw_verify_area() { 9) | security_file_permission() { 9) | selinux_file_permission() { 9) | __inode_security_revalidate() { 9) | _cond_resched() { 9) 0.034 us | rcu_all_qs(); 9) 0.241 us | } 9) 0.649 us | } 9) | file_has_perm() { 9) 0.034 us | bpf_fd_pass(); 9) | inode_has_perm() { 9) 0.133 us | avc_has_perm(); 9) 0.376 us | } 9) 0.808 us | } 9) 2.126 us | } 9) 0.032 us | bpf_lsm_file_permission(); 9) 2.616 us | } 9) 2.815 us | } 9) | __vfs_write() { 9) | new_sync_write() { 9) | pipe_write() { 9) | mutex_lock() { 9) | _cond_resched() { 9) 0.034 us | rcu_all_qs(); 9) 0.236 us | } 9) 0.566 us | } 9) | _cond_resched() { 9) 0.036 us | rcu_all_qs(); 9) 0.232 us | } 9) 0.036 us | mutex_unlock(); 9) | __wake_up_sync_key() { 9) | __wake_up_common_lock() { 9) 0.036 us | _raw_spin_lock_irqsave(); 9) | __wake_up_common() { 9) | pollwake() { 9) | default_wake_function() { 9) | try_to_wake_up() { 9) 0.178 us | _raw_spin_lock_irqsave(); 9) | select_task_rq_fair() { 9) 0.033 us | available_idle_cpu(); 9) 0.032 us | available_idle_cpu(); 9) 0.040 us | cpus_share_cache(); 9) 0.058 us | available_idle_cpu(); 9) 1.061 us | } 9) 0.036 us | ttwu_queue_wakelist(); 9) 0.036 us | _raw_spin_lock(); 9) 0.079 us | update_rq_clock(); 9) | ttwu_do_activate() { 9) | enqueue_task_fair() { 9) | enqueue_entity() { 9) 0.040 us | update_curr(); 9) 0.088 us | __update_load_avg_se(); 9) 0.070 us | __update_load_avg_cfs_rq(); 9) 0.032 us | update_cfs_group(); 9) 0.055 us | __enqueue_entity(); 9) 1.347 us | } 9) | enqueue_entity() { 9) 0.038 us | update_curr(); 9) 0.077 us | __update_load_avg_se(); 9) 0.050 us | __update_load_avg_cfs_rq(); 9) | update_cfs_group() { 9) 0.047 us | reweight_entity(); 9) 0.289 us | } 9) 0.035 us | __enqueue_entity(); 9) 1.469 us | } 9) 0.033 us | hrtick_update(); 9) 3.546 us | } 9) | ttwu_do_wakeup() { 9) | check_preempt_curr() { 9) 0.046 us | resched_curr(); 9) 0.279 us | } 9) 0.671 us | } 9) 4.653 us | } 9) 0.038 us | _raw_spin_unlock_irqrestore(); 9) 7.458 us | } 9) 7.652 us | } 9) 7.924 us | } 9) 8.865 us | } 9) 0.045 us | _raw_spin_unlock_irqrestore(); 9) 9.501 us | } 9) 9.703 us | } 9) 0.033 us | kill_fasync(); 9) 0.055 us | __sb_start_write(); 9) | file_update_time() { 9) | current_time() { 9) 0.037 us | ktime_get_coarse_real_ts64(); 9) 0.039 us | timestamp_truncate(); 9) 0.454 us | } 9) | __mnt_want_write_file() { 9) 0.057 us | __mnt_want_write(); 9) 0.289 us | } 9) | generic_update_time() { 9) 0.089 us | __mark_inode_dirty(); 9) 0.321 us | } 9) 0.039 us | __mnt_drop_write_file(); 9) 1.904 us | } 9) 0.037 us | __sb_end_write(); 9) + 14.315 us | } 9) + 14.620 us | } 9) + 14.840 us | } 9) 0.166 us | __fsnotify_parent(); 9) 0.095 us | fsnotify(); 9) + 18.702 us | } 9) | fput() { 9) 0.035 us | fput_many(); 9) 0.238 us | } 9) + 20.668 us | } 9) + 20.907 us | }
這是一個相當極端的例子,但它說明系統呼叫最終可以做的比複製記憶體要多得多。