百科问答小站 logo
百科问答小站 font logo



虚函数效率真的低吗? 第1页

  

user avatar   haozhi-yang-41 网友的相关建议: 
      

要做比较,最起码要有一个公平的比较场景。

说虚函数效率低,那要看跟谁比:如果跟普通函数比,编译器对普通函数可以做预加载,可以做分支预测,可以做内联展开,当然虚函数慢。

但是,虚函数的RTTI是用在这个场景的吗?你都能在编译期知道实际调用哪个函数了,还叫作RTTI吗?其实如果编译器在编译时能够识别出虚函数的实际调用目标,一样可以绕开虚表直接调用目标函数,这时候,甚至包括内联等优化手段也都是可以用的,和普通成员函数完全一样的待遇。


所以,换一个比法:在实现同样功能需求下,虚函数真的比其它实现RTTI的方式要慢吗?

例如说我随手摘两个linux内核的数据结构,看看经典的纯C代码是怎么解决这类问题的:

       struct file {  union {   struct llist_node fu_llist;   struct rcu_head  fu_rcuhead;  } f_u;  struct path  f_path;  struct inode  *f_inode; /* cached value */  const struct file_operations *f_op;   /*   * Protects f_ep_links, f_flags.   * Must not be taken from IRQ context.   */  spinlock_t  f_lock;  enum rw_hint  f_write_hint;  atomic_long_t  f_count;  unsigned int   f_flags;  fmode_t   f_mode;  struct mutex  f_pos_lock;  loff_t   f_pos;  struct fown_struct f_owner;  const struct cred *f_cred;  struct file_ra_state f_ra;   u64   f_version; #ifdef CONFIG_SECURITY  void   *f_security; #endif  /* needed for tty driver, and maybe others */  void   *private_data;  #ifdef CONFIG_EPOLL  /* Used by fs/eventpoll.c to link all the hooks to this file */  struct list_head f_ep_links;  struct list_head f_tfile_llink; #endif /* #ifdef CONFIG_EPOLL */  struct address_space *f_mapping;  errseq_t  f_wb_err;  errseq_t  f_sb_err; /* for syncfs */ } __randomize_layout   __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */     

其中,file_operations的定义是这样的:

       struct file_operations {  struct module *owner;  loff_t (*llseek) (struct file *, loff_t, int);  ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);  ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);  ssize_t (*read_iter) (struct kiocb *, struct iov_iter *);  ssize_t (*write_iter) (struct kiocb *, struct iov_iter *);  int (*iopoll)(struct kiocb *kiocb, bool spin);  int (*iterate) (struct file *, struct dir_context *);  int (*iterate_shared) (struct file *, struct dir_context *);  __poll_t (*poll) (struct file *, struct poll_table_struct *);  long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);  long (*compat_ioctl) (struct file *, unsigned int, unsigned long);  int (*mmap) (struct file *, struct vm_area_struct *);  unsigned long mmap_supported_flags;  int (*open) (struct inode *, struct file *);  int (*flush) (struct file *, fl_owner_t id);  int (*release) (struct inode *, struct file *);  int (*fsync) (struct file *, loff_t, loff_t, int datasync);  int (*fasync) (int, struct file *, int);  int (*lock) (struct file *, int, struct file_lock *);  ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);  unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);  int (*check_flags)(int);  int (*flock) (struct file *, int, struct file_lock *);  ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);  ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);  int (*setlease)(struct file *, long, struct file_lock **, void **);  long (*fallocate)(struct file *file, int mode, loff_t offset,      loff_t len);  void (*show_fdinfo)(struct seq_file *m, struct file *f); #ifndef CONFIG_MMU  unsigned (*mmap_capabilities)(struct file *); #endif  ssize_t (*copy_file_range)(struct file *, loff_t, struct file *,    loff_t, size_t, unsigned int);  loff_t (*remap_file_range)(struct file *file_in, loff_t pos_in,        struct file *file_out, loff_t pos_out,        loff_t len, unsigned int remap_flags);  int (*fadvise)(struct file *, loff_t, loff_t, int); } __randomize_layout;     

也就是说,在“一切皆文件”的linux内核里,它的实现手段本质上和C++的vptr+vtable是完全等价的,照样是查表跳转。所谓的“虚函数慢”的因素,在这里照样一个不拉。


如果有谁不服气,觉得linux在这里的实现方案不够精巧效率不够高,可以自己提一个方案出来试试看?


user avatar   xi-yang-86-73 网友的相关建议: 
      

在你真的受到它的效率困扰之前,纠结它没有任何意义。


user avatar   swxlion 网友的相关建议: 
      
  • 多个一两次的寻址问题不大,现代的CPU主频动不动上GHz,不比当年的386、486、586了。
  • 你要是考虑多一两次寻址的问题,还不如好好优化一下参数传递。参数列表是不是过长?如果是拷贝传递的,那拷贝成本如何?参数列表中有没有临时对象?这些临时对象如果开销太大,是否可以提前准备和异步销毁?
  • 不能内联。不能内联的函数多了去了,又不是教学示范,即使都不内联,也比某些慢吞吞的解释执行的语言快到不知哪里去了。
  • 打断CPU流水线,严重降低分支预测的成功率:1个函数2000行,且没有 if-else?这样我保证不打断。

最后,除非你是写8位或者16位低功耗片上系统,否则还是多考虑一下其他地方的成本开销吧。比如说,锁、事务、业务流程、I/O(HTTP、HTTPS等)、编码协议(JSON、SOAP、XML等)。

虚函数使你设计的时候更加灵活便捷,但如果介意它的性能开销,除非你不用事务、锁,不用HTTP/HTTPS做传输,不用JSON/SOAP/XML做编码或者序列化。否则就是渴得要死的时候,放着遍地的西瓜 不捡,却在找哪里有芝麻,希望芝麻炸出的油能解解渴。毕竟大家都说,芝麻能榨油不是?


user avatar   pansz 网友的相关建议: 
      

去请交警开定责书,如果对方不肯开,那你就说:交警都不认可是我的责任,我凭什么赔钱?

如果开定责书,你就跟交警说对方不同意赔偿条款,让对方打官司吧。如果对方肯打,那么交警把该给的材料给对方,你就直接等着保险公司帮你处理上庭的事务了。这种简易案件判得也很快的,判多少保险赔就是了。




  

相关话题

  如何学习递归呢? 
  现在快2022年了,c++为什么还要实现(.cpp)和声明(.h)分开? 
  就节省编译时间来说,Precompiled Header和Pimpl范式哪个更好? 
  如何才能写出没有bug的程序? 
  p是char类型指针,p[1]不是指向p[0]的下一个字节吗?为什么会到0x11? 
  如果鸿蒙系统成功,会复兴C/C++吗? 
  在有能够转化为C、C++、Java的第四代语言Raptor的情况下,为什么还需要C等程序设计语言? 
  C# 和 Java 哪个更像 C++? 
  C++1y又给C++增加了多少坑? 
  for 循环为什么不支持小数? 

前一个讨论
如何看待吉利和沃尔沃合并?这会带来哪些影响?
下一个讨论
能够同时熟练掌握羽毛球、乒乓球、网球的可能性大嘛?





© 2024-11-21 - tinynew.org. All Rights Reserved.
© 2024-11-21 - tinynew.org. 保留所有权利