热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:LLVMLinkTimeOptimization

篇首语:本文由编程笔记#小编为大家整理,主要介绍了LLVMLinkTimeOptimization相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了LLVM Link Time Optimization相关的知识,希望对你有一定的参考价值。








作者: 靛青,坚持在移动端的 ios 开发,还爱着 Swift 和 RxSwift,目前更多关注在 LLVM 方面的内容。



LLVM[1] 的链接时优化(Link Time Optimization,简称 LTO)已经在 WWDC 2016[2] 中提及到。因为这个选项在 Xcode 中默认关闭的,我也一直没有开启过这个选项,所以之前没有做过什么了解。趁着这次五一放个假,我们可以看看 LTO 是什么,以及它的整个流程是什么样子。


我们知道一个程序从源码到运行,需要有一个静态链接的过程。


在这个过程中,在解决所有的符号引用关系期间,我们可以知道整个程序的全貌。为此我们能以全局的角度做一些优化,这就是链接时优化。


我在这里将 LTO 理解为:借助静态链接可以获取程序全局信息的机会,做一些全局优化,这样可以提高运行时的性能,并进一步减少二进制的大小。



阅读本文前,建议先看完 LLVM Link Time Optimization: Design and Implementation[3]



从一个 Xcode 项目了解 LTO



工程地址:GitHub - DianQK/lto-example[4]



为了了解整个的优化过程,我们从创建一个 Xcode 项目开始探索。



为了尽可能减少无关文件影响,这里创建了一个简单的 macOS 命令行工具 foo。



这是从 LLVM Link Time Optimization: Design and Implementation[5] 复制的例子,这个例子很好地展示的 LTO 优化过程。不同的是我们创建了一个静态库。


同时还有以下改动:


  • foo3 移除了 static 避免内联优化

  • 关闭 DEAD_CODE_STRIPPING 避免被其他优化影响我们关注 LTO 关键流程

  • 使用 -Os 编译参数观察结果


代码:


--- bar.h ---
extern int foo1(void);
extern void foo2(void);
extern void foo4(void);
--- bar.c ---
#include "a.h"
static signed int i = 0;
void foo2(void) {
  i = -1;
}
int foo3() {
  foo4();
  return 10;
}
int foo1(void) {
  int data = 0;
  if (i < 0)
    data &#61; foo3();
  data &#61; data &#43; 42;
  return data;
}
--- main.c ---
#include 
#include "a.h"
void foo4(void) {
  printf("Hi\\n");
}
int main() {
  return foo1();
}

工程结构&#xff1a;



Commad &#43; B 一把梭&#xff0c;得到如下链接命令&#xff1a;



从这个命令的执行中&#xff0c;我们可以看到 -object_path_ltofoo_lto.o &#xff0c;或许我们早就开启了 LTO&#xff1f;但如果你尝试查看这个文件&#xff0c;会发现这个文件不存在。


从 clang 文档[6] 中可以了解到&#xff0c;-Xlinker 会把后面的参数会传递给链接器。而链接时调用 clang 的命令&#xff0c;仅仅是对链接器参数进行一个封装和传递。



Tip&#xff1a;使用 -Wl 也可以传递参数&#xff0c;-Xlinker -object_path_lto -Xlinker /path/foo_lto.o 等于 -Wl,-object_path_lto,/path/foo_lto.o&#xff0c;使用 -Wl 会更简洁。



将该命令完整复制并添加 -v 参数&#xff0c;可以得到翻译后的真实链接器命令&#xff1a;


Apple clang version 12.0.5 (clang-1205.0.22.9)
Target: arm64-apple-macos11.3
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
 "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -dynamic -arch arm64 -platform_version macos 11.3.0 11.3 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -o /Users/yahaha/Desktop/foo/build/foo/Build/Products/Release/foo -L/Users/yahaha/Desktop/foo/build/foo/Build/Products/Release -filelist /Users/yahaha/Desktop/foo/build/foo/Build/Intermediates.noindex/foo.build/Release/foo.build/Objects-normal/arm64/foo.LinkFileList -object_path_lto /Users/yahaha/Desktop/foo/build/foo/Build/Intermediates.noindex/foo.build/Release/foo.build/Objects-normal/arm64/foo_lto.o -lbar -no_adhoc_codesign -dependency_info /Users/yahaha/Desktop/foo/build/foo/Build/Intermediates.noindex/foo.build/Release/foo.build/Objects-normal/arm64/foo_dependency_info.dat -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.5/lib/darwin/libclang_rt.osx.a -F/Users/yahaha/Desktop/foo/build/foo/Build/Products/Release

我们可以从该命令中&#xff0c;搜索到两个有 lto 关键字的参数&#xff1a;-lto_library /path/usr/lib/libLTO.dylib-object_path_lto /path/arm64/foo_lto.o


使用 man ld 可以得到这两个参数的用途&#xff1a;


-object_path_lto filename
When performing Link Time Optimization (LTO) and a temporary mach-o object file is needed, if this option is used, the temporary file will be stored at the specified path and remain after the link is complete. Without the option, the linker picks a path and deletes the object file before the linker tool completes, thus tools such as the debugger or dsymutil will not be able to access the DWARF debug info in the temporary object file.
-lto_library path
When performing Link Time Optimization (LTO), the linker normally loads libLTO.dylib relative to the linker binary (../lib/libLTO.dylib). This option allows the user to specify the path to a specific libLTO.dylib to load instead.

很明显&#xff0c;这就是本文提到的 LTO。


为了可以链接时以全局的范围进行优化&#xff0c;使用 -object_path_lto  指定一个临时的目标文件&#xff0c;LTO 会将所有的目标文件合成一个大的 lto.o  目标文件。借助这个大目标文件进行全局优化。从参数说明中可以看到当指定这个文件路径时&#xff0c;链接完成后&#xff0c;这个文件会保留下来的。


-lto_library 用于指定具体使用的 libLTO 动态库&#xff0c;链接器将加载该动态库&#xff0c;借助动态库中提供的函数完成目标文件的合并工作。


现在我们在项目中打开 LTO&#xff1a;




本文只关注 Monolithic 参数下的 LTO&#xff0c;不讨论 Incremental LTO



得到的链接命令如下&#xff1a;


Apple clang version 12.0.5 (clang-1205.0.22.9)
Target: arm64-apple-macos11.3
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
 "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -dynamic -arch arm64 -platform_version macos 11.3.0 11.3 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -o /Users/yahaha/Desktop/foo/build/foo/Build/Products/Release/foo -L/Users/yahaha/Desktop/foo/build/foo/Build/Products/Release -filelist /Users/yahaha/Desktop/foo/build/foo/Build/Intermediates.noindex/foo.build/Release/foo.build/Objects-normal/arm64/foo.LinkFileList -object_path_lto /Users/yahaha/Desktop/foo/build/foo/Build/Intermediates.noindex/foo.build/Release/foo.build/Objects-normal/arm64/foo_lto.o -lbar -no_adhoc_codesign -dependency_info /Users/yahaha/Desktop/foo/build/foo/Build/Intermediates.noindex/foo.build/Release/foo.build/Objects-normal/arm64/foo_dependency_info.dat -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.5/lib/darwin/libclang_rt.osx.a -F/Users/yahaha/Desktop/foo/build/foo/Build/Products/Release

这链接参数和关闭 LTO 的参数完全一样&#xff0c;不过这次我们得到了 lto.o 文件&#xff1a;


$ file /path/arm64/foo_lto.o
/path/arm64/foo_lto.o: Mach-O 64-bit object arm64

如果查看这个目标文件&#xff0c;可以看到这个目标文件包含了所有的符号信息&#xff0c;说明 LTO 确实将所有目标文件合并到该临时文件。


符合链接器的参数描述情况&#xff0c;这说明打开工程中 LLVM_LTO 应当生效了&#xff0c;我们成功打开了 LTO。而链接器的参数完全没有变化&#xff0c;说明 LTO 的工作还需要编译的支持。


当开启 LLVM_LTO 的 Target 编译时&#xff0c;clang 参数将多出一个 -flto。此时我们查看编译的目标文件&#xff0c;可以得到如下内容&#xff1a;


$ file /path/arm64/bar.o
/path/arm64/bar.o: LLVM bitcode, wrapper

当关闭 LLVM_LTO&#xff0c;即去掉 -flto 时&#xff1a;


$ file /path/arm64/bar.o
/path/arm64/bar.o: Mach-O 64-bit object arm64

这是我们平时遇到的 Mach-O 目标文件。而 LLVM bitcode 是 LLVM 的中间文件&#xff08;IR&#xff09;&#xff0c;这个中间文件可以使用 llvm 的一系列工具进行优化&#xff0c;最常见的应当是 opt - LLVM optimizer[7]



此外如果我们关闭 LLVM_LTO&#xff0c;并在 OTHER_CFLAGS 中添加 -emit-llvm&#xff0c;也能得到 IR 文件。



让我们继续调整配置&#xff0c;可以得到以下结果&#xff1a;


  1. 静态库 bar 打开 LTO&#xff0c;主工程 foo 也打开 LTO&#xff0c;产物大小为 35680 Bytes&#xff0c;定义符号有 _main

  2. bar 打开 LTO&#xff0c;foo 关闭 LTO&#xff0c;产物大小为 69216 Bytes&#xff0c;定义符号有 _foo1_foo4_main

  3. bar 关闭 LTO&#xff0c;foo 打开 LTO&#xff0c;产物大小为 69424 Bytes&#xff0c;定义符号有 _foo1_foo2_foo3_foo4_main

  4. 全部关闭 LTO&#xff0c;产物大小为 69424 Bytes&#xff0c;定义符号有 _foo1_foo2_foo3_foo4_main


这说明如果想完美展现 LTO 效果&#xff0c;所有静态库必须编译为 LLVM bitcode&#xff08;添加 -flto 参数&#xff09;。如果在一个大型项目中&#xff0c;集成的组件都以 Mach-O 的二进制格式集成&#xff0c;那最终 LTO 的效果会变得不明显。一个比较简单的判断优化效果的方式是链接时间越长&#xff0c;可优化内容越多&#xff0c;效果越好。如果你开启 LTO 和没开启 LTO&#xff0c;链接耗时差不多&#xff0c;那说明没有完全开启 LTO。


以上 四种 LTO 开启范围的结果如下&#xff1a;


第一种&#xff0c;全部打开 LTO&#xff0c;bar.o 和 foo.a 均为 LLVM Bitcode&#xff0c;都可以再进行优化。和 LLVM Link Time Optimization: Design and Implementation[8] 文档中一样。


  1. 由于链接产物就是最终产物&#xff0c;我们可以判断出没有使用 foo2&#xff0c;于是可以移除 foo2 这个符号

  2. i <0 永远是 false&#xff0c;实际运行不会用到 foo3&#xff0c;于是可以移除 foo3 这个符号

  3. 移除 foo3 后&#xff0c;foo4 也用不到了&#xff0c;也可以移除

  4. Os 优化下&#xff0c;foo1 仅有一处调用&#xff0c;我们可以合并到 main 中&#xff0c;此时仅剩一个 main


第二种&#xff0c;仅打开 bar LTO&#xff1a;


  1. 由于 main.o 不是 IR&#xff0c;所以我们只能保留 mainfoo4

  2. 由于 main.o 中调用了 foo1&#xff0c;foo1 也得保留下来

  3. 幸运地是&#xff0c;foo3foo2bar.o 这个 Bitcode 中&#xff0c;我们也可以判断到这两个函数运行时不可能调用&#xff0c;将它们移除

  4. 最终保留 foo1foo4main


第三种&#xff0c;仅打开 foo LTO&#xff1a;由于 bar.o 不能优化&#xff0c;foo4 也被 bar.o 使用&#xff0c;所以全部符号都得保留下来。


第四种&#xff0c;由于没有 IR 文件&#xff0c;所以不会进行优化。


从这四种情况中&#xff0c;进一步说明了 LTO 在链接时&#xff0c;如果查找到的对象是个 LLVM Bitcode 文件时&#xff0c;则将 将该文件合并到 lto.o 中进行优化。当然如果都是 Mach-O 文件&#xff0c;链接会跳过 LTO 过程。


从链接过程中了解 LTO


在 LLVM Link Time Optimization: Design and Implementation[9]中已经有了细致的解释&#xff0c;我们结合 ld64 和 llvm 中 libLTO 部分进行一番理解。


ld64[10] 是 Xcode 使用的静态链接器&#xff0c;也就是 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld 源码。


这个开源工程不能直接跑起来&#xff0c;我创建了个 patch 解决了编译问题&#xff0c;可以使用 GitHub - DianQK/ld64-build[11] 进行编译调试。


使用 ld 提供的 -print_statistics 参数&#xff0c;可以得到链接过程每个步骤的耗时&#xff1a;



clang 上使用 -Wl,-print_statistics



           ld total time:   36.5 milliseconds ( 100.0%)
     option parsing time:    0.7 milliseconds (   2.0%)
  object file processing:    0.0 milliseconds (   0.1%)
         resolve symbols:   33.8 milliseconds (  92.4%)
         build atom list:    0.0 milliseconds (   0.0%)
                 passess:    1.2 milliseconds (   3.2%)
            write output:    0.7 milliseconds (   2.0%)
pageins&#61;83, pageouts&#61;0, faults&#61;1781
processed   1 object files,  totaling           4,240 bytes
processed   2 archive files, totaling         143,016 bytes
processed  38 dylib files
wrote output file            totaling          16,816 bytes

以上为打开 LTO 的数据&#xff0c;关闭 LTO 得到&#xff1a;


           ld total time:   19.2 milliseconds ( 100.0%)
     option parsing time:    0.2 milliseconds (   1.2%)
  object file processing:    0.0 milliseconds (   0.1%)
         resolve symbols:   17.8 milliseconds (  92.3%)
         build atom list:    0.0 milliseconds (   0.0%)
                 passess:    0.8 milliseconds (   4.6%)
            write output:    0.3 milliseconds (   1.6%)
pageins&#61;1, pageouts&#61;0, faults&#61;677
processed   1 object files,  totaling           2,708 bytes
processed   2 archive files, totaling         140,960 bytes
processed  38 dylib files
wrote output file            totaling          50,312 bytes

通过可以对比得到 resolve symbols 环节是静态链接最耗时的地方&#xff0c;并且在打开 LTO 后&#xff0c;这个环节时间增加了近一倍。


用伪代码标识 ld main 函数如下&#xff1a;


int main(int argc, const char* argv[]) {
 // option parsing 解析输入参数
 Options options(argc, argv);
  // object file processing 获取所有的输入文件&#xff0c;包括 .o .a 等
 InputFiles inputFiles(options);
  // resolve symbols 解决符号引用关系
 Resolver resolver(options, inputFiles);
 resolver.resolve();
  // passess 执行一些生成地址的 pass&#xff0c;比如 GOT
 Passes.doPass();
 // write output 写入产物信息
 OutputFile out(options, state);
 out.write(state);
}

而在 Resolver::resolve() 关键过程如下&#xff1a;


void Resolver::resolve() {
  // 构建 Atom 列表&#xff0c;Atom 是 ld64 中链接最小单元&#xff0c;比如函数、全局变量
  this->buildAtomList();
  // 解决符号引用关系
 this->resolveUndefines();
  // 执行 LTO
  this->linkTimeOptimize();
}

从这里我们知道&#xff0c;LTO 是在解决完一次符号引用关系查找后进行的。


在 LTO 文档中&#xff0c;我们知道 LTO 的处理有 4 个阶段&#xff1a;


  1. Read LLVM Bitcode Files

  2. Symbol Resolution

  3. Optimize Bitcode Files

  4. Symbol Resolution after optimization


这里我们重点关注第 1 阶段和第 3 阶段。


第 1 阶段&#xff0c;获取 LLVM Bitcode&#xff0c;和获取其他文件一样&#xff0c;都在静态链接的 object file processing 中。


InputFiles 类提供了 makeFile 工厂方法&#xff0c;可以通过读取文件头部信息判断文件类型&#xff0c;生成对应的 File 实例。当发现输入文件为 LLVM Bitcode 时&#xff0c;调用 lto_module_create() 等 libLTO 提供的函数完成对 LLVM bitcode 的解析及符号信息获取。


判断是不是 LLVM Bitcode 的方式很简单&#xff0c;确定文件头部信息为 0xdec017ob 即可&#xff1a;


$ file /path/bar.o
/path/bar.o: LLVM bitcode, wrapper
$ hexdump -n 4 /path/bar.o
0000000 de c0 17 0b

第 3 阶段&#xff0c;优化合并的 Bitcode&#xff0c;这部分在 Resolver::linkTimeOptimize() 中&#xff0c;这里有一个比较长的调用链&#xff0c;顺着调用链 Parser::optimize() -> Parser::optimizeLTO() -> Parser::codegen() 找到 Parser::codegen()


与文档描述略有不同&#xff0c;在 Parser::codegen() 中&#xff0c;ld 使用新版的 libLTO 时&#xff0c;将 lto_codegen_compile() 分为两个函数 lto_codegen_optimize()lto_codegen_compile_optimized() 依次调用&#xff0c;这两个函数分别表示对 Bitcode 进行优化、汇编生成机器码。


由于 lto_* 属于 libLTO 部分&#xff0c;想了解更多细节可以在 llvm 工程的 llvm/tools/ltollvm/lib/LTO 中找到。lto_codegen_optimize() 最终会调用 LTOCodeGenerator::optimize()&#xff0c;这是 Bitcode 优化关键逻辑。


这个优化方法将调用内部的一个 lto::opt&#xff0c;这个 opt 和 opt - LLVM optimizer[12] 几乎一样&#xff0c;执行 LLVM 的各种 Pass 优化。


所以我们甚至在链接期间传递 opt 相关参数&#xff0c;这个参数将被应用到 LTO 优化阶段。比如使用 -Wl,-mllvm,-time-passes 传递一个优化耗时记录&#xff1a;



到这里我们可以了解到&#xff0c;LTO 的核心功能在 libLTO 动态库中&#xff0c;它主要提供了 LLVM Bitcode 解析和优化能力。ld 通过在不同时机调用 libLTO 提供的 API 完成全部的优化功能。


从以上内容中&#xff0c;我们可以得到值得关注的几点&#xff1a;


  1. ld 的静态链接中&#xff0c;是否执行 LTO 由输入文件中是否有 LLVM Bitcode 判断

  2. 开启 LTO 时&#xff0c;编译的 .o 文件由 Mach-O 变为 LLVM Bitcode 中间文件

  3. ld 使用 libLTO 将所有的 Bitcode 文件合并为一个模块进行优化&#xff0c;

  4. 静态链接中添加 -mllvm,-opt-argument 可以传递参数给 LTO 的优化过程


额外的一些问题


全二进制集成下&#xff0c;LTO 能不能有些效果


会有&#xff0c;这部分主要在 Section __objc_const 上。当开启 -dead_strip 时&#xff0c;Resolver::linkTimeOptimize()  会有一次额外的 dead code 优化。


示例的 iOS 工程中&#xff0c;两个二进制大小如下&#xff1a;


$ llvm-size --format&#61;darwin link_only_main_lto
Segment __TEXT: 32768
 total 7459
Segment __DATA_CONST: 16384
 total 104
Segment __DATA: 16384
  Section __objc_const: 3840
 total 4724
Segment __LINKEDIT: 32768
$ llvm-size --format&#61;darwin link_nolto
Segment __TEXT: 32768
  total 7459
Segment __DATA_CONST: 16384
  total 104
Segment __DATA: 16384
  Section __objc_const: 4568
 total 5452
Segment __LINKEDIT: 32768

可以清晰看到仅在主工程&#xff08;只有 main 函数情况下&#xff09;&#xff0c;开启 LTO&#xff0c;__objc_const 减少了 728。这个效果在更大的工程中将表现的更明显。


从 LinkMap 中可以看到移除的符号都属于 AppDelegate.o


# Path: /path/link.app/link
# Arch: arm64
# Object files:
[  0] linker synthesized
[  1] /path/arm64/main.o
[  2] /path/Release-iphoneos/libcode.a(ViewController.o)
[  3] /path/libcode.a(AppDelegate.o)
[  4] /path/libcode.a(SceneDelegate.o)
...
# Dead Stripped Symbols:
#         Size     File  Name
<>  0x000001D0 [  3] __OBJC_$_PROTOCOL_INSTANCE_METHODS_NSObject
<>  0x00000020 [  3] __OBJC_$_PROTOCOL_INSTANCE_METHODS_OPT_NSObject
<>  0x00000048 [  3] __OBJC_$_PROP_LIST_NSObject
<>  0x000000A0 [  3] __OBJC_$_PROTOCOL_METHOD_TYPES_NSObject

如果你打算了解更多细节&#xff0c;搜索 ld64 中 this->deadStripOptimize(true) 进行调试即可。



如果你在 main 函数中引用更多的符号&#xff0c;这部分优化效果将更明显&#xff0c;这将移除双份的 __OBJC_$_PROTOCOL_INSTANCE_METHODS_NSObject 等符号记录。



关注我们


我们是「老司机技术周报」&#xff0c;每周会发布一份关于 iOS 的周报&#xff0c;也会定期分享一些和 iOS 相关的技术。欢迎关注。


关注有礼&#xff0c;关注【老司机技术周报】&#xff0c;回复「2020」&#xff0c;领取学习大礼包。


参考资料


[1]


LLVM: https://github.com/llvm/llvm-project


[2]

WWDC 2016: https://developer.apple.com/videos/play/wwdc2016/405


[3]

LLVM Link Time Optimization: Design and Implementation: https://llvm.org/docs/LinkTimeOptimization.html


[4]

GitHub - DianQK/lto-example: https://github.com/DianQK/lto-example


[5]

LLVM Link Time Optimization: Design and Implementation: https://llvm.org/docs/LinkTimeOptimization.html


[6]

clang 文档: https://clang.llvm.org/docs/CommandGuide/clang.html#cmdoption-xlinker


[7]

opt - LLVM optimizer: https://llvm.org/docs/CommandGuide/opt.html


[8]

LLVM Link Time Optimization: Design and Implementation: https://llvm.org/docs/LinkTimeOptimization.html


[9]

LLVM Link Time Optimization: Design and Implementation: https://llvm.org/docs/LinkTimeOptimization.html


[10]

ld64: https://opensource.apple.com/source/ld64/ld64-609/


[11]

GitHub - DianQK/ld64-build: https://github.com/DianQK/ld64-build


[12]

opt - LLVM optimizer: https://llvm.org/docs/CommandGuide/opt.html


[13]

程序员的自我修养&#xff1a;链接、装载与库: https://www.duokan.com/book/115161





推荐阅读
  • 在软件开发过程中,经常需要将多个项目或模块进行集成和调试,尤其是当项目依赖于第三方开源库(如Cordova、CocoaPods)时。本文介绍了如何在Xcode中高效地进行多项目联合调试,分享了一些实用的技巧和最佳实践,帮助开发者解决常见的调试难题,提高开发效率。 ... [详细]
  • [转]doc,ppt,xls文件格式转PDF格式http:blog.csdn.netlee353086articledetails7920355确实好用。需要注意的是#import ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 本文介绍如何使用 Python 的 DOM 和 SAX 方法解析 XML 文件,并通过示例展示了如何动态创建数据库表和处理大量数据的实时插入。 ... [详细]
  • 开机自启动的几种方式
    0x01快速自启动目录快速启动目录自启动方式源于Windows中的一个目录,这个目录一般叫启动或者Startup。位于该目录下的PE文件会在开机后进行自启动 ... [详细]
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
  • 本文详细介绍了在 CentOS 7 系统中配置 fstab 文件以实现开机自动挂载 NFS 共享目录的方法,并解决了常见的配置失败问题。 ... [详细]
  • 本文介绍如何在 Android 中自定义加载对话框 CustomProgressDialog,包括自定义 View 类和 XML 布局文件的详细步骤。 ... [详细]
  • 本文介绍如何使用线段树解决洛谷 P1531 我讨厌它问题,重点在于单点更新和区间查询最大值。 ... [详细]
  • 在Delphi7下要制作系统托盘,只能制作一个比较简单的系统托盘,因为ShellAPI文件定义的TNotifyIconData结构体是比较早的版本。定义如下:1234 ... [详细]
  • 检查在所有可能的“?”替换中,给定的二进制字符串中是否出现子字符串“10”带 1 或 0 ... [详细]
  • PTArchiver工作原理详解与应用分析
    PTArchiver工作原理及其应用分析本文详细解析了PTArchiver的工作机制,探讨了其在数据归档和管理中的应用。PTArchiver通过高效的压缩算法和灵活的存储策略,实现了对大规模数据的高效管理和长期保存。文章还介绍了其在企业级数据备份、历史数据迁移等场景中的实际应用案例,为用户提供了实用的操作建议和技术支持。 ... [详细]
  • 如何将TS文件转换为M3U8直播流:HLS与M3U8格式详解
    在视频传输领域,MP4虽然常见,但在直播场景中直接使用MP4格式存在诸多问题。例如,MP4文件的头部信息(如ftyp、moov)较大,导致初始加载时间较长,影响用户体验。相比之下,HLS(HTTP Live Streaming)协议及其M3U8格式更具优势。HLS通过将视频切分成多个小片段,并生成一个M3U8播放列表文件,实现低延迟和高稳定性。本文详细介绍了如何将TS文件转换为M3U8直播流,包括技术原理和具体操作步骤,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 本文介绍了如何利用Shell脚本高效地部署MHA(MySQL High Availability)高可用集群。通过详细的脚本编写和配置示例,展示了自动化部署过程中的关键步骤和注意事项。该方法不仅简化了集群的部署流程,还提高了系统的稳定性和可用性。 ... [详细]
  • 解决Only fullscreen opaque activities can request orientation错误的方法
    本文介绍了在使用PictureSelectorLight第三方框架时遇到的Only fullscreen opaque activities can request orientation错误,并提供了一种有效的解决方案。 ... [详细]
author-avatar
手机用户2502935101
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有