1.C++ string 源码实现对比
2.lldb 小记和std::string的数据结构图源码阅读
3.什么时候以struct替代class?
4.图文鲲鹏916-ARM64架构源码gcc编译完整记录
5.å¦ä½ä½¿ç¨oprofile对软件åprofiling
6.Linux环境源码安装GCC/CMAKE
C++ string 源码实现对比
标题:C++ string 源码实现对比 作为游戏客户端开发工程师,作者lucasfan分享了他对不同版本C++ string源码的深入分析,以帮助开发者解决std::string在现网中可能引发的Crash问题。本文将对比libstdc++、腾讯内部的Android和iOS SDK使用的string实现,以及tpstl string,拉萨到阿里源码涉及内存结构、构造函数和析构方法等关键部分。1. libstdc++ string
Android SDK普遍采用的libstdc++ string以写时拷贝(COW)特性为主,但可能导致性能问题。其内存结构包含指向堆上数据的指针和一个包含长度信息的_Rep对象。构造函数如char*构造器负责内存申请和字符串拷贝,拷贝构造通过_M_grab处理共享与深度拷贝,拷贝赋值操作涉及assign方法。2. libc++ string (iOS SDK)
相比之下,iOS使用了短字符串优化(SSO),内存结构分为长字符串和短字符串模式,通过位标志判断。char*构造器和拷贝构造根据字符串类型执行不同初始化方法,右值拷贝利用转移语义节省内存。3. tpstl string (腾讯自研)
tpstl string简化了STL,使用内存池管理内存,其构造和赋值操作均在内存池上进行,有助于解决跨库问题。结论
理解这些string源码实现有助于开发者定位和解决实际问题。源码精灵新精灵作者将继续分享更多案例和调试策略,有兴趣的开发者可加入官方QQ交流群:,获取更多技术分享。lldb 小记和std::string的数据结构图源码阅读
在深入理解LLVM和GCC的std::string实现差异时,我们首先从lldb着手,探索其内部工作机制。昨天尝试编译llvm后,今天我们就来研究std::string在lldb中的表现,它与g++的std::string实现有何不同。
从一个简单的测试程序开始,我们对比了用g++和clang++编译的代码。在g++版本中,字符串s使用了短字符串优化(SSO)的存储方式,"hi"存储在_M_local_buf中。SSO旨在节省内存,通过为短字符串预留固定大小的结构,如长度、容量和指向字符串数组的指针,共字节。
对于长字符串,例如scow,存储方式不同。尽管_M_local_buf中没有"this is a sunny day",但通过M p找到的闲鱼溯源码实际字符串地址显示出长字符串的存储策略。这表明在长字符串时,std::string会采用常规的存储方式,即一个指针指向字符串数组。
接下来,我们需要弄清楚如何判断何时使用SSO。在代码中,我们猜测可能通过某些标志位来区分,但实际观察到的operator[]函数表明,M p始终指向字符串数组,不论字符串长度。这就意味着,无论短长,读取指定位置的字符都遵循相同的逻辑。
在libstdc++(g++)和libc++(llvm)的实现上,我们发现两者在数据结构上存在差异。例如,libstdc++的std::string使用字节的union,而libc++则为字节。区分长短字符串的方式在libc++中通过检查容量字段的比特位实现,这依赖于机器的字节序。
附录中,我分享了自己编译LLVM的体验和使用clang++编译程序的CMakeLists.txt设置,供有兴趣的读者参考。
什么时候以struct替代class?iapp聊天社区源码
考虑使用struct而非class时,重点在于结构体的C++风格及其特性。与C语言中的结构体不同,C++中的结构体支持模板和大量继承(尽管不使用虚函数)。这些特性在C语言中不存在,因此在C++中使用结构体显得更为自然。
例如,ut库,一个小巧的C++单元测试库,其源码仅多行,几乎全部使用了结构体。这种选择体现了C++中结构体的灵活性和简洁性。
以libstdc++为例,即gcc开源项目中的C++标准库,其ranges实现源码同样采用了大量结构体。这些结构体设计简洁,成员变量数量不多,一般只有一两个至三个,且成员变量的私有化并不普遍。因此,在这种情况下,使用类定义反而显得多余。
综上所述,在C++中,当结构体的溯源码场所码成员变量数量不多,且成员变量的私有化不是主要需求时,使用结构体而非类更为合适。结构体的简洁性、模板支持以及与大量继承的结合,使其成为解决特定问题时的优良选择。
图文鲲鹏-ARM架构源码gcc编译完整记录
以下是关于ARM架构源码gcc编译的详细步骤记录: 首先,确保已经准备就绪,如果cmake未安装,需要进行安装。检查cmake版本以确认其是否满足需求。 安装必要的依赖包,如isl、gmp、mpc、mpfr等,检查它们是否已成功安装。 针对gcc版本过低的问题,需下载并更新到7.3版本。下载并解压gcc7.3的安装包。 在gcc-7.3.0目录下,确认已下载和安装了所有依赖包。 利用多核CPU的优势,通过“-j”参数加速编译过程。原先是按照官方文档使用make -j,但速度缓慢,后来调整为make -j以提升效率。 依次执行编译目录创建、gcc编译、安装以及确认“libstdc++.so”软连接在正确的目录(/usr/lib)。 编译完成后,通过查看gcc版本来确认安装是否成功。 以上就是完整的gcc编译安装流程。如果您觉得这些信息对您有所帮助,欢迎分享和关注我们的更新。更多技术内容敬请期待,感谢您的支持!å¦ä½ä½¿ç¨oprofile对软件åprofiling
ããå ³äºXilinx Zynq-带æ¥çæ°çç³»ç»è®¾è®¡æè·¯ï¼ä»¥åProfilingç对象libjpegï¼åæå·²ç»æè¿°è¿äºï¼åæ¤ä¸åèµè¿°ã
ããä¸. Oprofileç®ä»
ããProfilingæ¯å¯¹ä¸åæ§è½ç¹å¾çæ°æ®çå½¢å¼åæ»ç»æåæï¼å®é常以å¾å½¢å表çå½¢å¼åºç°ãå®æä¾ä¸ºç¹å®çå¤çå¨äºä»¶æ¶éçéæ ·ç¾åæ°ææ° éï¼æ¯å¦cache miss rateãTLB miss rateççãä¸è¬æ¥è¯´ï¼ä¸»è¦ç®çæ¯ä¸ºäºæ¾åºè½¯ä»¶ä¸çæ§è½ç¶é¢ï¼ç¶åæé对æ§çä¼å以æå软件çæ´ä½æ§è½ã
ããOprofile æ¯ç¨äº Linux çè¥å¹²ç§è¯æµåæ§è½çæ§å·¥å ·ä¸çä¸ç§ãå®å¯ä»¥å·¥ä½å¨ä¸åçä½ç³»ç»æä¸ï¼å æ¬ARM, PowerPC, MIPS, IA, IA å AMD Athlonççãå®çå¼éå¾å°ï¼ä»Linux 2.6 çèµ·ï¼å®è¢«å å«è¿äºLinuxå æ ¸ä¸ã
ããOprofileå¯ä»¥æ¶éæå ³å¤çå¨äºä»¶çä¿¡æ¯ï¼å¸®å©ç¨æ·è¯å«è¯¸å¦å¾ªç¯çå±å¼ãcacheç使ç¨çä½ãä½æçç±»å转æ¢ååä½æä½ãé误é¢æµè½¬ç§» çé®é¢ãOprofileæ¯ä¸ç§ç»ç²åº¦çå·¥å ·ï¼å¯ä»¥ä¸ºæ令éæè 为å½æ°ãç³»ç»è°ç¨æä¸æå¤çä¾ç¨æ¶ééæ ·ãOprofile éè¿åæ ·æ¥å·¥ä½ã使ç¨æ¶éå°çè¯æµæ°æ®ï¼ç¨æ·å¯ä»¥å¾å®¹æå°æ¾åºæ§è½é®é¢ã
ããéè¿çå¯CPUçhardware eventsï¼oprofileå¯ä»¥å¨è¿è¡ç¶æä¸å¯¹æ´ä¸ªLinuxç³»ç»è¿è¡profilingãProfilingç对象å¯ä»¥æ¯Linux kernel (å æ¬modulesåinterrupt handlers), shared librariesæè åºç¨ç¨åºã
ããä»0.9.8çæ¬å¼å§ï¼oprofileæ¯æPerf_events profiling mode模å¼ãåºç¨ç¨åºoperf被ç¨æ¥æ§å¶profilingè¿ç¨;èå¨legacy modeä¸ï¼æ¯éè¿opcontrolèæ¬åoprofiled daemonæ¥å®æçãOperfä¸å象legacy modeé£æ ·éè¦OProfile kernel driverï¼å®ç´æ¥åLinux Kernel Performance Events Subsystemæ交éã使ç¨operfï¼å°±å¯ä»¥ç¨æ®éç¨æ·ç身份æ¥profilingç¨æ·çåºç¨ç¨åºäºï¼å½ç¶å¦æéè¦å¯¹æ´ä¸ªç³»ç»æ¥profiling çæ¶åè¿æ¯éè¦rootæéçã
ããå¦æ硬件ä¸æ¯æOProfile使ç¨performance countersï¼OProfileå°±åªè½å·¥ä½å¨Timer Modeä¸äºãTimer Modeåªè½å¨legacy profiling modeä¸ä½¿ç¨ï¼å³åªè½éè¿opcontrolèæ¬æ¥æ§å¶ã
ããOprofileçwebsite为ï¼piled (JIT) code
ãã? å¯ä»¥å¯¹æ´ä¸ªç³»ç»åprofiling
ãã? å¯ä»¥è§å¯CPUå é¨çç»èï¼ä¾å¦cache miss rate
ãã? å¯ä»¥å¤æºä»£ç åannotation
ãã? å¯ä»¥æ¯æinstruction-levelçprofiling
ãã? å¯ä»¥çæcall-graph profiles
ããä¸è¿OProfileä¹ä¸æ¯ä¸è½çï¼å®ä¹æèªå·±çå±éæ§ï¼
ãã? åªè½å¨x, ARM, åPowerPCæ¶æä¸çæcall graph profiles
ãã? ä¸æ¯æ%精确çinstruction-level profiling
ãã? 对dynamically compiled (JIT) code profilingçæ¯æè¿ä¸å®åã
ããæ 论å¦ä½ï¼Oprofileçåè½é½æ¯gprofè¦å¼ºå¾å¤ï¼ä»£ä»·æ¯é 置起æ¥ä¼æ¯è¾éº»ç¦ã
ããäº. ç¼è¯Oprofile
ããé¦å æ好å¨Linux kerneléé¢éä¸Oprofile driverï¼ä»¥è·å¾å ¨é¢çæ¯æã
ããä¸è½½Linux kernel Sourceï¼ä»/Xilinx/linux-xlnx å¯ä»¥ä¸è½½å°Xilinxæä¾çéªè¯å¥½çå æ ¸ãå¦æä¸æ¹ä¾¿ä½¿ç¨Linuxä¸çgitå·¥å ·ï¼å¯ä»¥åå»é¡µé¢ä¸çreleasesæ¾å°ç¸åºççæ¬ä¸è½½tar ballãä¸è½½çæ¶åæ好étar.gzæ ¼å¼çï¼èä¸æ¯zipæ ¼å¼çï¼å 为åè å¨å¤çsymbol linkçæ¶åæå¯è½ä¼åºé®é¢ã
ããå 为ç¬è 使ç¨çæ¯Xilinx Linux pre-built .7ï¼æ以è¿éä¸è½½çæ¯linux-xlnx-xilinx-v.7.tar.gz
ãã解å缩åï¼ç¨ä»¥ä¸å½ä»¤è°åºLinux kernelçé ç½®çé¢ï¼
ããexport ARCH=arm
ããexport CROSS_COMPILE=arm-xilinx-linux-gnueabi-
ããmake xilinx_zynq_defconfig
ããmake xconfig æè make menuconfig
ããå¨é ç½®çé¢ä¸å°ä»¥ä¸ä¸¤é¡¹å¾ä¸ï¼
ããGeneral setup --->
ãã[*] Profiling support
ãã<*> OProfile system profiling
ããç¶åmake uImageå³å¯çææ°çuImageï¼ç¨æ¥æ¿æ¢Xilinx Linux pre-built .7ä¸çLinux kernel imageãåæ¶æ们ä¹éè¦vmlinuxæ¥æ£æ¥profilingçç»æã
ããOprofileéè¦popt, bfd, libertyåºï¼è¦å¨åµå ¥å¼åæ¿ä¸ä½¿ç¨è¿äºåºï¼éè¦æå·¥å®æ交åç¼è¯ã
ããé对popt 1.7ï¼ç¨ä»¥ä¸å½ä»¤å®æç¼è¯ï¼
ãã./configure --prefix=/home/wave/xilinx/oprofileprj/rootfs --host=arm-xilinx-linux-gnueabi --with-kernel-support --disable-nls && make && make install
ããé对binutils 2.ï¼ç¨ä»¥ä¸å½ä»¤å®æç¼è¯ï¼
ãã./configure --host=arm-xilinx-linux-gnueabi --prefix=/home/wave/xilinx/oprofileprj/rootfs --enable-install-libbfd --enable-install-libiberty --enable-shared && make && make install
ããä¸è¿--enable-install-libiberty没æææï¼æ以éè¦æå·¥ælibiberty.aålibiberty.hæ·è´å°ç¸åºçä½ç½®ã
ããé对oprofile 0.9.9ï¼ç¨ä»¥ä¸å½ä»¤å®æç¼è¯ï¼
ãã./configure --host=arm-xilinx-linux-gnueabi --prefix=/home/wave/xilinx/oprofileprj/rootfs --with-kernel-support --with-binutils=/home/wave/xilinx/oprofileprj/rootfs && make && make install
ããé ç½®è¿ç¨ç»æåå¯è½ä¼æ以ä¸æ示ï¼å 为没ææç®ç¨GUIåprofile JITed codeï¼æ以ç´æ¥å¿½è§ä¹ã
ããconfig.status: executing libtool commands
ããWarning: QT version 3 was requested but not found. No GUI will be built.
ããWarning: The user account 'oprofile:oprofile' does not exist on the system.
ããTo profile JITed code, this special user account must exist.
ããPlease ask your system administrator to add the following user and group:
ããuser name : 'oprofile'
ããgroup name: 'oprofile'
ããThe 'oprofile' group must be the default group for the 'oprofile' user.
ããå°ç¼è¯å®æçuImageï¼vmlinuxï¼oprofile binaryï¼éæ°ç¼è¯ç没æ-pgçlibjpeg binary以åtool chainçlibcæå æ¾å°SDå¡ä¸ï¼åå¤å¨ZCå¼åæ¿ä¸å°è¯profile djpegã
ããä¸. è¿è¡Oprofile
ããæ£å¸¸å¯å¨åµå ¥å¼Linuxåï¼å¨å¼åæ¿çconsoleä¸ä¸æ¬¡è¾å ¥ä»¥ä¸å½ä»¤ï¼
ããmount /dev/mmcblk0p1 /mnt
ããmkdir -p /home/root/work
ããcd /home/root/work
ããtar zxvf /mnt/jpeg-bin-nopg.tar.gz
ããcd jpeg-bin/bin
ããcp /mnt/park-x.jpg .
ããexport LD_LIBRARY_PATH=/home/root/work/jpeg-bin/lib
ããcd /home/root/work
ããtar zxvf /mnt/rootfs.tar.gz
ããcd rootfs
ããchown root:root -R
*ããcp -R bin/* /usr/bin
ããcp -R lib/* /lib
ããcp /bin/which /usr/bin
ããcp /bin/dirname /usr/bin
ããmkdir -p /home/wave/xilinx/oprofileprj/rootfs/share
ããcp -R ./rootfs/* /home/wave/xilinx/oprofileprj/rootfs
ããcd /home/root/work
ããtar zxvf /mnt/libc.tar.gz
ããcp ./lib/libstdc*.* /lib
ããmkdir -p /home/wave/xilinx/libjpeg
ããcd /home/wave/xilinx/libjpeg
ããtar zxvf /mnt/jpeg-9.tar.gz
ããcp /mnt/vmlinux /home/root/work
ããcd /home/root/work/jpeg-bin/bin
ããopcontrol --init
ããopcontrol --vmlinux=/home/root/work/vmlinux
ããopcontrol --setup --event=CPU_CYCLES:::0:1 --session-dir=/home/root/
ããoperf --vmlinux /home/root/work/vmlinux ./djpeg -bmp park-x.jpg > result.bmp
ããopreport -l ./djpeg
ããå®æè¿ä¸æ¥åï¼æ们就å¯ä»¥çå°profilingçç»æäºï¼å¨ç¬è çå¹³å°ä¸çå°çå 容ç主è¦é¨åå¦ä¸ï¼
ããroot@zynq:~/work/jpeg-bin/bin# opreport -l ./djpeg
ããUsing /home/root/work/jpeg-bin/bin/oprofile_data/samples/ for samples directory.
ããCPU: ARM Cortex-A9, speed MHz (estimated)
ããCounted CPU_CYCLES events (CPU cycle) with a unit mask of 0x (No unit mask) count
ããsamples % image name symbol name
ãã . libc-2..so /lib/libc-2..so
ãã 7. libjpeg.so.9.0.0 ycc_rgb_convert
ãã 7. libjpeg.so.9.0.0 jpeg_idct_x
ãã 7. libjpeg.so.9.0.0 decode_mcu
ãã 6. libjpeg.so.9.0.0 jpeg_idct_islow
ãã 6. djpeg finish_output_bmp
ãã 2. libjpeg.so.9.0.0 jpeg_fill_bit_buffer
ãã 1. djpeg put_pixel_rows
ãã 0. vmlinux __copy_from_user
ãã 0. libjpeg.so.9.0.0 decompress_onepass
ãã 0. libjpeg.so.9.0.0 jpeg_huff_decode
ãã 0. vmlinux get_page_from_freelist
ãã 0. vmlinux __memzero
ãã 0. vmlinux __copy_to_user_std
ãã 0. vmlinux _raw_spin_unlock_irqrestore
ãã 0. vmlinux do_page_fault
ãã 0. vmlinux __generic_file_aio_write
ãã 0. vmlinux _raw_spin_unlock_irq
ãã 0. vmlinux free_hot_cold_page
ãã 0. vmlinux vector_swi
ãã 0. vmlinux handle_pte_fault
ããä»ç»æä¸æ们å¯ä»¥çå°libjpeg.so.9.0.0, djpegåvmlinuxä¸çsymbol nameå·²ç»å¯ä»¥è¢«æ£ç¡®ç解æåºæ¥äºï¼ågprofçç»æåºæ¬ä¸è´ãç¸æ¯gprofï¼oprofileå¯ä»¥å¨æ´å¤§çèå´å å®æprofilingã
ããæ们è¿å¯ä»¥ç¨ä»¥ä¸å½ä»¤è§å¯æºä»£ç ä¸ç¹å®è¡çæ§è¡æ¶é´ï¼è¿ä¸æ¥ç¼©å°ä¼åçèå´ï¼è¾¾å°äºåååçææã
ããopannotate --source ./djpeg > opannotate.txt
ããå. å°ç»
ããéè¿å®éªï¼æ们å¯ä»¥çå°Oprofileå¯ä»¥æä¾æ´ä¸°å¯çprofilingç»æï¼å¯ä»¥æ´å¥½ç帮å©å¼åè æ¾å°ç¶é¢ï¼éè¿æé对æ§çä¼åæå软件 æ§è½;profilingçç»æä¹å¯ä»¥å¸®å©å¼åè å°æ§è½ç¶é¢ä»£ç éè¿Xilinx HLSå·¥å ·ç¨ç¡¬ä»¶å éå¨æ¥å®ç°ï¼ä»è为è¿ä¸æ¥æåæ´ä¸ªåµå ¥å¼ç³»ç»çæ§è½æå¼äºå¤§é¨ã
Linux环境源码安装GCC/CMAKE
为了在Linux环境下源码安装GCC和CMAKE,我们需要遵循详细的步骤和策略。对于GCC源码,我们可以从GitHub-gcc-mirror/gcc获取4.4.6版本。接下来,进入下载后的GCC源代码目录。
在配置和编译GCC时,首先应该明确指定安装的目录,避免冲突。可能在配置脚本时遇到错误,这时候需要解决依赖项问题。分别安装MPFR、MPC和任何其他必要的依赖库。对于GCC8.3及以上版本,内部集成脚本能够简便地获取这些依赖库。
安装库路径后,再次执行配置文件,加入库路径参数,确保安装的每个步骤顺利进行。配置完成后,整个GCC安装过程即宣告成功。
为了测试GCC是否正确安装,遵循指导进行验证。
CMake的安装同样关键,可以通过直接指定需要的GCC版本来简化安装流程。在CMake命令行参数中指定GCC路径也是可行的。
在运行GCC4.4.6编译的程序时,可能存在系统路径问题,这是因为我们选择的是不替换安装方式。因此,需要额外操作,确保所需的库被正确添加到路径中。
遇到GCC多版本引起的ABI兼容问题时,如果编译链接过程中遇到“undefined reference to"“std::__cxx ***””错误,这提示可能是C++ ABI问题。处理方法是,针对GCC5.1之前版本发布的libstdc++中新增的ABI,通过添加定义-D_GLIBCXX_USE_CXX_ABI=0来解决该问题。
对于GDB版本的问题,特别在GCC.1的使用中,要求C++的编译器,导致了旧版本GDB启动出现Segment Fault。解决办法是升级GDB版本。
附录中提供了一些额外资源,例如Mingw下载,适用于位和位Windows的最新版x_-win-sjlj;CMake下载链接以及GCC的GitHub地址等。遵循这些资源和提示,能够帮助用户顺畅进行Linux环境下的GCC和CMAKE的源码安装与配置。
c++protected继承和private继承是不是没用的废物?
既然你所统计的项目里出现了 private 继承和 protected 继承,这不正说明确实有他们的用武之地吗?
让我们来康康 C++ 代码的标杆——STL 的源码,是怎么做的:
先来康 GCC 自带的 libstdc++ 的实现:
vector, list, deque, forward_list, unordered_(multi)set/map 的底层都有用到 protected 或 private 继承。
比如 vector 会空基类压缩优化技术,这只能用继承实现,而使用组合时就没有压缩的效果。
考察下面代码,这是对 vector 压缩 allocator 字段原理的简化实现:
如果 vector 直接 public 继承自 allocator,根据类型兼容原则,在指针和引用语义下,子类同时也可被视作是父类。那 vector 也能被当做 allocator 用了?这会引发语义混乱。
而改成 private 或 protected 继承就不会了:
这时候编译器会报错,这阻止了上面的情况发生。这样的例子比比皆是。
还有 tuple 对空类字段的压缩,也采用了这个手法。
2)既然谈到了 tuple,我们就来考察一下 tuple。
这次我不亲手写代码了,就百度一下,随手找找一篇博客现场打脸好啦。
百度搜“std::tuple 实现”,第一篇博客用常规思路来实现 tuple,即:取到第一个模板参数后,作为一个数据成员,然后递归继承 tuple。这份实现没有用到空类成员压缩优化,不过没关系,反正这个优化也不是强制的。
但是,如果使用 public 继承,类型兼容原则会导致 tuple 是 tuple 的子类,那么就可以当父类去用。这将引发大坑,比如接收二元组参数的函数接收到的居然是一个三元组。这种低质量的库在业务代码里是不可用的。
总结一下,protected private 继承能暴露问题,避免不当使用时的隐患;空基类优化的需求使得必须用继承实现,而 public 继承会产生奇怪的语义,这决定了 protected private 继承在模板库中很有用。
业务代码在使用继承时,往往只是为了利用多态性,而模板库在设计时会考虑到所有场景,所以 protected private 继承在模板库中用得多,在业务代码中用得少。
最后,private protected 继承虽然在实践中用得相对较少,但他们绝不是像 vector, auto_ptr 这样的实在是非常拉垮的设计。他们在模板编程中十分有用!