C++ 代码执行时间随源代码的微小变化而变化,不应引入任何额外的工作

2024-01-25

在对某些代码进行基准测试时,我发现即使是最无害的代码更改,其执行时间也会有所不同。

我试图将下面的代码简化为最小的测试用例,但它仍然相当冗长(对此我深表歉意)。几乎任何改变都会很大程度上影响基准测试结果。

#include <string>
#include <vector>
#include <iostream>
#include <random>
#include <chrono>
#include <functional>

constexpr double usec_to_sec = 1000000.0;

// Simple convenience timer
class Timer
{
    std::chrono::high_resolution_clock::time_point start_time;
public:
    Timer() : start_time(std::chrono::high_resolution_clock::now()) { }
    int64_t operator()() const {
        return static_cast<int64_t>(
        std::chrono::duration_cast<std::chrono::microseconds>(
            std::chrono::high_resolution_clock::now()-start_time).count()
        );
    }
};

// Convenience random number generator
template <typename T>
class RandGen
{
    mutable std::default_random_engine generator;
    std::uniform_int_distribution<T> distribution;

    constexpr unsigned make_seed() const {
        return static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count());
    }
public:
    RandGen(T min, T max) : generator(make_seed()), distribution(min, max) { }
    T operator ()() { return distribution(generator); }
};

// Printer class
class Printer
{
    std::string filename;
    template <class S>    
    friend Printer &operator<<(Printer &, S &&s);
public:
    Printer(const char *filename) : filename(filename) {}
};

template <class S>
Printer &operator<<(Printer &pm, S &&s) {
    std::cout << s;
    return pm;
}

// +------------+
// | Main Stuff |
// +------------+
void runtest(size_t run_length)
{
    static RandGen<size_t> word_sz_generator(10, 20);
    static RandGen<int> rand_char_generator(0, 25);

    size_t total_char_count = 0;
    std::vector<std::string> word_list;
    word_list.reserve(run_length);

    Printer printer("benchmark.dat");
    printer << "Running test... ";

    Timer timer; // start timer
    for (auto i = 0; i < run_length; i++) {

        size_t word_sz = word_sz_generator();
        std::string word;
        for (auto sz = 0; sz < word_sz; sz++) {
            word.push_back(static_cast<char>(rand_char_generator())+'a');
        }
        word_list.emplace_back(std::move(word));
        total_char_count += word_sz;
    }
    int64_t execution_time_usec = timer(); // stop timer

    printer << /*run_length*/ word_list.size() << " words, and " 
            << total_char_count << " total characters, were built in "
            << execution_time_usec/usec_to_sec << " seconds.\n";
}

int main(int argc, char **argv)
{
    constexpr size_t iterations = 30;
    constexpr size_t run_length = 50000000;

    for (auto i = 0; i < iterations; i++)
        runtest(run_length);

    return EXIT_SUCCESS;
}

The 1st class, Timer, is just a small convenience class (intentionally not well-featured, for brevity) for timing the code.

I tried to do without the 2nd class RandGen (which just generates random values), but any attempt to exclude this from the test code made the problem auto-magically disappear. So, I suspect the issue has something to do with it. But I can't figure out how.

The 3rd class Printer seems entirely unnecessary for this question, but again, including it seems to exacerbate the issue.

所以,现在我们要做的是main()(它只是运行测试)和runtest().

runtest()很丑陋,所以请不要从“干净代码”的角度来看它。以任何方式改变它(例如移动内部for loop其自身功能)导致基准结果发生变化。最简单、最令人困惑的例子是最后一行:

printer << /*run_length*/ word_list.size() << " words, and " 
        << total_char_count << " total characters, were built in "
        << execution_time_usec/usec_to_sec << " seconds.\n";

在上面的行中,run_length and word_list.size()是相同的。向量的大小word_list定义为run_length。但是,如果我按原样运行代码,我得到的平均执行时间为9.8秒,而如果我取消注释run_length并注释掉word_list.size(),实际执行时间增加平均为10.6秒。我无法理解如此微不足道的代码更改如何会影响整个程序的时间到如此程度。

换句话说...

9.8秒:

printer << /*run_length*/ word_list.size() << " words, and " 
        << total_char_count << " total characters, were built in "
        << execution_time_usec/usec_to_sec << " seconds.\n";

10.6秒:

printer << run_length /*word_list.size()*/ << " words, and " 
        << total_char_count << " total characters, were built in "
        << execution_time_usec/usec_to_sec << " seconds.\n";

我多次重复了对上述变量进行注释和取消注释以及重新运行基准测试的练习。基准测试是可重复且一致的 - 即它们分别一致为 9.8 秒和 10.6 秒。

对于两种情况,代码输出如下所示:

Running test... 50000000 words, and 750000798 total characters, were built in 9.83379 seconds.
Running test... 50000000 words, and 749978210 total characters, were built in 9.84541 seconds.
Running test... 50000000 words, and 749996688 total characters, were built in 9.87418 seconds.
Running test... 50000000 words, and 749995415 total characters, were built in 9.85704 seconds.
Running test... 50000000 words, and 750017699 total characters, were built in 9.86186 seconds.
Running test... 50000000 words, and 749998680 total characters, were built in 9.83395 seconds.
...

Running test... 50000000 words, and 749988517 total characters, were built in 10.604 seconds.
Running test... 50000000 words, and 749958011 total characters, were built in 10.6283 seconds.
Running test... 50000000 words, and 749994387 total characters, were built in 10.6374 seconds.
Running test... 50000000 words, and 749995242 total characters, were built in 10.6445 seconds.
Running test... 50000000 words, and 749988379 total characters, were built in 10.6543 seconds.
Running test... 50000000 words, and 749969532 total characters, were built in 10.6722 seconds.
...

任何有关导致这种差异的原因的信息将不胜感激。

Notes:

  1. 甚至删除未使用的std::string filename成员对象来自Printer类产生不同的基准结果 - 这样做可以消除(或减少到微不足道的比例)上面提供的两个基准之间的差异。
  2. 当使用 g++(在 Ubuntu 上)编译时,这似乎不是问题。虽然,我不能明确地说;我对 Ubuntu 的测试是在同一台 Windows 计算机上的虚拟机中进行的,该虚拟机可能无法访问所有资源和处理器增强功能。
  3. I am using Visual Studio Community 2017 (version 15.7.4)
    • 编译器版本:19.14.26431
    • 所有测试和报告的结果均发布版本, 64-bit
  4. 系统:Win10、i7-6700K @ 4.00 GHz、32 GB RAM

您可能会遇到某种代码对齐效果。大多数时候,现代 x86-64 CPU 在对齐方面相当稳健,但对齐可能会影响分支预测器中相互别名的分支(如@rcgldr 提到的)以及各种前端效果。

See https://agner.org/optimize/ https://agner.org/optimize/,以及性能链接x86 标签维基 https://stackoverflow.com/tags/x86/info。但老实说,我认为这里没有任何有用的解释,除了您发现循环对来自前端或分支预测的对齐效果敏感之外。这意味着即使主程序中不同对齐的相同机器代码也可能具有不同的性能。

这是众所周知的现象。的回答一个目标文件中的代码对齐正在影响另一目标文件中函数的性能 https://stackoverflow.com/questions/25958649/code-alignment-in-one-object-file-is-affecting-the-performance-of-a-function-in/26003659#26003659关于对齐的重要性有一些一般性评论,另请参阅为什么引入无用的 MOV 指令会加速 x86_64 汇编中的紧密循环? https://stackoverflow.com/questions/17896714/why-would-introducing-useless-mov-instructions-speed-up-a-tight-loop-in-x86-64-a有一篇文章介绍了以不同顺序链接对象文件如何影响性能(这是工具链的意外影响),但我找不到它。

您可以使用硬件性能计数器来测量分支错误预测率,看看这是否可以解释为什么一个版本比另一个版本慢。或者是否有其他一些前端效果。

但不幸的是,你无能为力。微小的源差异,如果它们完全影响汇编,将改变所有内容的对齐方式。

有时,您可以通过用无分支代码替换分支来重新设计事物,使其对分支预测不太敏感。例如始终生成 16 字节的随机字母,并将其截断为随机长度。 (复制时一些大小上的分支可能是不可避免的,除非创建一个 16 字节std::string然后截断它可以是无分支的。)

您可以使用 SIMD 来加快速度,例如使用矢量化 PRNG,例如使用 SSE2 或 AVX2xorshift+ https://unix.stackexchange.com/questions/323845/whats-the-fastest-way-to-generate-a-1-gb-text-file-containing-random-digits/324520#324520一次生成 16 个字节的随机字母。 (通过打包字节操作有效地获得统一的 0..25 分布可能很棘手,但也许与我以前使用的 0..9 分布相同的技术生成 1GiB 的空格分隔的随机 ASCII 数字 https://unix.stackexchange.com/questions/323845/whats-the-fastest-way-to-generate-a-1-gb-text-file-containing-random-digits/324520#324520在 3.9GHz Skylake 上每约 0.03 秒会很有用。不过,它并不是完全均匀分布的,因为 65536 % 10 有余数(如 65536/25),但您可能可以更改质量与速度的权衡,并且仍然运行得很快。)


比较两个版本的编译器输出

两个版本的内循环的 asmruntest功能基本相同,至少如果我们看到编译器 asm 输出在 Godbolt 编译器资源管理器上 https://godbolt.org/#z:OYLghAFBqd5QCxAYwPYBMCmBRdBLAF1QCcAaPECAKxAEZSAbAQwDtRkBSAJgCFufSAZ1QBXYskwgA5NwDMeFsgYisAag6yAwoILEFwDdg4AGAIJyFSlZnVaAbpmRFihk%2Ba7zFytRs15UOsSYTAC2rmYWXta2msSs6KhhskYRHpbeNr7ICMSoLKjh7p5WPloAZiKKBP4sTAyFkSWZWv6BwUkp5mZoLDqYAB4ADsSqCSIARgw2IoKOAPpEc7PItgAiqrTGW9sAdMYafBFmAPTHqgDKeCGDU6o9Dix4mIo21SGYLt3MgoKqACpXD5uDgAdkOplUkNUOnQIBQOTyqDhCDwwAQcyCwmU1TycyUqGQAGs4W9MHNBqgFARoQQmMQCAtAQc3IMJgw8MgQG4of9AcQIABKVQgGl0hmkiAwuHZXL5ZGo9GY1DYmp4hgE4kgfIAd0FQtBPHUINW3KhVIAbAAWBaqVCDD5MZyCwV3PI6I3gnk8oIEMQsUXVZB4pg6XwW60EQwQU1emmw%2BGypEgdBiR2q5AhyNaKUJxFwkIc3LLPLoQRRmOxqE5mV5kAotEYzBYkQ4lhqjVwnWCgC0OjFjPeAp2aEqBEFFZ5AuZZh5oJNEWN066plOqk0eQeTxeqjiLASIVULBEIXGH1UwGeDucbgImGuzFvMQIAE97bV3v9CkoQ78AErxABxZ5gTBCsQhbJhJhsHMsDKJgRAYBld33OZnmABQbAvFgrxIJceRzSo8DKEgQjmKk5nwQI8HGFsal8P5DFGPAqJo1slwrHo%2BiGEZKkEVFsPQVQQiYQkyVmTB0BdTjqQNCcoR9P0Aw5YNQy0Xj%2BIkqNqwROUQEEZ8%2BlI/EiU7VBdSHUklksMlMApbJBWHUQWDHAUp1kT0oTnFk2Q5LkZyhf89yAlgID%2BISFFIf4hKYfohRFLCcP5YTRKWTAJL1SLKN0ajaLyCACxYSLhNij0jXnCEoTCu1EtUZ19TBHdMF9Yh/SyvRWJqaBLziZw3MNLyFxNdzgROM4AAU9Gcs9vx%2BNwZt%2BCaqSBBcPMhHMqLYVQyjwKZ30wPCoVve9HWaTR5ouRjPP8yEyj0Z5BMWqaRm4c1qp63CtF8CBHtvZ6uHNSLznUf6XsEPrvMmXyKx%2Bj4IGku4EDpVQACptt20JMDiradueDGIDR3HBw9AbzEXYajlMI6bhOmJzvOQoYb%2B177XelxPq0b7Jt%2B4HzUGEJAZ50H6tWuNpVEGT2c0aEDshBSWtUPn2MXUbgZ4bt1Y1zX1f4EaVzOUEpYAWSYBQLl9MoyiNTQ3FXfgtft7sdbMOxKUE4hRybMc%2BIALzJal3bbXbgAIBApxWis%2B0DVRAvQYLfB9v3GO1Eh0CWb25gS1mIE2SKuGMcHrqUlYY7jlpnMY5C8UR4gM%2B6x0SAgYxc4AVgL5d8LwX2bSIWkGCruk8SciX1n2cmKrWgh4wcJwPu0Se4Q2gxklUZPiFT9lVJF1f1%2BYggdkxD4HAgAO5iDkO2%2Bhrmz2GJb%2BW4LhT0UBBhOIQkdnQR177bnkb6emJfGBlwX8lRHibVvDoHYkDAHsULgCd4IxSRs0NKuPs9JVCIIrMREYEB4JEFUHgNYqhR6GgIQAk%2BZ8EAHHwTrXgwtdaVmhJ3P2K8U5p0IdvNOtdsJZ2/pWda2VNrbxll6LBtVcGoGhN7QhxDJExBYWvNOVDBDexoTwOhhcGHbx2KyQQ6JxhMCJJKWkgYVJZjOtXKMldsgD0zvXfkrl%2BBgCkEwJxvDYwk00awjee87zUwkHMfRhicwhFQEfbeDix4MJ7nUfuNcRzOVVhodYHDlHCI8WaZyVobQDEcLlNslkZiOEIYg8c7lVAoKIIMdBfJdYWDKLBVQABVc42A5i/gADIVl/tzXwZDKinzQiHcpyN5E7wgQnF0yN9aS0AaMwQkV4izOBB4TADBZhdKvs9GZxxkbkMGQgKZozT67x2BM/UMz75zIWXuJZqRZD3SInJdxMzol92sXEoe/8tCzNeQjOkBjfrzJXh8GwNEdrUlNvfJ5XpenfJycgPJA4ySFOQMcFFCxUCpRWLCqWlzix7kEDsDgzdNAsChWPEmbgqTRQUBAaldJgDIEiu8lGyMGV2DDqTEW0kBjDEYV3CFv00xukIbIYhHE3S3m4vy5hey2BDKSaoZu2wVUwPHltEgYiWwSNIbIEeVDdVS0IFeGoZYyl4FUZyhhAdwFjjlcHUOarvRNUUtgAAGgAST%2BHMc4jTNCaGwOcemFLjRSAFIwaQzcpCkBYNIYw0akRSGtrwfg0JRDiEyB4Wg0aCBxrDeG4klpjA7FoLIAAHEWstYrLSWgAJxlq4GWiNUhLTRpCHQLYMa82kETdGwQIAm65qkPG8NcBYAwCgBOsdiAUCJEGDjMgFA4ZzoXSAYAghaiDF0agAgpA0aAsoOMbt4wFB0mfNIbNpA0AhHeM5AA8iwBg57h3RqwMJNgUxu34CCE4PADh%2B0vtIPClskgpCXqWgwbt2V21gbDYwVg7BVGMGov2yA4a7StgA92O9shVC9knkkzgKbeC0BBHh%2B0H9nIcm7B8XIxBfjdgAOp1AYHhxjAxdBMD7emiQdBw2QakFGrtgHE39DLeabsVpzzIBWBAXQlRCRCggLgQgmq5D0DXCuqYf1ZC0CFMmvgvAc15tHUga987tPkEoOZ1dShaC1rmFaPd4KPj9ogEewDJ7ajEGfZe69t6CAPqfV%2Bu8CHP2Ae/Y4ao/7u3AdvBe6NEGoN6Bg9m/jCGUBIfZOMVDgoe2DEw9IbD/RuNiF43p5tQnY0iekGJiTUnDbnAAGpS3s6oPOpHo5/EY7VFTzhga6ciuua4C6BtcH06o4zL7XKkAQMELAxBKAFpAMq5trbSDtrLcWjwXBm7NzLaW2tzdZCWjLbW4T8ae3SD7QO0gQ6R2kGnRAJA4tWS7qXTZ7TdAgP4GcN97UcRBgJf45G6N1XLuJvUyvQgCBVB1ck5aaTsn5MsEU1Nkd4a5tMAW0ttbba6C1p2CCc0IIa20HE8YS05pm60C4BdhN12hC3fu/m0gxJVsCdkGD7tva7smce1OszWmPhWeXSNr7dmHNOf3a5w9x7T0%2BYS1exIAWgvPsu2%2BsLoGNd4B/dFpssX%2Bi5Pi7B8gU0BOXeg8D%2BDbBMvEYENl3L6GCumqK6sZprSOl4bvSVoQPHJAVYE1VnntXxMI9UI1lrGxa3teMJ1383Xeu/bU1mobwudOyAm/b9HrOsc47y%2BzpuAn1vg4Z1IG7g6TMg6kFwfHIIy07EtFwUn9nZDN1O%2BWkEdPS9XfL3z6bAv4DPeTERMoouGAIH7XT4gk%2BQCZ8q9zmrUhVij7h2HhrzXWsx462RhPPW7C/HhxvqPbWd9dcYznmbeePi45oHMDODBjb9CuMxCSeONsgC4LQHYzcu/7eMFaDTuaLTudj3rzv2pXtNsts3ITuJrQDAbQOaEdsqqRs3M2lzvTr3pftXrXh/psE3GAYzizjNg4PRjUCAJaEAA%3D%3D与您在 MSVC 的可执行文件中实际获得的内容相匹配。 (与 gcc/clang 不同,它的 asm 输出不一定会被组装到工作对象文件中。)如果您的真实版本版本进行了任何可以内联某些库代码的链接时优化,则它可能会在最终中做出不同的优化选择可执行的。

我放入了一个#ifdef所以我可以用-DUSE_RL拥有两个以不同方式构建相同源的 MSVC 2017 输出,并将这些 asm 输出提供给 diff 窗格。 (差异窗格位于我链接的混乱布局的底部;单击其上的全屏框即可显示该内容.)

整个功能的唯一区别是:

  • 一些指令的订购和寄存器选择,例如mov edx, DWORD PTR _tls_index and mov QWORD PTR run_length$GSCopy$1$[rbp-121], rcx位于仅运行一次的函数顶部。 (但不是代码大小,因此它们不会影响以后的对齐)。这应该对以后的代码没有影响,并且他们最终会对架构状态进行相同的更改,只是使用不再使用的不同的暂存寄存器。
  • 堆栈布局(局部变量相对于 RBP 的位置)。但所有的偏移量都在+127以下,所以它们仍然可以使用[rbp + disp8]寻址模式。
  • 不同的code-gen与实际源码的区别:

          mov     rdx, QWORD PTR word_list$[rbp-113]
          sub     rdx, QWORD PTR word_list$[rbp-121]  ; word_list.size() = end - start 
          ...
          sar     rdx, 5               ; >> 5   arithmetic right shift
    

    vs.

          mov     rdx, rsi             ; copy run_length from another register
    

    不,这些说明本身不可能解释速度差异。它们在每个时间间隔仅在某些 I/O 之前运行一次。

  • 一个额外的npad 7用于在函数底部附近的分支目标之前对齐(在call _Xtime_get_ticks),在上述代码差异之后。

红/绿有很大的差异,但这些是only来自不同编号的标签,除了函数开头的那三个指令。

但之前runtest, the word_list.size()版本包括代码??$?6_K@@YAAEAVPrinter@@AEAV0@$QEA_K@Z PROC功能对于使用的版本,它不会出现在任何地方run_length。 (C++ 名称修饰将类型转换为函数的 asm 名称中的时髦字符。)这正在做一些事情class Printer.

你说删除未使用的std::string filename from Printer删除了代码生成差异。好吧,这个功能可能会随着这个变化而消失。 IDKwhyMSVC 决定完全发布它,更不用说仅在一个版本与另一个版本中发布它了。

大概g++ -O3没有代码生成差异,这就是为什么你看不到差异。 (假设您的 VM 是硬件虚拟化,g++ 生成的机器代码仍然在 CPU 上本机运行。从操作系统获取新的内存页面可能会在 VM 中花费更长的时间,但循环中花费的主要时间可能是在此代码中的用户空间中。)


顺便说一句,海湾合作委员会警告

<source>:72:24: warning: comparison of integer expressions of different signedness: 'int' and 'size_t' {aka 'long unsigned int'} [-Wsign-compare]

     for (auto i = 0; i < run_length; i++) {
                      ~~^~~~~~~~~~~~

我没有仔细查看 asm 输出,看看这是否会导致 gcc 或 MSVC 的代码生成更糟糕,或者如果传递大量输入,是否会变得不安全。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ 代码执行时间随源代码的微小变化而变化,不应引入任何额外的工作 的相关文章

  • C#.Net 邮件将进入垃圾邮件文件夹

    我正在从 ASP net Web 应用程序发送电子邮件 邮件发送成功 没有失败 但大多数都进入了垃圾邮件文件夹 请帮助我克服垃圾邮件过滤器 我的发送邮件代码 public void SendMail string FromAddress s
  • 捕获 .aspx 和 .ascx 页面中的异常

    问题说明了一切 请看以下示例代码 ul li li ul
  • 为什么 C 程序使用 Scanf 给出奇怪的输出?

    我目前正在学习 C 编程 并且遇到了这个奇怪的输出 Program will try functionalities of the scanf function include
  • C# 中的 Stack<> 实现

    我最近一直在实现递归目录搜索实现 并且使用堆栈来跟踪路径元素 当我使用 string Join 连接路径元素时 我发现它们被颠倒了 当我调试该方法时 我查看了堆栈 发现堆栈内部数组中的元素本身是相反的 即最近 Push 的元素位于内部数组的
  • strlen() 编译时优化

    前几天我发现你可以找到编译时strlen使用这样的东西 template
  • 在 Mono 中反序列化 JSON 数据

    使用 Monodroid 时 是否有一种简单的方法可以将简单的 JSON 字符串反序列化为 NET 对象 System Json 只提供序列化 不提供反序列化 我尝试过的各种第三方库都会导致 Mono Monodroid 出现问题 谢谢 f
  • 为什么这个 makefile 在“make clean”上执行目标

    这是我当前的 makefile CXX g CXXFLAGS Wall O3 LDFLAGS TARGET testcpp SRCS main cpp object cpp foo cpp OBJS SRCS cpp o DEPS SRCS
  • C# 获取数据表中所有重复行的计数

    我通过运行存储过程来填充数据集 并且从数据集中填充数据表 DataSet RawDataSet DataAccessHelper RunProcedure storedprocedureName this will just return
  • 在 azure blob 存储中就地创建 zip 文件

    我将文件存储在 Blob 存储帐户内的一个容器中 我需要在第二个容器中创建一个 zip 文件 其中包含第一个容器中的文件 我有一个使用辅助角色和 DotNetZip 工作的解决方案 但由于 zip 文件的大小最终可能达到 1GB 我担心在进
  • MySQL 连接器 C++ 64 位在 Visual Studio 2012 中从源代码构建

    我正在尝试建立mySQL 连接器 C 从源头在视觉工作室2012为了64 bit建筑学 我知道这取决于一些boost头文件和C 连接器 跑步CMake生成一个项目文件 但该项目文件无法编译 因为有一大堆非常令人困惑的错误 这些错误可能与包含
  • 为什么python+sqlite3特别慢?

    我尝试使用 Python 2 7 4 sqlite3 和 Firefox SQLite Manager 0 8 0 处理对同一数据库的相同请求 在小型数据库 8000 条记录 上 Python 和 Firefox 都运行得很快并且给出了相同
  • 记录类名、方法名和行号的性能影响

    我正在我的 java 应用程序中实现日志记录 以便我可以调试应用程序投入生产后可能出现的潜在问题 考虑到在这种情况下 人们不会奢侈地使用 IDE 开发工具 以调试模式运行事物或单步执行完整代码 因此在每条消息中记录类名 方法名和行号将非常有
  • 任何人都可以清楚地告诉如何在不使用像 这样的预定义函数的情况下找到带有小数值或小数值的指数吗? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 例如 2 0 5 1 414 所以想要 我是 c 的新手 所以请解释简单的逻辑 如果不是复杂的逻辑也足够了 在数学中 从整数取幂到实数
  • 如何组合两个 lambda [重复]

    这个问题在这里已经有答案了 可能的重复 在 C 中组合两个 lambda 表达式 https stackoverflow com questions 1717444 combining two lamba expressions in c
  • Streamwriter 覆盖 txt 文件中的文本

    有没有什么方法可以重新打开流写入器而不创建新的写入对象 因为此时 当调用 WriteOdd 时 streamwriter 正在覆盖在它之前调用的 WriteEven public void WriteEven StreamWriter wr
  • 从后面的代码添加外部 css 文件

    我有一个 CSS 文件 例如 SomeStyle css 我是否可以将此样式表文档从其代码隐藏应用到 aspx 页面 您可以将文字控件添加到标头控件中 Page Header Controls Add new System Web UI L
  • 如果找不到指定的图像文件,显示默认图像的最佳方式?

    我有一个普通的电子商务应用程序 我将 ITEM IMAGE NAME 存储在数据库中 有时经理会拼错图像名称 为了避免 丢失图像 IE 中的红色 X 每次显示产品列表时 我都会检查服务器中是否有与该产品相关的图像 如果该文件不存在 我会将其
  • ContentDialog Windows 10 Mobile XAML - 全屏 - 填充

    我在项目中放置了一个 ContentDialog 用于 Windows 10 上的登录弹出窗口 当我在移动设备上运行此项目时 ContentDialog 未全屏显示 并且该元素周围有最小的填充 在键盘上可见 例如在焦点元素文本框上 键盘和内
  • 如何为有时异步的操作创建和实现接口

    假设我有数百个类 它们使用 计算 方法实现公共接口 一些类将执行异步 例如读取文件 而实现相同接口的其他类将执行同步代码 例如将两个数字相加 为了维护和性能 对此进行编码的好方法是什么 到目前为止我读到的帖子总是建议将异步 等待方法冒泡给调
  • 嵌入式linux编写AT命令

    我在向 GSM 模块写入 AT 命令时遇到问题 当我使用 minicom b 115200 D dev ttySP0 term vt100 时它工作完美 但我不知道如何在 C 代码中做同样的事情 我没有收到任何错误 但模块对命令没有反应 有

随机推荐