精华内容
下载资源
问答
  • 记得 14 年转岗来微信时,用自己笔记本编译微信工程才十来分钟。如今用公司配的 17 年款 27-inch iMac 编译要接近半小时;偶然间更新完代码,又莫名其妙需要全新编译。在这么低的编译效率下,开发心情受到严重影响。...

    1、引言

    岁月真是个养猪场,这几年,人胖了,微信代码也翻了。

    记得 14 年转岗来微信时,用自己笔记本编译微信工程才十来分钟。如今用公司配的 17 年款 27-inch iMac 编译要接近半小时;偶然间更新完代码,又莫名其妙需要全新编译。在这么低的编译效率下,开发心情受到严重影响。

    于是年初我向上头请示,优化微信编译效率,上头也同意了。

     

     

    学习交流:

    - 即时通讯/推送技术开发交流5群:215477170 [推荐]

    - 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

    (本文同步发布于:http://www.52im.net/thread-2873-1-1.html

    2、相关文章

    微信团队分享:微信移动端的全文检索多音字问题解决方案

    微信团队分享:iOS版微信的高性能通用key-value组件技术实践

    微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?

    微信团队原创分享:iOS版微信的内存监控系统技术实践

    iOS后台唤醒实战:微信收款到账语音提醒技术总结

    微信团队分享:微信Android版小视频编码填过的那些坑

    微信手机端的本地数据全文检索优化之路

    微信团队披露:微信界面卡死超级bug“15。。。。”的来龙去脉

    微信客户端团队负责人技术访谈:如何着手客户端性能监控和优化

    微信团队原创分享:Android版微信的臃肿之困与模块化实践之路

    微信团队原创分享:微信客户端SQLite数据库损坏修复实践

    微信团队原创分享:Android版微信从300KB到30MB的技术演进

    微信团队原创分享:Android内存泄漏监控和优化技巧总结

    全面总结iOS版微信升级iOS9遇到的各种“坑”

    微信团队原创资源混淆工具:让你的APK立减1M

    Android版微信安装包“减肥”实战记录

    iOS版微信安装包“减肥”实战记录

    移动端IM实践:iOS版微信界面卡顿监测方案

    移动端IM实践:iOS版微信小视频功能技术方案实录

    移动端IM实践:iOS版微信的多设备字体适配方案探讨

    手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)

    微信团队分享:Kotlin渐被认可,Android版微信的技术尝鲜之旅

    3、现有方案

    在动手之前,先搜索目前已有方案,大概情况如下。

    3.1 优化工程配置

    1)将 Debug Information Format 改为 DWARF:

    Debug 时是不需要生成符号表,可以检查一下子工程(尤其开源库)有没有设置正确。

    2)将 Build Active Architecture Only 改为 Yes:

    Debug 时是不需要生成全架构,可以检查一下子工程(尤其开源库)有没有设置正确。

    3)优化头文件搜索路径:

    避免工程 Header Search Paths 设置了路径递归引用:

    Xcode 编译源文件时,会根据 Header Search Paths 自动添加 -I 参数,如果递归引用的路径下子目录越多,-I 参数也越多,编译器预处理头文件效率就越低,所以不能简单的设置路径递归引用。同样 Framework Search Paths 也类似处理。

    3.2 使用 CocoaPods 管理第三方库

    这是业界常用的做法,利用 cocoapods 插件 cocoapods-packager 将任意的 pod 打包成 Static Library,省去重复编译的时间;但缺点是不方便调试源码,如果库代码反复修改,需要重新生成二进制并上传到内部服务器,等等。

    3.3 CCache

    CCache 是一个能够把编译的中间产物缓存起来的工具,不需要过多修改项目配置,也不需要修改开发工具链。Xcode 9 有个很偶然的 bug,在源码没有任何修改的情况下经常触发全新编译,用 CCache 很好的解决这一问题。但随着 Xcode 10 修复全量编译问题,这一方案逐步弃用了。

    3.4 distcc

    distcc 是一个分布式编译工具,它原理是把本地多个编译任务分发到网络中多个机器,其他机器编译完成后,再把产物返回给本机上执行链接,最终得到编译结果。

    3.5 硬件解决

    如把 Derived Data 目录放到由内存创建的虚拟磁盘,或者购买最新款的 iMac Pro...

    4、实践过程

    4.1 优化编译选项

    1)优化头文件搜索路径:

    把一些递归引用路径去了后,整体编译速度快了 20s。

    2)关闭 Enable Index-While-Building Functionality:

    这选项无意中找到的(Xcode 9 的新特性?),默认打开,作用是 Xcode 编译时会顺带建立代码索引,但影响编译速度。关闭后整体编译速度快 80s(Xcode 会换回以前的方式,在空闲时间建立代码索引)。

    4.2 优化 kinda

    kinda 是今年引入支付跨平台框架(C++),但编译速度奇慢,一个源文件编译都要 30s。另外生成的二进制大小在 App 占比较高,感觉有不少冗余代码,理论上减少冗余代码也能加快编译速度。

    经过分析 LinkMap 文件和使用 Xcode Preprocess 某些源文件,发现有以下问题:

    1)proto 文件生成的代码较多;

    2)某个基类/宏使用了大量模版。

    对于问题一:可以设置 proto 文件选项为 optimize_for=CODE_SIZE 来让 protobuf 编译器生成精简版代码。但我是用自己的工具生成(具体原理可看《iOS版微信安装包“减肥”实战记录》),代码更少。

    对于问题二:由于模版是编译期间的多态(增加代码膨胀和编译时间),所以可以把模版基类改成虚基类这种运行时的多态;另外推荐使用 hyper_function 取代 std::function,使得基类用通用函数指针,就能存储任意 lambda 回调函数,从而避免基类模板化。

    例如:

    template<typenameRequest, typenameResponse>

    classBaseCgi {

    public:

        BaseCgi(Request request, std::function<void(Response &)> &callback) {

            _request = request;

            _callback = callback;

        }

     

        voidonRequest(std::vector<uint8_t> &outData) {

            _request.toData(outData);

        }

     

        voidonResponse(std::vector<uint8_t> &inData) {

            Response response;

            response.fromData(inData);

            callback(response);

        }

     

    public:

        Request _request;

        std::function<void(Response &)> _callback;

    };

     

    classCgiA : publicBaseCgi<RequestA, ResponseA> {

    public:

        CgiA(RequestA &request, std::function<void(ResponseA &)> &callback) :

            BaseCgi(request, callback) {}

    };

    可改成:

    class BaseRequest {

    public:

        virtual void toData(std::vector<uint8_t> &outData) = 0;

    };

     

    classBaseResponse {

    public:

        virtualvoidfromData(std::vector<uint8_t> &outData) = 0;

    };

     

    classBaseCgi {

    public:

        template<typenameRequest, typenameResponse>

        BaseCgi(Request &request, hyper_function<void(Response &)> callback) {

            _request = newRequest(request);

            _response = newResponse;

            _callback = callback;

        }

     

        voidonRequest(std::vector<uint8_t> &outData) {

            _request->toData(outData);

        }

     

        voidonResponse(std::vector<uint8_t> &inData) {

            _response->fromData(inData);

            _callback(*_response);

        }

     

    public:

        BaseRequest *_request;

        BaseResponse *_response;

        hyper_function<void(BaseResponse &)> _callback;

    };

     

    classRequestA : publicBaseRequest { ... };

     

    classResponseA : publicBaseResponse { ... };

     

    classCgiA : publicBaseCgi {

    public:

        CgiA(RequestA &request, hyper_function<void(ResponseA &)> &callback) :

            BaseCgi(request, callback) {}

    };

    BaseCgi 由模版基类变成只有构造函数是模板的基类,onRequest 和 onResponse 逻辑代码并不因为基类模版实例化而被“复制黏贴”。

    经过上述优化:整体编译速度快了 70s,而 kinda 二进制也减少了 60%,效果特别明显。

    4.3 使用 PCH 预编译头文件

    PCH(Precompile Prefix Header File)文件,也就是预编译头文件,其文件里的内容能被项目中的其他所有源文件访问。通常放一些通用的宏和头文件,方便编写代码,提高效率。

    另外 PCH 文件预编译完成后,后面用到 PCH 文件的源文件编译速度也会加快。缺点是 PCH 文件和 PCH 引用到的头文件内容一旦发生变化,引用到 PCH 的所有源文件都要重新编译。所以使用时要谨慎。

    在 Xcode 里设置 Prefix Header 和 Precompile Prefix Header 即可使用 PCH 文件并对它进行预编译: 

    微信使用 PCH 预编译后:编译速度提升非常可观,快了接近 280s。

    5、终极优化

    通过上述优化,微信工程的编译时间由原来的 1,626.4s 下降到 1,182.8s,快了将近 450s,但仍然需要 20 分钟,令人不满意。

    如果继续优化,得从编译器下手。正如我们平常做的客户端性能优化,在优化之前,先分析原理,输出每个地方的耗时,针对耗时做相对应的优化。

    5.1 编译原理

    编译器,是把一种语言(通常是高级语言)转换为另一种语言(通常是低级语言)的程序。

    大多数编译器由三部分组成: 

    各部分的作用如下:

    前端(Frontend):负责解析源码,检查错误,生成抽象语法树(AST),并把 AST 转化成类汇编中间代码;

    优化器(Optimizer):对中间代码进行架构无关的优化,提高运行效率,减少代码体积,例如删除 if (0) 无效分支;

    后端(Backend):把中间代码转换成目标平台的机器码。

    LLVM 实现了更通用的编译框架,它提供了一系列模块化的编译器组件和工具链。首先它定义了一种 LLVM IR(Intermediate Representation,中间表达码)。Frontend 把原始语言转换成 LLVM IR;LLVM Optimizer 优化 LLVM IR;Backend 把 LLVM IR 转换为目标平台的机器语言。这样一来,不管是新的语言,还是新的平台,只要实现对应的 Frontend 和 Backend,新的编译器就出来了。 

    在 Xcode,C/C++/ObjC 的编译器是 Clang(前端)+LLVM(后端),简称 Clang。

    Clang 的编译过程有这几个阶段:

    ➜  clang -ccc-print-phases main.m

    0: input, "main.m", objective-c

    1: preprocessor, {0}, objective-c-cpp-output

    2: compiler, {1}, ir

    3: backend, {2}, assembler

    4: assembler, {3}, object

    5: linker, {4}, image

    6: bind-arch, "x86_64", {5}, image

    1)预处理:

    这阶段的工作主要是头文件导入,宏展开/替换,预编译指令处理,以及注释的去除。

    2)编译:

    这阶段做的事情比较多,主要有:

    a. 词法分析(Lexical Analysis):将代码转换成一系列 token,如大中小括号 paren'()' square'[]' brace'{}'、标识符 identifier、字符串 string_literal、数字常量 numeric_constant 等等;

    b. 语法分析(Semantic Analysis):将 token 流组成抽象语法树 AST;

    c. 静态分析(Static Analysis):检查代码错误,例如参数类型是否错误,调用对象方法是否有实现;

    d. 中间代码生成(Code Generation):将语法树自顶向下遍历逐步翻译成 LLVM IR。

    3)生成汇编代码:

    LLVM 将 LLVM IR 生成当前平台的汇编代码,期间 LLVM 根据编译设置的优化级别 Optimization Level 做对应的优化(Optimize),例如 Debug 的 -O0 不需要优化,而 Release 的 -Os 是尽可能优化代码效率并减少体积。

    4)生成目标文件:

    汇编器(Assembler)将汇编代码转换为机器代码,它会创建一个目标对象文件,以 .o 结尾。

    5)链接:

    链接器(Linker)把若干个目标文件链接在一起,生成可执行文件。

    5.2 分析耗时

    Clang/LLVM 编译器是开源的,我们可以从官网下载其源码,根据上述编译过程,在每个编译阶段埋点输出耗时,生成定制化的编译器。在自己准备动手的前一周,国外大神 Aras Pranckevičius 已经在 LLVM 项目提交了 rL357340 修改:clang 增加 -ftime-trace 选项,编译时生成 Chrome(chrome://tracing) JSON 格式的耗时报告,列出所有阶段的耗时。

    效果如下: 

    说明如下:

    1)整体编译(ExecuteCompiler)耗时 8,423.8ms

    2)其中前端(Frontend)耗时 5,307.9ms,后端(Backend)耗时 3,009.6ms

    3)而前端编译里头文件 SourceA 耗时 xx ms,B 耗时 xx ms,...

    4)头文件处理里 Parse ClassA 耗时 xx ms,B 耗时 xx ms,...

    5)等等

    这就是我想要的耗时报告!

    接下来修改工程 CC={YOUR PATH}/clang,让 Xcode 编译时使用自己的编译器;同时编译选项 OTHER_CFLAGS 后面增加 -ftime-trace,每个源文件编译后输出耗时报告。

    最终把所有报告汇聚起来,形成整体的编译耗时:

    由整体耗时可以看出:

    1)编译器前端处理(Frontend)耗时 7,659.2s,占整体 87%;

    2)而前端处理下头文件处理(Source)耗时 7,146.2s,占整体 71.9%!

    猜测:头文件嵌套严重,每个源文件都要引入几十个甚至几百个头文件,每个头文件源码要做预处理、词法分析、语法分析等等。实际上源文件不需要使用某些头文件里的定义(如 class、function),所以编译时间才那么长。

    于是又写了个工具,统计所有头文件被引用次数、总处理时间、头文件分组(指一个耗时顶部的头文件所引用到的所有子头文件的集合)。

    列出一份表格(截取 Top10): 

    如上表所示:

    Header1 处理时间 1187.7s,被引用 2,304 次;

    Header2 处理时间 1,124.9s,被引用 3,831 次;

    后面 Header3~10 都是被 Header1 引用。

    所以可以尝试优化 TopN 头文件里的头文件引用,尽量不包含其他头文件。

    5.3 解决耗时

    通常我们写代码时,如果用到某个类,就直接 include 该类声明所在头文件,但在头文件,我们可以用前置声明解决。

    因此优化头文件思路很简单:就是能用前置声明,就用前置声明替代 include。

    实际上改动量非常大:我跟组内另外的同事 vakeee 分工优化 Header1 和 Header2,花了整整 5 个工作日,才改完。效果还是有,整体编译时间减少 80s。

    但需要优化的头文件还有几十个,我们不可能继续做这种体力活。因此我们可以做这样的工具,通过 AST 找到代码里出现的标识符(包括类型、函数、宏),以及标识符定义所在文件,然后分析是否需要 include 它定义所在文件。

    先看看代码如何转换 AST,如以下代码:

    // HeaderA.h

    struct StructA {

        intval;

    };

     

    // HeaderB.h

    structStructB {

        intval;

    };

     

    // main.c

    #include "HeaderA.h"

    #include "HeaderB.h"

     

    inttestAndReturn(structStructA *a, structStructB *b) {

        returna->val;

    }

    控制台输入:

    ➜  TestContainer clang -Xclang -ast-dump -fsyntax-only main.c

    TranslationUnitDecl 0x7f8f36834208 <<invalid sloc>> <invalid sloc>

    |-RecordDecl 0x7faa62831d78 <./HeaderA.h:12:1, line:14:1> line:12:8 struct StructA definition

    | `-FieldDecl 0x7faa6383da38 <line:13:2, col:6> col:6 referenced val 'int'

    |-RecordDecl 0x7faa6383da80 <./HeaderB.h:12:1, line:14:1> line:12:8 struct StructB definition

    | `-FieldDecl 0x7faa6383db38 <line:13:2, col:6> col:6 val 'int'

    `-FunctionDecl 0x7faa6383de50 <main.c:35:1, line:37:1> line:35:5 testAndReturn 'int (struct StructA *, struct StructB *)'

      |-ParmVarDecl 0x7faa6383dc30 <col:19, col:35> col:35 used a 'struct StructA *'

      |-ParmVarDecl 0x7faa6383dd40 <col:38, col:54> col:54 b 'struct StructB *'

      `-CompoundStmt 0x7faa6383dfc8 <col:57, line:37:1>

        `-ReturnStmt 0x7faa6383dfb8 <line:36:2, col:12>

          `-ImplicitCastExpr 0x7faa6383dfa0 <col:9, col:12> 'int'<LValueToRValue>

            `-MemberExpr 0x7faa6383df70 <col:9, col:12> 'int'lvalue ->val 0x7faa6383da38

              `-ImplicitCastExpr 0x7faa6383df58 <col:9> 'struct StructA *'<LValueToRValue>

                `-DeclRefExpr 0x7faa6383df38 <col:9> 'struct StructA *'lvalue ParmVar 0x7faa6383dc30 'a''struct StructA *'

    从上可以看出:每一行包括 AST Node 的类型、所在位置(文件名,行号,列号)和结点描述信息。头文件定义的类也包含进 AST 中。AST Node 常见类型有 Decl(如 RecordDecl 结构体定义,FunctionDecl 函数定义)、Stmt(如 CompoundStmt 函数体括号内实现)。

     

    Clang AST 有三个重要的基类:ASTFrontendAction、ASTConsumer 以及 RecursiveASTVisitor。

    ClangTool 类读入命令行配置项后初始化 CompilerInstance;CompilerInstance 成员函数 ExcutionAction 会调用 ASTFrontendAction 3 个成员函数 BeginSourceFile(准备遍历 AST)、Execute(解析 AST)、EndSourceFileAction(结束遍历)。

    ASTFrontendAction 有个重要的纯虚函数 CreateASTConsumer(会被自己 BeginSourceFile 调用),用于返回读取 AST 的 ASTConsumer 对象。

    代码如下:

    class MyFrontendAction : public clang::ASTFrontendAction {

    public:

        virtualstd::unique_ptr<clang::ASTConsumer> CreateASTConsumer(clang::CompilerInstance &CI, llvm::StringRef file) override {

            TheRewriter.setSourceMgr(CI.getASTContext().getSourceManager(), CI.getASTContext().getLangOpts());

            returnllvm::make_unique<MyASTConsumer>(&CI);

        }

    };

     

    intmain(intargc, constchar**argv) {

        clang::tooling::CommonOptionsParser op(argc, argv, OptsCategory);

        clang::tooling::ClangTool Tool(op.getCompilations(), op.getSourcePathList());

        intresult = Tool.run(clang::tooling::newFrontendActionFactory<MyFrontendAction>().get());

     

        returnresult;

    }

    ASTConsumer 有若干个可以 override 的方法,用来接收 AST 解析过程中的回调,其中之一是工具用到的 HandleTranslationUnit 方法。当编译单元 TranslationUnit 的 AST 完整解析后,HandleTranslationUnit 会被回调。我们在 HandleTranslationUnit 使用 RecursiveASTVisitor 对象以深度优先的方式遍历 AST 所有结点。

    代码如下:

    class MyASTVisitor

    : public clang::RecursiveASTVisitor<MyASTVisitor> {

    public:

        explicitMyASTVisitor(clang::ASTContext *Ctx) {}

     

        boolVisitFunctionDecl(clang::FunctionDecl* decl) {

            // FunctionDecl 下的所有参数声明允许前置声明取代 include

            // 如上面 Demo 代码里 StructA、StructB

            returntrue;

        }

     

        boolVisitMemberExpr(clang::MemberExpr* expr) {

            // 被引用的成员所在的类,需要 include 它定义所在文件

            // 如 StructA

            returntrue;

        }

     

        boolVisitXXX(XXX) {

            returntrue;

        }

     

        // 同一个类型,可能出现若干次判定结果

        // 如果其中一个判断的结果需要 include,则 include

        // 否则使用前置声明代替 include

        // 例如 StructA 只能 include,StructB 可以前置声明

    };

     

    class MyASTConsumer : public clang::ASTConsumer {

    private:

        MyASTVisitor Visitor;

    public:

        explicitMyASTConsumer(clang::CompilerInstance *aCI)

        : Visitor(&(aCI->getASTContext())) {}

     

        void HandleTranslationUnit(clang::ASTContext &context) override {

            clang::TranslationUnitDecl *decl = context.getTranslationUnitDecl();

            Visitor.TraverseTranslationUnitDecl(decl);

        }

    };

    工具框架大致如上所示。

    不过早在 2011 年 Google 内部做了个基于 Clang libTooling 的工具 include-what-you-use,用来整理 C/C++ 头文件。

    这个工具的使用效果如下:

    ➜  include-what-you-use main.c

    HeaderA.h has correct #includes/fwd-decls)

    HeaderB.h has correct #includes/fwd-decls)

    main.c should add these lines:

    struct StructB;

    main.c should remove these lines:

    - #include "HeaderB.h"  // lines 2-2

    The full include-list formain.c:

    #include "HeaderA.h"  // for StructA

    struct StructB;

    我们在 IWYU 基础上,增加了 ObjC 语言的支持,并增强它的逻辑,让结果更好看(通常 IWYU 处理完后,会引入很多头文件和前置声明,我们做剪枝处理,进一步去掉多余的头文件和前置声明,篇幅限制就不多做解释了)。

    微信源码通过工具优化头文件引入后,整体编译时间降到了 710s。另外头文件依赖的减少,也能降低因修改头文件引起大规模源码重编的可能性。

    我们再用编译耗时分析工具分析当前瓶颈: 

    WCDB 头文件处理时间太长了,业务代码(如 Model 类)没有很好的隔离 WCDB 代码,把 WINQ 暴露出去,外面被动 include WCDB 头文件。解决方法有很多,例如 WCDB 相关放 category 头文件(XXModel+WCDB.h)里引入,或者跟其他库一样,把 放 PCH。

    最终编译时间优化到 540s 以下,是原来的三分之一,编译效率得到巨大的提升。

    6、优化总结

    总结微信的编译优化方案:

    即:

    A)优化头文件搜索路径;

    B)关闭 Enable Index-While-Building Functionality;

    C)优化 PB/模版,减少冗余代码;

    D)使用 PCH 预编译;

    E)使用工具优化头文件引入;尽量避免头文件里包含 C++ 标准库。

    7、未来展望

    期待公司的蓝盾分布式编译 for ObjC;另外可以把业务代码模块化,项目文件按模块加载,目前 kinda/小程序/mars 在很好的实践中。

    8、参考文献

    [1] 如何将 iOS 项目的编译速度提高5倍

    [2] 深入剖析 iOS 编译 Clang / LLVM

    [3] Clang之语法抽象语法树AST

    [4] time-trace: timeline / flame chart profiler for Clang

    [5] Introduction to the Clang AST

    附录:QQ、微信团队原创技术文章汇总

    微信朋友圈千亿访问量背后的技术挑战和实践总结

    腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(图片压缩篇)

    腾讯技术分享:腾讯是如何大幅降低带宽和网络流量的(音视频技术篇)

    微信团队分享:微信移动端的全文检索多音字问题解决方案

    腾讯技术分享:Android版手机QQ的缓存监控与优化实践

    微信团队分享:iOS版微信的高性能通用key-value组件技术实践

    微信团队分享:iOS版微信是如何防止特殊字符导致的炸群、APP崩溃的?

    腾讯技术分享:Android手Q的线程死锁监控系统技术实践

    微信团队原创分享:iOS版微信的内存监控系统技术实践

    让互联网更快:新一代QUIC协议在腾讯的技术实践分享

    iOS后台唤醒实战:微信收款到账语音提醒技术总结

    腾讯技术分享:社交网络图片的带宽压缩技术演进之路

    微信团队分享:视频图像的超分辨率技术原理和应用场景

    微信团队分享:微信每日亿次实时音视频聊天背后的技术解密

    QQ音乐团队分享:Android中的图片压缩技术详解(上篇)

    QQ音乐团队分享:Android中的图片压缩技术详解(下篇)

    腾讯团队分享:手机QQ中的人脸识别酷炫动画效果实现详解

    腾讯团队分享 :一次手Q聊天界面中图片显示bug的追踪过程分享

    微信团队分享:微信Android版小视频编码填过的那些坑

    微信手机端的本地数据全文检索优化之路

    企业微信客户端中组织架构数据的同步更新方案优化实战

    微信团队披露:微信界面卡死超级bug“15。。。。”的来龙去脉

    QQ 18年:解密8亿月活的QQ后台服务接口隔离技术

    月活8.89亿的超级IM微信是如何进行Android端兼容测试的

    以手机QQ为例探讨移动端IM中的“轻应用”

    一篇文章get微信开源移动端数据库组件WCDB的一切!

    微信客户端团队负责人技术访谈:如何着手客户端性能监控和优化

    微信后台基于时间序的海量数据冷热分级架构设计实践

    微信团队原创分享:Android版微信的臃肿之困与模块化实践之路

    微信后台团队:微信后台异步消息队列的优化升级实践分享

    微信团队原创分享:微信客户端SQLite数据库损坏修复实践

    腾讯原创分享(一):如何大幅提升移动网络下手机QQ的图片传输速度和成功率

    腾讯原创分享(二):如何大幅压缩移动网络下APP的流量消耗(下篇)

    腾讯原创分享(三):如何大幅压缩移动网络下APP的流量消耗(上篇)

    微信Mars:微信内部正在使用的网络层封装库,即将开源

    如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源

    开源libco库:单机千万连接、支撑微信8亿用户的后台框架基石 [源码下载]

    微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解

    微信团队原创分享:Android版微信后台保活实战分享(进程保活篇)

    微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)

    Android版微信从300KB到30MB的技术演进(PPT讲稿) [附件下载]

    微信团队原创分享:Android版微信从300KB到30MB的技术演进

    微信技术总监谈架构:微信之道——大道至简(演讲全文)

    微信技术总监谈架构:微信之道——大道至简(PPT讲稿) [附件下载]

    如何解读《微信技术总监谈架构:微信之道——大道至简》

    微信海量用户背后的后台系统存储架构(视频+PPT) [附件下载]

    微信异步化改造实践:8亿月活、单机千万连接背后的后台解决方案

    微信朋友圈海量技术之道PPT [附件下载]

    微信对网络影响的技术试验及分析(论文全文)

    一份微信后台技术架构的总结性笔记

    架构之道:3个程序员成就微信朋友圈日均10亿发布量[有视频]

    快速裂变:见证微信强大后台架构从0到1的演进历程(一)

    快速裂变:见证微信强大后台架构从0到1的演进历程(二)

    微信团队原创分享:Android内存泄漏监控和优化技巧总结

    全面总结iOS版微信升级iOS9遇到的各种“坑”

    微信团队原创资源混淆工具:让你的APK立减1M

    微信团队原创Android资源混淆工具:AndResGuard [有源码]

    Android版微信安装包“减肥”实战记录

    iOS版微信安装包“减肥”实战记录

    移动端IM实践:iOS版微信界面卡顿监测方案

    微信“红包照片”背后的技术难题

    移动端IM实践:iOS版微信小视频功能技术方案实录

    移动端IM实践:Android版微信如何大幅提升交互性能(一)

    移动端IM实践:Android版微信如何大幅提升交互性能(二)

    移动端IM实践:实现Android版微信的智能心跳机制

    移动端IM实践:WhatsApp、Line、微信的心跳策略分析

    移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)

    移动端IM实践:iOS版微信的多设备字体适配方案探讨

    信鸽团队原创:一起走过 iOS10 上消息推送(APNS)的坑

    腾讯信鸽技术分享:百亿级实时消息推送的实战经验

    IPv6技术详解:基本概念、应用现状、技术实践(上篇)

    IPv6技术详解:基本概念、应用现状、技术实践(下篇)

    腾讯TEG团队原创:基于MySQL的分布式数据库TDSQL十年锻造经验分享

    微信多媒体团队访谈:音视频开发的学习、微信的音视频技术和挑战等

    了解iOS消息推送一文就够:史上最全iOS Push技术详解

    腾讯技术分享:微信小程序音视频技术背后的故事

    腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面

    微信多媒体团队梁俊斌访谈:聊一聊我所了解的音视频技术

    腾讯音视频实验室:使用AI黑科技实现超低码率的高清实时视频聊天

    腾讯技术分享:微信小程序音视频与WebRTC互通的技术思路和实践

    手把手教你读取Android版微信和手Q的聊天记录(仅作技术研究学习)

    微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)

    微信技术分享:微信的海量IM聊天消息序列号生成实践(容灾方案篇)

    腾讯技术分享:GIF动图技术详解及手机QQ动态表情压缩技术实践

    微信团队分享:Kotlin渐被认可,Android版微信的技术尝鲜之旅

    社交软件红包技术解密(一):全面解密QQ红包技术方案——架构、技术实现等

    社交软件红包技术解密(二):解密微信摇一摇红包从0到1的技术演进

    社交软件红包技术解密(三):微信摇一摇红包雨背后的技术细节

    社交软件红包技术解密(四):微信红包系统是如何应对高并发的

    社交软件红包技术解密(五):微信红包系统是如何实现高可用性的

    社交软件红包技术解密(六):微信红包系统的存储层架构演进实践

    社交软件红包技术解密(九):谈谈手Q红包的功能逻辑、容灾、运维、架构等

    QQ设计团队分享:新版 QQ 8.0 语音消息改版背后的功能设计思路

    微信团队分享:极致优化,iOS版微信编译速度3倍提升的实践总结

    >> 更多同类文章 ……

    (本文同步发布于:http://www.52im.net/thread-2873-1-1.html

    展开全文
  • 微信小游戏加载太问题总结

    千次阅读 2019-11-28 15:09:59
    开发微信小游戏加载过问题总结了一下几个优化方案: 1:使用预加载的方式,调用Creator提供的API=>preLoadScene. cc.director.preloadScene('Main', (completedCount, totalCount, item) => { this....

    开发微信小游戏加载过慢问题总结了一下几个优化方案:

    1:使用预加载的方式,调用Creator提供的API=>preLoadScene.

    cc.director.preloadScene('Main', (completedCount, totalCount, item) => {
        this.percent = Math.floor(completedCount / totalCount * 100);
        this.LoadingBar.progress = this.percent / 100;
        this.loadText.string = this.percent + '%';
        if (this.LoadingBar.progress >= 1) {
            cc.director.loadScene('Main');
        }
    }, (error) => {
        console.log(error);
    });

    2:压缩,简单暴力的方式:https://tinypng.com/,这是一个免费而且超高压缩比的一个压图网站,推荐给大家

    3:更改微信的最大同时下载量,如图:

    展开全文
  • 微信公众号:Python数据科学 知乎:python数据分析师 前言 当大家谈到数据分析时,提及最多的语言就是Python和SQL。Python之所以适合数据分析,是因为它有很多第三方强大的库来协助,pandas就是其中之一。...

    作者:xiaoyu

    微信公众号:Python数据科学

    知乎:python数据分析师


    在这里插入图片描述

    前言

    当大家谈到数据分析时,提及最多的语言就是PythonSQL。Python之所以适合数据分析,是因为它有很多第三方强大的库来协助,pandas就是其中之一。pandas的文档中是这样描述的:

    “快速,灵活,富有表现力的数据结构,旨在使”关系“或”标记“数据的使用既简单又直观。”

    我们知道pandas的两个主要数据结构:dataframeseries,我们对数据的一些操作都是基于这两个数据结构的。但在实际的使用中,我们可能很多时候会感觉运行一些数据结构的操作会异常的慢。一个操作慢几秒可能看不出来什么,但是一整个项目中很多个操作加起来会让整个开发工作效率变得很低。有的朋友抱怨pandas简直太慢了,其实对于pandas的一些操作也是有一定技巧的。

    pandas是基于numpy库的数组结构上构建的,并且它的很多操作都是(通过numpy或者pandas自身由Cpython实现并编译成C的扩展模块)在C语言中实现的。因此,如果正确使用pandas的话,它的运行速度应该是非常快的。

    本篇将要介绍几种pandas中常用到的方法,对于这些方法使用存在哪些需要注意的问题,以及如何对它们进行速度提升。

    • 将datetime数据与时间序列一起使用的优点
    • 进行批量计算的最有效途径
    • 通过HDFStore存储数据节省时间

    使用Datetime数据节省时间

    我们来看一个例子。

    >>> import pandas as pd
    >>> pd.__version__
    '0.23.1'
    
    # 导入数据集
    >>> df = pd.read_csv('demand_profile.csv')
    >>> df.head()
         date_time  energy_kwh
    0  1/1/13 0:00       0.586
    1  1/1/13 1:00       0.580
    2  1/1/13 2:00       0.572
    3  1/1/13 3:00       0.596
    4  1/1/13 4:00       0.592
    

    从运行上面代码得到的结果来看,好像没有什么问题。但实际上pandas和numpy都有一个dtypes 的概念。如果没有特殊声明,那么date_time将会使用一个 object 的dtype类型,如下面代码所示:

    >>> df.dtypes
    date_time      object
    energy_kwh    float64
    dtype: object
    
    >>> type(df.iat[0, 0])
    str
    

    object 类型像一个大的容器,不仅仅可以承载 str,也可以包含那些不能很好地融进一个数据类型的任何特征列。而如果我们将日期作为 str 类型就会极大的影响效率。

    因此,对于时间序列的数据而言,我们需要让上面的date_time列格式化为datetime对象数组(pandas称之为时间戳)。pandas在这里操作非常简单,操作如下:

    >>> df['date_time'] = pd.to_datetime(df['date_time'])
    >>> df['date_time'].dtype
    datetime64[ns]
    

    我们来运行一下这个df看看转化后的效果是什么样的。

    >>> df.head()
                   date_time    energy_kwh
    0    2013-01-01 00:00:00         0.586
    1    2013-01-01 01:00:00         0.580
    2    2013-01-01 02:00:00         0.572
    3    2013-01-01 03:00:00         0.596
    4    2013-01-01 04:00:00         0.592
    

    date_time的格式已经自动转化了,但这还没完,在这个基础上,我们还是可以继续提高运行速度的。如何提速呢?为了更好的对比,我们首先通过 timeit 装饰器来测试一下上面代码的转化时间。

    >>> @timeit(repeat=3, number=10)
    ... def convert(df, column_name):
    ...     return pd.to_datetime(df[column_name])
    
    >>> df['date_time'] = convert(df, 'date_time')
    Best of 3 trials with 10 function calls per trial:
    Function `convert` ran in average of 1.610 seconds.
    ```python
    1.61s,看上去挺快,但其实可以更快,我们来看一下下面的方法。
    

    @timeit(repeat=3, number=100)
    def convert_with_format(df, column_name):
    … return pd.to_datetime(df[column_name],
    … format=’%d/%m/%y %H:%M’)
    Best of 3 trials with 100 function calls per trial:
    Function convert_with_format ran in average of 0.032 seconds.

    **结果只有0.032s,快了将近50倍。**原因是:我们设置了转化的格式format。由于在CSV中的datetimes并不是 ISO 8601 格式的,如果不进行设置的话,那么pandas将使用 `dateutil` 包把每个字符串str转化成date日期。
    
    相反,如果原始数据datetime已经是 `ISO 8601` 格式了,那么pandas就可以立即使用最快速的方法来解析日期。这也就是为什么提前设置好格式format可以提升这么多。
    
    ## pandas数据的循环操作
    
    仍然基于上面的数据,我们想添加一个新的特征,但这个新的特征是基于一些时间条件的,根据时长(小时)而变化,如下:
    ![在这里插入图片描述](https://img-blog.csdnimg.cn/20190212201545532.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1eGlhb3NtZA==,size_16,color_FFFFFF,t_70)
    
    
    因此,按照我们正常的做法就是使用apply方法写一个函数,函数里面写好时间条件的逻辑代码。
    ```python
    def apply_tariff(kwh, hour):
     """计算每个小时的电费"""  
        if 0 <= hour < 7:
            rate = 12
        elif 7 <= hour < 17:
            rate = 20
        elif 17 <= hour < 24:
            rate = 28
        else:
            raise ValueError(f'Invalid hour: {hour}')
        return rate * kwh
    

    然后使用for循环来遍历df,根据apply函数逻辑添加新的特征,如下:

    >>> # 不赞同这种操作
    >>> @timeit(repeat=3, number=100)
    ... def apply_tariff_loop(df):
    ...     """Calculate costs in loop.  Modifies `df` inplace."""
    ...     energy_cost_list = []
    ...     for i in range(len(df)):
    ...         # 获取用电量和时间(小时)
    ...         energy_used = df.iloc[i]['energy_kwh']
    ...         hour = df.iloc[i]['date_time'].hour
    ...         energy_cost = apply_tariff(energy_used, hour)
    ...         energy_cost_list.append(energy_cost)
    ...     df['cost_cents'] = energy_cost_list
    ... 
    >>> apply_tariff_loop(df)
    Best of 3 trials with 100 function calls per trial:
    Function `apply_tariff_loop` ran in average of 3.152 seconds.
    

    对于那些写Pythonic风格的人来说,这个设计看起来很自然。然而,这个循环将会严重影响效率,也是不赞同这么做。原因有几个:

    • 首先,它需要初始化一个将记录输出的列表。
    • 其次,它使用不透明对象范围(0,len(df))循环,然后在应用apply_tariff()之后,它必须将结果附加到用于创建新DataFrame列的列表中。它还使用df.iloc [i] ['date_time']执行所谓的链式索引,这通常会导致意外的结果。
    • 但这种方法的最大问题是计算的时间成本。对于8760行数据,此循环花费了3秒钟。接下来,你将看到一些改进的Pandas结构迭代解决方案。

    使用itertuples() 和iterrows() 循环

    那么推荐做法是什么样的呢?

    实际上可以通过pandas引入itertuples和iterrows方法可以使效率更快。这些都是一次产生一行的生成器方法,类似scrapy中使用的yield用法。

    .itertuples为每一行产生一个namedtuple,并且行的索引值作为元组的第一个元素。nametuple是Python的collections模块中的一种数据结构,其行为类似于Python元组,但具有可通过属性查找访问的字段。

    .iterrows为DataFrame中的每一行产生(index,series)这样的元组。

    虽然.itertuples往往会更快一些,但是在这个例子中使用.iterrows,我们看看这使用iterrows后效果如何。

    >>> @timeit(repeat=3, number=100)
    ... def apply_tariff_iterrows(df):
    ...     energy_cost_list = []
    ...     for index, row in df.iterrows():
    ...         # 获取用电量和时间(小时)
    ...         energy_used = row['energy_kwh']
    ...         hour = row['date_time'].hour
    ...         # 添加cost列表
    ...         energy_cost = apply_tariff(energy_used, hour)
    ...         energy_cost_list.append(energy_cost)
    ...     df['cost_cents'] = energy_cost_list
    ...
    >>> apply_tariff_iterrows(df)
    Best of 3 trials with 100 function calls per trial:
    Function `apply_tariff_iterrows` ran in average of 0.713 seconds.
    

    **语法方面:**这样的语法更明确,并且行值引用中的混乱更少,因此它更具可读性。

    **在时间收益方面:**快了近5倍! 但是,还有更多的改进空间。我们仍然在使用某种形式的Python for循环,这意味着每个函数调用都是在Python中完成的,理想情况是它可以用Pandas内部架构中内置的更快的语言完成。

    Pandas的 .apply()方法

    我们可以使用.apply方法而不是.iterrows进一步改进此操作。Pandas的.apply方法接受函数(callables)并沿DataFrame的轴(所有行或所有列)应用它们。在此示例中,lambda函数将帮助你将两列数据传递给apply_tariff()

    >>> @timeit(repeat=3, number=100)
    ... def apply_tariff_withapply(df):
    ...     df['cost_cents'] = df.apply(
    ...         lambda row: apply_tariff(
    ...             kwh=row['energy_kwh'],
    ...             hour=row['date_time'].hour),
    ...         axis=1)
    ...
    >>> apply_tariff_withapply(df)
    Best of 3 trials with 100 function calls per trial:
    Function `apply_tariff_withapply` ran in average of 0.272 seconds.
    

    .apply的语法优点很明显,行数少,代码可读性高。在这种情况下,所花费的时间大约是.iterrows方法的一半。

    但是,这还不是“非常快”。一个原因是.apply()将在内部尝试循环遍历Cython迭代器。但是在这种情况下,传递的lambda不是可以在Cython中处理的东西,因此它在Python中调用,因此并不是那么快。

    如果你使用.apply()获取10年的小时数据,那么你将需要大约15分钟的处理时间。如果这个计算只是大型模型的一小部分,那么你真的应该加快速度。这也就是矢量化操作派上用场的地方。

    矢量化操作:使用.isin()选择数据

    **什么是矢量化操作?**如果你不基于一些条件,而是可以在一行代码中将所有电力消耗数据应用于该价格(df ['energy_kwh'] * 28),类似这种。这个特定的操作就是矢量化操作的一个例子,它是在Pandas中执行的最快方法。

    但是如何将条件计算应用为Pandas中的矢量化运算?一个技巧是根据你的条件选择和分组DataFrame,然后对每个选定的组应用矢量化操作。 在下一个示例中,你将看到如何使用Pandas的.isin()方法选择行,然后在向量化操作中实现上面新特征的添加。在执行此操作之前,如果将date_time列设置为DataFrame的索引,则会使事情更方便:

    df.set_index('date_time', inplace=True)
    
    @timeit(repeat=3, number=100)
    def apply_tariff_isin(df):
    # 定义小时范围Boolean数组
        peak_hours = df.index.hour.isin(range(17, 24))
        shoulder_hours = df.index.hour.isin(range(7, 17))
        off_peak_hours = df.index.hour.isin(range(0, 7))
    
        # 使用上面的定义
        df.loc[peak_hours, 'cost_cents'] = df.loc[peak_hours, 'energy_kwh'] * 28
        df.loc[shoulder_hours,'cost_cents'] = df.loc[shoulder_hours, 'energy_kwh'] * 20
        df.loc[off_peak_hours,'cost_cents'] = df.loc[off_peak_hours, 'energy_kwh'] * 12
    

    我们来看一下结果如何。

    >>> apply_tariff_isin(df)
    Best of 3 trials with 100 function calls per trial:
    Function `apply_tariff_isin` ran in average of 0.010 seconds.
    

    为了了解刚才代码中发生的情况,我们需要知道.isin()方法返回的是一个布尔值数组,如下所示:

    [False, False, False, ..., True, True, True]
    

    这些值标识哪些DataFrame索引(datetimes)落在指定的小时范围内。然后,当你将这些布尔数组传递给DataFrame的.loc索引器时,你将获得一个仅包含与这些小时匹配的行的DataFrame切片。在那之后,仅仅是将切片乘以适当的费率,这是一种快速的矢量化操作。

    这与我们上面的循环操作相比如何?首先,你可能会注意到不再需要apply_tariff(),因为所有条件逻辑都应用于行的选择。因此,你必须编写的代码行和调用的Python代码会大大减少。

    处理时间怎么样?比不是Pythonic的循环快315倍,比.iterrows快71倍,比.apply快27倍。

    还可以做的更好吗?

    在apply_tariff_isin中,我们仍然可以通过调用df.locdf.index.hour.isin三次来进行一些“手动工作”。如果我们有更精细的时隙范围,你可能会争辩说这个解决方案是不可扩展的。幸运的是,在这种情况下,你可以使用Pandas的pd.cut() 函数以编程方式执行更多操作:

    @timeit(repeat=3, number=100)
    def apply_tariff_cut(df):
        cents_per_kwh = pd.cut(x=df.index.hour,
                               bins=[0, 7, 17, 24],
                               include_lowest=True,
                               labels=[12, 20, 28]).astype(int)
        df['cost_cents'] = cents_per_kwh * df['energy_kwh']
    

    让我们看看这里发生了什么。pd.cut() 根据每小时所属的bin应用一组标签(costs)。

    注意include_lowest参数表示第一个间隔是否应该是包含左边的(您希望在组中包含时间= 0)。
    这是一种完全矢量化的方式来获得我们的预期结果,它在时间方面是最快的:

    >>> apply_tariff_cut(df)
    Best of 3 trials with 100 function calls per trial:
    Function `apply_tariff_cut` ran in average of 0.003 seconds. 
    

    到目前为止,时间上基本快达到极限了,只需要花费不到一秒的时间来处理完整的10年的小时数据集。但是,最后一个选项是使用 NumPy 函数来操作每个DataFrame的底层NumPy数组,然后将结果集成回Pandas数据结构中。

    使用Numpy继续加速

    使用Pandas时不应忘记的一点是Pandas SeriesDataFrames是在NumPy库之上设计的。这为你提供了更多的计算灵活性,因为Pandas可以与NumPy阵列和操作无缝衔接。

    下面,我们将使用NumPy的 digitize() 函数。它类似于Pandas的cut(),因为数据将被分箱,但这次它将由一个索引数组表示,这些索引表示每小时所属的bin。然后将这些索引应用于价格数组:

    @timeit(repeat=3, number=100)
    def apply_tariff_digitize(df):
        prices = np.array([12, 20, 28])
        bins = np.digitize(df.index.hour.values, bins=[7, 17, 24])
        df['cost_cents'] = prices[bins] * df['energy_kwh'].values
    

    与cut函数一样,这种语法非常简洁易读。但它在速度方面有何比较?让我们来看看:

    >>> apply_tariff_digitize(df)
    Best of 3 trials with 100 function calls per trial:
    Function `apply_tariff_digitize` ran in average of 0.002 seconds.
    

    在这一点上,仍然有性能提升,但它本质上变得更加边缘化。使用Pandas,它可以帮助维持“层次结构”,如果你愿意,可以像在此处一样进行批量计算,这些通常排名从最快到最慢(最灵活到最不灵活):

    1. 使用向量化操作:没有for循环的Pandas方法和函数。

    2. 将.apply方法:与可调用方法一起使用。

    3. 使用.itertuples:从Python的集合模块迭代DataFrame行作为namedTuples。

    4. 使用.iterrows:迭代DataFrame行作为(index,Series)对。虽然Pandas系列是一种灵活的数据结构,但将每一行构建到一个系列中然后访问它可能会很昂贵。

    5. 使用“element-by-element”循环:使用df.loc或df.iloc一次更新一个单元格或行。

    在这里插入图片描述

    使用HDFStore防止重新处理

    现在你已经了解了Pandas中的加速数据流程,接着让我们探讨如何避免与最近集成到Pandas中的HDFStore一起重新处理时间。

    通常,在构建复杂数据模型时,可以方便地对数据进行一些预处理。例如,如果您有10年的分钟频率耗电量数据,即使你指定格式参数,只需将日期和时间转换为日期时间可能需要20分钟。你真的只想做一次,而不是每次运行你的模型,进行测试或分析。

    你可以在此处执行的一项非常有用的操作是预处理,然后将数据存储在已处理的表单中,以便在需要时使用。但是,如何以正确的格式存储数据而无需再次重新处理?如果你要另存为CSV,则只会丢失datetimes对象,并且在再次访问时必须重新处理它。

    Pandas有一个内置的解决方案,它使用 HDF5,这是一种专门用于存储表格数据阵列的高性能存储格式。 Pandas的 HDFStore 类允许你将DataFrame存储在HDF5文件中,以便可以有效地访问它,同时仍保留列类型和其他元数据。它是一个类似字典的类,因此您可以像读取Python dict对象一样进行读写。

    以下是将预处理电力消耗DataFrame df存储在HDF5文件中的方法:

    # 创建储存对象,并存为 processed_data
    data_store = pd.HDFStore('processed_data.h5')
    
    # 将 DataFrame 放进对象中,并设置 key 为 preprocessed_df
    data_store['preprocessed_df'] = df
    data_store.close()
    

    现在,你可以关闭计算机并休息一下。等你回来的时候,你处理的数据将在你需要时为你所用,而无需再次加工。以下是如何从HDF5文件访问数据,并保留数据类型:

    # 获取数据储存对象
    data_store = pd.HDFStore('processed_data.h5')
    
    # 通过key获取数据
    preprocessed_df = data_store['preprocessed_df']
    data_store.close()
    

    数据存储可以容纳多个表,每个表的名称作为键。

    关于在Pandas中使用HDFStore的注意事项:您需要安装PyTables> = 3.0.0,因此在安装Pandas之后,请确保更新PyTables,如下所示:

    pip install --upgrade tables
    

    结论

    如果你觉得你的Pandas项目不够快速,灵活,简单和直观,请考虑重新考虑你使用该库的方式。

    这里探讨的示例相当简单,但说明了Pandas功能的正确应用如何能够大大改进运行时和速度的代码可读性。以下是一些经验,可以在下次使用Pandas中的大型数据集时应用这些经验法则:

    • 尝试尽可能使用矢量化操作,而不是在df 中解决for x的问题。如果你的代码是许多for循环,那么它可能更适合使用本机Python数据结构,因为Pandas会带来很多开销。
    • 如果你有更复杂的操作,其中矢量化根本不可能或太难以有效地解决,请使用.apply方法。
    • 如果必须循环遍历数组(确实发生了这种情况),请使用.iterrows()或.itertuples()来提高速度和语法。
    • Pandas有很多可选性,几乎总有几种方法可以从A到B。请注意这一点,比较不同方法的执行方式,并选择在项目环境中效果最佳的路线。
    • 一旦建立了数据清理脚本,就可以通过使用HDFStore存储中间结果来避免重新处理。
    • 将NumPy集成到Pandas操作中通常可以提高速度并简化语法。

    参考:https://realpython.com/fast-flexible-pandas/

    如果觉得有帮助,还请给点个赞!

    欢迎关注我的个人公众号:Python数据科学

    在这里插入图片描述

    展开全文
  • 监控微信小程序中的HTTP请求

    千次阅读 2019-06-27 10:05:50
    摘要: 请求时间太长,影响用户体验,使用 Fundebug 监控请求。...时隔一年,微信小程序插件已经更新到 1.3.1, 而且提供了一个非常有用的功能,支持监控 HTTP 请求。对于轻量级的性能分析,可以说已经够用。 ...

    摘要: 请求时间太长,影响用户体验,使用 Fundebug 监控慢请求。

    Fundebug 的微信小程序监控插件在 0.5.0 版本已经支持监控 HTTP 请求错误,在小程序中通过wx.request发起 HTTP 请求,如果请求失败,会被捕获并上报。时隔一年,微信小程序插件已经更新到 1.3.1, 而且提供了一个非常有用的功能,支持监控 HTTP 慢请求。对于轻量级的性能分析,可以说已经够用。

    本文我们以一个天气微信小程序为例(由bodekjan开发),来演示如何监控慢请求。bmap-wx.js中的weather()函数调用百度地图小程序 api 提供的接口来获取天气预报信息。

    接入监控

    由于使用百度的 api,我们无法确认该接口的稳定性,可能有时候会特别慢,导致天气信息显示不出来。于是,我们使用 Fundebug 来监控请求过慢的情况。接下来,我们来演示如何监控慢请求。注册账户后,记得要在创建项目是选择“微信小程序”这一项目类型。

    根据指示完成接入流程:

    app.js顶部加入下面的代码(记得将 apikey 替换成你自己的):

    var fundebug = require("./utils/fundebug.1.3.1.min.js");
    fundebug.init({
        apikey: "YOUR-API-KEY",
        monitorMethodCall: true,
        monitorMethodArguments: true,
        monitorHttpData: true,
        setSystemInfo: true,
        setUserInfo: true,
        setLocation: true,
        httpTimeout: 200
    });
    

    虽然init()函数只要设置apikey即可使用,但是为了最大程度发挥监控的威力,我们不妨多设置一些监控选项。
    微信小程序插件有很多的可配置项,由于涉及到数据,默认处于关闭状态。我们可以监控函数调用(monitorMethodCall),以及函数调用的参数(monitorMethodArguments),监控 HTTP 请求的 Body 中的数据(monitorHttpData),获取系统信息(setSystemInfo)、用户信息(setUserInfo)、地理位置(setLocation)。

    监控慢请求

    最后,最重要的一步,配置httpTimeout来监控超过特定时长的请求,httpTimeout 类型为 Number,单位为毫秒(ms)。演示起见,我们将时间设置为 200 毫秒。

    在微信开发者工具内运行代码,Fundebug 立马收到报错。小程序发往https://api.map.baidu.com/telematics/v3/weather接口的请求时长为 571ms,超过预设时间 200ms。

    错误详情

    该请求返回代码 200,表明能够正常获取数据。点击该条错误,查看错误详情:

    通过上方的统计数据,我们可以知道获取天气信息的接口出现缓慢情况的趋势,影响的用户数量,累计发生的次数。我们可以以此来评估是否需要优化该接口,甚至替换成其它第三方接口来解决这个问题。

    附加信息

    因为配置了 monitorHttpData,所以我们可以查看到请求 body 中的详细数据。当请求失败的时候,有时候需要结合参数来分析失败的原因。

    另外,用户行为数据记录了小程序运行的详细状况,特别是函数的调用序列,对于理解出错前程序的执行逻辑很有帮助:

    关于Fundebug

    Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用

    img

    版权声明

    转载时请注明作者 Fundebug以及本文地址:

    https://blog.fundebug.com/2019/06/27/monitor-httptimeout-in-weather-miniapp/

    展开全文
  • 调用了第三方统计JS一、停用第三方统计JS后续可以考虑如下方式来加载:通常我们的网站里面会加载一些js代码,如:统计,google广告一堆,最后弄得页面加载速度。解决办法:换一个js包含的方式,让javascri...
  • 为什么微信小程序的云开发响应这么?Why is the response of WeChat applet cloud development so slow?为什么微信小程序的云开发响应这么?从云函数请求开始到接收,用了几乎一秒钟的时间,导致用户在加载时...
  • 步骤: 打开微信开发者工具---工具--项目详情--本地设置--启动多核心编译不勾选--即可
  • 打开会话速度慢 在同一个会话有较多的历史消息下,各种查询,更新,删除等操作,速度明显下降。 在会话内有较大量历史消息情况下,进入速度/刷新速度明显降低。 分析阶段 整个优化我们分2个阶段进行: 第一...
  • ubuntu 安装微信

    2018-09-20 17:56:45
    sudo snap install electronic-wechat,直接运行这个命令即可以安装,只是下载速度慢,也不知道是我网速慢,用了两个小时才完成。图标有点low,在扫二维码登录时会显示是网页版登录,还有不能保存聊天记录。这个希望...
  • 怎样解决用到特殊字体的页面因为要加载庞大的字体库导致加载的问题  在微信H5活动页面里, 为了增强活动的表现力或视觉效果,经常会出现设计师在页面中使用特殊字体库的情况,,如果页面没有复杂的交互,直接将...
  • 小伙伴们有没有注意到这种情况,有时明明网速挺好,但是在微信中打开公众号的文章或者其他网页时加载却是很,这在很多情况下,是因为微信内置的浏览器内核不是安卓原生的WebView,而是腾讯的X5 WebView内核,使用...
  • 微信红包

    千次阅读 2015-12-08 18:14:52
    但是作为Android开发者来说,我们在抢红包的同时意识到了很多问题,就是手动去抢红包的速度慢了,当然这些有很多原因导致了。或许是网络的原因,而且这个也是最大的原因。但是其他的不可忽略的因素也是要考虑到进去...
  • 微信小程序开发工具win10下编译非常的解决方法 点击微信小程序开发工具右侧详情,在本地设置中将【启用多核心编译】选项去掉,开发工具重启即可 ...
  • 微信小程序测试,开发者工具响应秒开,安卓也就是0.1秒的加载,但是在ios真机至少响应4秒以上,查看后台并不是后台响应 2、原因 到处查资料,发现是因为let's encrpyt 证书导致的 Let's Encrypt 证书的OCSP...
  • 春节期间用微信抢发红包已成为一种新的祝福方式但每当手而和大红包擦肩而过时那种真实的挫败感和失落感是不是让你感到很无奈呢? 今天,小悦妞为大家讲一讲如何提高抢红包速度的各种方法来来来,发家致富全靠这些...
  • 微信小程序之提高应用速度小技巧

    万次阅读 2017-03-10 14:21:30
    小程序科普类的文章已经很多了,今天这里讲的是针对小程序的优化方法,可以有效提高小程序的响应速度和用户体验。当然,开发体验也提高不少。
  • 微信小程序云开发初体验,感受飞一般的速度

    千次阅读 多人点赞 2019-01-26 16:43:47
    微信小程序最近更新了.支持了云开发.可以不用写接口,直接使用腾讯云的为个人配置的数据库.开发起来,飞一般的流畅 先放链接 小程序云开发主要有三部分,数据库,存储管理,和云函数. 彻底实现了小程序的闭环. 首先:...
  • 微信硬件平台

    千次阅读 2017-09-01 17:03:09
    微信公众号分为订阅号和服务号,其中服务号需要xxx认证,个人不好申请。 但服务号提供“设备功能”。若想要使用微信硬件平台,就必须花钱认证服务号。通过微信硬件平台提供的AirSync、AirKiss、硬件JSAPI、直连SDK...
  • 加快R的运行速度

    千次阅读 2016-03-11 19:30:09
    各位朋友,我已开通微信公共号:小程在线 我会把文章及时的更新到公共号上,欢迎大家的关注。 源文件:http://pan.baidu.com/s/1o7g4edc 可以先运行下,观察此时的运动状态 x<-matrix(1:(6666*6666),6666...
  • 微信已经成为了我们生活中不可社交工具,很多小伙伴使用微信也好几年了,微信好友也越来越多。微信使用时间长了,我们经常会删一些好友或者自己被别人删除,因此我删好友,一般是先找下哪些人把自己给删了,然后我们...
  • 微信小程序

    2019-10-25 19:31:06
    微信小程序的生命周期 微信小程序组件的生命周期? 简单描述下微信小程序的相关文件类型? app.json 是对当前小程序的全局配置,讲述三个配置各个项的含义? 小程序的wxss和css有哪些不一样的地方? 小程序有...
  • 微信与朋友圈后台架构

    万次阅读 多人点赞 2016-03-30 21:47:28
    微信朋友圈技术之道:三个人的后台团队与每日十亿的发布量视屏讲解概述截止到2015年7月,微信每月活跃用户约5.49亿,朋友圈每天的发表量(包括赞和评论)超过10亿,浏览量超过100亿。得益于4G网络的发展,以上数据仍...
  • php 实现微信支付

    千次阅读 2019-06-03 20:25:59
    在接入微信统一支付接口之后,微信中调用创建订单功能,尝试多次之后,服务器对普通请求的响应速度,甚至出现500错误,在重启服务后恢复正常。 问题解决: 该问题经过长时间检查,最终定位到微信接口WxPay.Data....
  • 历史的年轮总是碾过慢速度的落后者,而带动了嗅觉敏锐的瞻望者,跟上节奏的是有准备的人。小程序由点点细雨,掀起满城风雨。微信小程序的推出,在移动互联网行业里掀起的风浪的在持续。给很多创业者和中小企业带来了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 21,059
精华内容 8,423
关键字:

微信运行速度慢