Linux

Linux 頁面記憶體性能與 memcpy

  • January 17, 2022

在 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另一方面,具有系統呼叫的所有成本。特別是在您的測試中,由於寫入大小很小,因此復製本身的成本可能與系統呼叫的成本相形見絀。即使進行較大的測試,系統呼叫成本可能仍然是主要因素。

要減少系統呼叫的重量,請嘗試與更大的大小進行比較,必要時更改臟比率配置,或寫入 atmpfs以避免寫入磁碟的成本。您可能還想禁用 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   |  }

這是一個相當極端的例子,但它說明系統呼叫最終可以做的比複製記憶體要多得多。

引用自:https://unix.stackexchange.com/questions/686541