优化 itoa 功能

2024-05-02

我正在考虑如何使用SSE指令实现整数(4字节,无符号)到字符串的转换。通常的例程是将数字相除并将其存储在局部变量中,然后反转字符串(本示例中缺少反转例程):

char *convert(unsigned int num, int base) {
    static char buff[33];  

    char *ptr;    
    ptr = &buff[sizeof(buff) - 1];    
    *ptr = '\0';

    do {
        *--ptr="0123456789abcdef"[num%base];
        num /= base;
    } while(num != 0);

    return ptr;
}

但反转需要额外的时间。除了 SSE 指令之外,还有其他算法可以更好地并行化函数吗?


Terje Mathisen 发明了一种非常快的 itoa(),不需要查找表。如果您对其工作原理的解释不感兴趣,请跳至性能或实现。

15 多年前,Terje Mathisen 提出了以 10 为基数的并行 itoa()。其想法是采用 32 位值并将其分解为两个 5 位数字的块。 (在谷歌上快速搜索“Terje Mathisen itoa”给出了这篇文章:http://computer-programming-forum.com/46-asm/7aa4b50bce8dd985.htm http://computer-programming-forum.com/46-asm/7aa4b50bce8dd985.htm)

我们这样开始:

void itoa(char *buf, uint32_t val)
{
    lo = val % 100000;
    hi = val / 100000;
    itoa_half(&buf[0], hi);
    itoa_half(&buf[5], lo);
}

现在我们只需要一个可以将域 [0, 99999] 中的任何整数转换为字符串的算法。一个天真的方法可能是:

// 0 <= val <= 99999
void itoa_half(char *buf, uint32_t val)
{
    // Move all but the first digit to the right of the decimal point.
    float tmp = val / 10000.0;

    for(size_t i = 0; i < 5; i++)
    {
        // Extract the next digit.
        int digit = (int) tmp;

        // Convert to a character.
        buf[i] = '0' + (char) digit;

        // Remove the lead digit and shift left 1 decimal place.
        tmp = (tmp - digit) * 10.0;
    }
}

我们将使用 4.28 定点数学,而不是使用浮点,因为在我们的例子中它要快得多。也就是说,我们将二进制小数点固定在第 28 位位置,这样 1.0 就表示为 2^28。要转换为定点,我们只需乘以 2^28 即可。我们可以通过使用 0xf0000000 掩码轻松地向下舍入到最接近的整数,并且可以通过使用 0x0fffffff 掩码来提取小数部分。

(注:Terje 的算法在定点格式的选择上略有不同。)

所以现在我们有:

typedef uint32_t fix4_28;

// 0 <= val <= 99999
void itoa_half(char *buf, uint32_t val)
{
    // Convert `val` to fixed-point and divide by 10000 in a single step.
    // N.B. we would overflow a uint32_t if not for the parentheses.
    fix4_28 tmp = val * ((1 << 28) / 10000);

    for(size_t i = 0; i < 5; i++)
    {
        int digit = (int)(tmp >> 28);
        buf[i] = '0' + (char) digit;
        tmp = (tmp & 0x0fffffff) * 10;
    }
}

此代码的唯一问题是 2^28 / 10000 = 26843.5456,它被截断为 26843。这会导致某些值不准确。例如,itoa_half(buf, 83492) 生成字符串“83490”。如果我们在转换为 4.28 定点时应用一个小修正,那么该算法适用于域 [0, 99999] 中的所有数字:

// 0 <= val <= 99999
void itoa_half(char *buf, uint32_t val)
{
    fix4_28 const f1_10000 = (1 << 28) / 10000;

    // 2^28 / 10000 is 26843.5456, but 26843.75 is sufficiently close.
    fix4_28 tmp = val * ((f1_10000 + 1) - (val / 4);

    for(size_t i = 0; i < 5; i++)
    {
        int digit = (int)(tmp >> 28);
        buf[i] = '0' + (char) digit;
        tmp = (tmp & 0x0fffffff) * 10;
    }
}

Terje 将 itoa_half 部分交错用于低半部和高半部:

void itoa(char *buf, uint32_t val)
{
    fix4_28 const f1_10000 = (1 << 28) / 10000;
    fix4_28 tmplo, tmphi;

    lo = val % 100000;
    hi = val / 100000;

    tmplo = lo * (f1_10000 + 1) - (lo / 4);
    tmphi = hi * (f1_10000 + 1) - (hi / 4);

    for(size_t i = 0; i < 5; i++)
    {
        buf[i + 0] = '0' + (char)(tmphi >> 28);
        buf[i + 5] = '0' + (char)(tmplo >> 28);
        tmphi = (tmphi & 0x0fffffff) * 10;
        tmplo = (tmplo & 0x0fffffff) * 10;
    }
}

如果循环完全展开,还有一个额外的技巧可以使代码稍微快一些。乘以 10 被实现为 LEA+SHL 或 LEA+ADD 序列。我们可以通过乘以 5 来节省 1 条指令,这仅需要一个 LEA。这与每次通过循环将 tmphi 和 tmplo 右移 1 个位置具有相同的效果,但我们可以通过调整移位计数和掩码来进行补偿,如下所示:

uint32_t mask = 0x0fffffff;
uint32_t shift = 28;

for(size_t i = 0; i < 5; i++)
{
    buf[i + 0] = '0' + (char)(tmphi >> shift);
    buf[i + 5] = '0' + (char)(tmplo >> shift);
    tmphi = (tmphi & mask) * 5;
    tmplo = (tmplo & mask) * 5;
    mask >>= 1;
    shift--;
}

这仅在循环完全展开时才有用,因为您可以预先计算每次迭代的移位和掩码值。

最后,该例程产生补零结果。如果 val == 0,您可以通过返回指向非 0 的第一个字符或最后一个字符的指针来消除填充:

char *itoa_unpadded(char *buf, uint32_t val)
{
    char *p;
    itoa(buf, val);

    p = buf;

    // Note: will break on GCC, but you can work around it by using memcpy() to dereference p.
    if (*((uint64_t *) p) == 0x3030303030303030)
        p += 8;

    if (*((uint32_t *) p) == 0x30303030)
        p += 4;

    if (*((uint16_t *) p) == 0x3030)
        p += 2;

    if (*((uint8_t *) p) == 0x30)
        p += 1;

    return min(p, &buf[15]);
}

还有一个适用于 64 位(即 AMD64)代码的额外技巧。额外、更宽的寄存器可以有效地在寄存器中累加每个 5 位数字组;计算出最后一位数字后,您可以将它们与 SHRD 粉碎在一起,或与 0x3030303030303030 一起粉碎,然后存储到内存中。这使我的性能提高了约 12.3%。

矢量化

我们可以在 SSE 单元上按原样执行上述算法,但性能几乎没有任何提升。然而,如果我们将值分割成更小的块,我们就可以利用 SSE4.1 32 位乘法指令。我尝试了三种不同的分割:

  1. 2组5位数字
  2. 3组4位数字
  3. 4组3位数字

最快的变体是 4 组 3 位数字。结果见下文。

表现

除了 vitaut 和 Inge Henriksen 建议的算法之外,我还测试了 Terje 算法的许多变体。我通过对输入的详尽测试来验证每个算法的输出与 itoa() 匹配。

我的数据取自运行 Windows 7 64 位的 Westmere E5640。我以实时优先级进行基准测试并锁定到核心 0。我将每个算法执行 4 次,以将所有内容强制放入缓存中。我使用 RDTSCP 对 2^24 个调用进行计时,以消除任何动态时钟速度变化的影响。

我对 5 种不同的输入模式进行了计时:

  1. itoa(0 .. 9) -- 接近最佳情况的性能
  2. itoa(1000 .. 1999) -- 更长的输出,没有分支错误预测
  3. itoa(100000000 .. 999999999) -- 最长的输出,没有分支错误预测
  4. itoa(256 个随机值) -- 不同的输出长度
  5. itoa(65536 随机值) -- 不同的输出长度and冲击 L1/L2 缓存

数据:


ALG        TINY     MEDIUM   LARGE    RND256   RND64K   NOTES
NULL         7 clk    7 clk    7 clk    7 clk    7 clk  Benchmark overhead baseline
TERJE_C     63 clk   62 clk   63 clk   57 clk   56 clk  Best C implementation of Terje's algorithm
TERJE_ASM   48 clk   48 clk   50 clk   45 clk   44 clk  Naive, hand-written AMD64 version of Terje's algorithm
TERJE_SSE   41 clk   42 clk   41 clk   34 clk   35 clk  SSE intrinsic version of Terje's algorithm with 1/3/3/3 digit grouping
INGE_0      12 clk   31 clk   71 clk   72 clk   72 clk  Inge's first algorithm
INGE_1      20 clk   23 clk   45 clk   69 clk   96 clk  Inge's second algorithm
INGE_2      18 clk   19 clk   32 clk   29 clk   36 clk  Improved version of Inge's second algorithm
VITAUT_0     9 clk   16 clk   32 clk   35 clk   35 clk  vitaut's algorithm
VITAUT_1    11 clk   15 clk   33 clk   31 clk   30 clk  Improved version of vitaut's algorithm
LIBC        46 clk  128 clk  329 clk  339 clk  340 clk  MSVCRT12 implementation
  

我的编译器(VS 2013 Update 4)生成了令人惊讶的糟糕代码; Terje 算法的汇编版本只是一个简单的翻译,而且速度足足快了 21%。我还对 SSE 实现的性能感到惊讶,我预计它会更慢。最令人惊讶的是 INGE_2、VITAUT_0 和 VITAUT_1 的速度有多快。值得称赞的是,vitaut 提出了一种便携式解决方案,甚至在装配级别上也超越了我的最佳努力。

注意:INGE_1 是 Inge Henriksen 的第二个算法的修改版本,因为原始算法有一个错误。

INGE_2 基于 Inge Henriksen 给出的第二种算法。它将字符串本身存储在 char[][5] 数组中,而不是在 char*[] 数组中存储指向预先计算的字符串的指针。另一个重大改进是它在输出缓冲区中存储字符的方式。它存储比所需更多的字符,并使用指针算术返回指向第一个非零字符的指针。结果明显更快——即使与 Terje 算法的 SSE 优化版本相比也具有竞争力。应该指出的是,微基准测试有点偏向于这种算法,因为在实际应用中,600K 数据集会不断地破坏缓存。

VITAUT_1 基于 vitaut 算法,有两个小变化。第一个变化是它在主循环中复制字符对,从而减少存储指令的数量。与INGE_2类似,VITAUT_1复制两个最终字符并使用指针算术返回指向字符串的指针。

执行

这里我给出了 3 个最有趣的算法的代码。

TERJE_ASM:

; char *itoa_terje_asm(char *buf<rcx>, uint32_t val<edx>)
;
; *** NOTE ***
; buf *must* be 8-byte aligned or this code will break!
itoa_terje_asm:
    MOV     EAX, 0xA7C5AC47
    ADD     RDX, 1
    IMUL    RAX, RDX
    SHR     RAX, 48          ; EAX = val / 100000

    IMUL    R11D, EAX, 100000
    ADD     EAX, 1
    SUB     EDX, R11D        ; EDX = (val % 100000) + 1

    IMUL    RAX, 214748      ; RAX = (val / 100000) * 2^31 / 10000
    IMUL    RDX, 214748      ; RDX = (val % 100000) * 2^31 / 10000

    ; Extract buf[0] & buf[5]
    MOV     R8, RAX
    MOV     R9, RDX
    LEA     EAX, [RAX+RAX]   ; RAX = (RAX * 2) & 0xFFFFFFFF
    LEA     EDX, [RDX+RDX]   ; RDX = (RDX * 2) & 0xFFFFFFFF
    LEA     RAX, [RAX+RAX*4] ; RAX *= 5
    LEA     RDX, [RDX+RDX*4] ; RDX *= 5
    SHR     R8, 31           ; R8 = buf[0]
    SHR     R9, 31           ; R9 = buf[5]

    ; Extract buf[1] & buf[6]
    MOV     R10, RAX
    MOV     R11, RDX
    LEA     EAX, [RAX+RAX]   ; RAX = (RAX * 2) & 0xFFFFFFFF
    LEA     EDX, [RDX+RDX]   ; RDX = (RDX * 2) & 0xFFFFFFFF
    LEA     RAX, [RAX+RAX*4] ; RAX *= 5
    LEA     RDX, [RDX+RDX*4] ; RDX *= 5
    SHR     R10, 31 - 8
    SHR     R11, 31 - 8
    AND     R10D, 0x0000FF00 ; R10 = buf[1] << 8
    AND     R11D, 0x0000FF00 ; R11 = buf[6] << 8
    OR      R10D, R8D        ; R10 = buf[0] | (buf[1] << 8)
    OR      R11D, R9D        ; R11 = buf[5] | (buf[6] << 8)

    ; Extract buf[2] & buf[7]
    MOV     R8, RAX
    MOV     R9, RDX
    LEA     EAX, [RAX+RAX]   ; RAX = (RAX * 2) & 0xFFFFFFFF
    LEA     EDX, [RDX+RDX]   ; RDX = (RDX * 2) & 0xFFFFFFFF
    LEA     RAX, [RAX+RAX*4] ; RAX *= 5
    LEA     RDX, [RDX+RDX*4] ; RDX *= 5
    SHR     R8, 31 - 16
    SHR     R9, 31 - 16
    AND     R8D, 0x00FF0000  ; R8 = buf[2] << 16
    AND     R9D, 0x00FF0000  ; R9 = buf[7] << 16
    OR      R8D, R10D        ; R8 = buf[0] | (buf[1] << 8) | (buf[2] << 16)
    OR      R9D, R11D        ; R9 = buf[5] | (buf[6] << 8) | (buf[7] << 16)

    ; Extract buf[3], buf[4], buf[8], & buf[9]
    MOV     R10, RAX
    MOV     R11, RDX
    LEA     EAX, [RAX+RAX]   ; RAX = (RAX * 2) & 0xFFFFFFFF
    LEA     EDX, [RDX+RDX]   ; RDX = (RDX * 2) & 0xFFFFFFFF
    LEA     RAX, [RAX+RAX*4] ; RAX *= 5
    LEA     RDX, [RDX+RDX*4] ; RDX *= 5
    SHR     R10, 31 - 24
    SHR     R11, 31 - 24
    AND     R10D, 0xFF000000 ; R10 = buf[3] << 24
    AND     R11D, 0xFF000000 ; R11 = buf[7] << 24
    AND     RAX, 0x80000000  ; RAX = buf[4] << 31
    AND     RDX, 0x80000000  ; RDX = buf[9] << 31
    OR      R10D, R8D        ; R10 = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)
    OR      R11D, R9D        ; R11 = buf[5] | (buf[6] << 8) | (buf[7] << 16) | (buf[8] << 24)
    LEA     RAX, [R10+RAX*2] ; RAX = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24) | (buf[4] << 32)
    LEA     RDX, [R11+RDX*2] ; RDX = buf[5] | (buf[6] << 8) | (buf[7] << 16) | (buf[8] << 24) | (buf[9] << 32)

    ; Compact the character strings
    SHL     RAX, 24          ; RAX = (buf[0] << 24) | (buf[1] << 32) | (buf[2] << 40) | (buf[3] << 48) | (buf[4] << 56)
    MOV     R8, 0x3030303030303030
    SHRD    RAX, RDX, 24     ; RAX = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24) | (buf[4] << 32) | (buf[5] << 40) | (buf[6] << 48) | (buf[7] << 56)
    SHR     RDX, 24          ; RDX = buf[8] | (buf[9] << 8)

    ; Store 12 characters. The last 2 will be null bytes.
    OR      R8, RAX
    LEA     R9, [RDX+0x3030]
    MOV     [RCX], R8
    MOV     [RCX+8], R9D

    ; Convert RCX into a bit pointer.
    SHL     RCX, 3

    ; Scan the first 8 bytes for a non-zero character.
    OR      EDX, 0x00000100
    TEST    RAX, RAX
    LEA     R10, [RCX+64]
    CMOVZ   RAX, RDX
    CMOVZ   RCX, R10

    ; Scan the next 4 bytes for a non-zero character.
    TEST    EAX, EAX
    LEA     R10, [RCX+32]
    CMOVZ   RCX, R10
    SHR     RAX, CL          ; N.B. RAX >>= (RCX % 64); this works because buf is 8-byte aligned.

    ; Scan the next 2 bytes for a non-zero character.
    TEST    AX, AX
    LEA     R10, [RCX+16]
    CMOVZ   RCX, R10
    SHR     EAX, CL          ; N.B. RAX >>= (RCX % 32)

    ; Convert back to byte pointer. N.B. this works because the AMD64 virtual address space is 48-bit.
    SAR     RCX, 3

    ; Scan the last byte for a non-zero character.
    TEST    AL, AL
    MOV     RAX, RCX
    LEA     R10, [RCX+1]
    CMOVZ   RAX, R10

    RETN

INGE_2:

uint8_t len100K[100000];
char str100K[100000][5];

void itoa_inge_2_init()
{
    memset(str100K, '0', sizeof(str100K));

    for(uint32_t i = 0; i < 100000; i++)
    {
        char buf[6];
        itoa(i, buf, 10);
        len100K[i] = strlen(buf);
        memcpy(&str100K[i][5 - len100K[i]], buf, len100K[i]);
    }
}

char *itoa_inge_2(char *buf, uint32_t val)
{
    char *p = &buf[10];
    uint32_t prevlen;

    *p = '\0';

    do
    {
        uint32_t const old = val;
        uint32_t mod;

        val /= 100000;
        mod = old - (val * 100000);

        prevlen = len100K[mod];
        p -= 5;
        memcpy(p, str100K[mod], 5);
    }
    while(val != 0);

    return &p[5 - prevlen];
}

VITAUT_1:

static uint16_t const str100p[100] = {
    0x3030, 0x3130, 0x3230, 0x3330, 0x3430, 0x3530, 0x3630, 0x3730, 0x3830, 0x3930,
    0x3031, 0x3131, 0x3231, 0x3331, 0x3431, 0x3531, 0x3631, 0x3731, 0x3831, 0x3931,
    0x3032, 0x3132, 0x3232, 0x3332, 0x3432, 0x3532, 0x3632, 0x3732, 0x3832, 0x3932,
    0x3033, 0x3133, 0x3233, 0x3333, 0x3433, 0x3533, 0x3633, 0x3733, 0x3833, 0x3933,
    0x3034, 0x3134, 0x3234, 0x3334, 0x3434, 0x3534, 0x3634, 0x3734, 0x3834, 0x3934,
    0x3035, 0x3135, 0x3235, 0x3335, 0x3435, 0x3535, 0x3635, 0x3735, 0x3835, 0x3935,
    0x3036, 0x3136, 0x3236, 0x3336, 0x3436, 0x3536, 0x3636, 0x3736, 0x3836, 0x3936,
    0x3037, 0x3137, 0x3237, 0x3337, 0x3437, 0x3537, 0x3637, 0x3737, 0x3837, 0x3937,
    0x3038, 0x3138, 0x3238, 0x3338, 0x3438, 0x3538, 0x3638, 0x3738, 0x3838, 0x3938,
    0x3039, 0x3139, 0x3239, 0x3339, 0x3439, 0x3539, 0x3639, 0x3739, 0x3839, 0x3939, };

char *itoa_vitaut_1(char *buf, uint32_t val)
{
    char *p = &buf[10];

    *p = '\0';

    while(val >= 100)
    {
        uint32_t const old = val;

        p -= 2;
        val /= 100;
        memcpy(p, &str100p[old - (val * 100)], sizeof(uint16_t));
    }

    p -= 2;
    memcpy(p, &str100p[val], sizeof(uint16_t));

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

优化 itoa 功能 的相关文章

  • EF Core Group By 翻译支持条件总和

    听说 EF Core 2 1 将支持翻译小组 我感到非常兴奋 我下载了预览版并开始测试它 但发现我在很多地方仍然没有得到翻译分组 在下面的代码片段中 对 TotalFlagCases 的查询将阻止翻译分组工作 无论如何 我可以重写这个以便我
  • 如何使用 C# 中的参数将用户重定向到 paypal

    如果我有像下面这样的简单表格 我可以用它来将用户重定向到 PayPal 以完成付款
  • “构建”构建我的项目,“构建解决方案”则不构建

    我刚刚开始使用VS2010 我有一个较大的解决方案 已从 VS2008 成功迁移 我已将一个名为 Test 的控制台应用程序项目添加到解决方案中 选择构建 gt 构建解决方案不编译新项目 选择构建 gt 构建测试确实构建了项目 在失败的情况
  • 用于检查类是否具有运算符/成员的 C++ 类型特征[重复]

    这个问题在这里已经有答案了 可能的重复 是否可以编写一个 C 模板来检查函数是否存在 https stackoverflow com questions 257288 is it possible to write a c template
  • 为什么当实例化新的游戏对象时,它没有向它们添加标签? [复制]

    这个问题在这里已经有答案了 using System Collections using System Collections Generic using UnityEngine public class Test MonoBehaviou
  • BitTorrent 追踪器宣布问题

    我花了一点业余时间编写 BitTorrent 客户端 主要是出于好奇 但部分是出于提高我的 C 技能的愿望 我一直在使用理论维基 http wiki theory org BitTorrentSpecification作为我的向导 我已经建
  • Clang 3.1 + libc++ 编译错误

    我已经构建并安装了 在前缀下 alt LLVM Clang trunk 2012 年 4 月 23 日 在 Ubuntu 12 04 上成功使用 GCC 4 6 然后使用此 Clang 构建的 libc 当我想使用它时我必须同时提供 lc
  • 显示UnityWebRequest的进度

    我正在尝试使用下载 assetbundle统一网络请求 https docs unity3d com ScriptReference Networking UnityWebRequest GetAssetBundle html并显示进度 根
  • 如何设计以 char* 指针作为类成员变量的类?

    首先我想介绍一下我的情况 我写了一些类 将 char 指针作为私有类成员 而且这个项目有 GUI 所以当单击按钮时 某些函数可能会执行多次 这些类是设计的单班在项目中 但是其中的某些函数可以执行多次 然后我发现我的项目存在内存泄漏 所以我想
  • 控件的命名约定[重复]

    这个问题在这里已经有答案了 Microsoft 在其网站上提供了命名指南 here http msdn microsoft com en us library xzf533w0 VS 71 aspx 我还有 框架设计指南 一书 我找不到有关
  • 如何查看网络连接状态是否发生变化?

    我正在编写一个应用程序 用于检查计算机是否连接到某个特定网络 并为我们的用户带来一些魔力 该应用程序将在后台运行并执行检查是否用户请求 托盘中的菜单 我还希望应用程序能够自动检查用户是否从有线更改为无线 或者断开连接并连接到新网络 并执行魔
  • 链接器错误:已定义

    我尝试在 Microsoft Visual Studio 2012 中编译我的 Visual C 项目 使用 MFC 但出现以下错误 error LNK2005 void cdecl operator new unsigned int 2
  • 如何使用 C# / .Net 将文件列表从 AWS S3 下载到我的设备?

    我希望下载存储在 S3 中的多个图像 但目前如果我只能下载一个就足够了 我有对象路径的信息 当我运行以下代码时 出现此错误 遇到错误 消息 读取对象时 访问被拒绝 我首先做一个亚马逊S3客户端基于我的密钥和访问配置的对象连接到服务器 然后创
  • 对现有视频添加水印

    我正在寻找一种用 C 在视频上加水印的方法 就像在上面写文字一样 图片或文字标签 我该怎么做 谢谢 您可以使用 Nreco 视频转换器 代码看起来像 NReco VideoConverter FFMpegConverter wrap new
  • 如何从两个不同的项目中获取文件夹的相对路径

    我有两个项目和一个共享库 用于从此文件夹加载图像 C MainProject Project1 Images 项目1的文件夹 C MainProject Project1 Files Bin x86 Debug 其中有project1 ex
  • 将控制台重定向到 .NET 程序中的字符串

    如何重定向写入控制台的任何内容以写入字符串 对于您自己的流程 Console SetOut http msdn microsoft com en us library system console setout aspx并将其重定向到构建在
  • 哪种 C 数据类型可以表示 40 位二进制数?

    我需要表示一个40位的二进制数 应该使用哪种 C 数据类型来处理这个问题 如果您使用的是 C99 或 C11 兼容编译器 则使用int least64 t以获得最大的兼容性 或者 如果您想要无符号类型 uint least64 t 这些都定
  • Windows 和 Linux 上的线程

    我在互联网上看到过在 Windows 上使用 C 制作多线程应用程序的教程 以及在 Linux 上执行相同操作的其他教程 但不能同时用于两者 是否存在即使在 Linux 或 Windows 上编译也能工作的函数 您需要使用一个包含两者的实现
  • 如何在文本框中插入图像

    有没有办法在文本框中插入图像 我正在开发一个聊天应用程序 我想用图标图像更改值 等 但我找不到如何在文本框中插入图像 Thanks 如果您使用 RichTextBox 进行聊天 请查看Paste http msdn microsoft co
  • 使用.NET技术录制屏幕视频[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 有没有一种方法可以使用 NET 技术来录制屏幕 无论是桌面还是窗口 我的目标是免费的 我喜欢小型 低

随机推荐

  • 使用 MVC 通配符证书在 Azure 上托管许多 SSL 站点

    以下应用程序当前在我尝试迁移到 Azure 的 Windows 2008 R2 服务器上运行 Part 1 首先 我有以下 ASP NET MVC 站点 它根据 DNS 名称的第一部分路由客户 https customer1 myAzure
  • 将数字的最后 n 位转换为零

    在Python中 将数字的最后一位数字替换为零并保持前三位数字不变的最佳方法是什么 例子 23456789 gt 23400000 112022 gt 112000 1111 gt 1110 111 gt 111 no conversion
  • Python Asyncio run_forever() 和任务

    我改编了这段代码 以便在异步 Python 中使用 Google Cloud PubSub https github com cloudfind google pubsub asyncio https github com cloudfin
  • 是否可以将 Black 作为 API 调用?

    说我想用black https black readthedocs io en stable index html作为 API 并执行以下操作 import black black format some python code 通过调用格
  • 在2.0中实现SFTP

    我想在 NET 2 0 中编写SFTP 客户端和服务器 那可能吗 请给我一些建议 看一眼SharpSSH http www tamirgal com blog page SharpSSH aspx 它具有开源 BSD 风格许可证 并支持 S
  • C - 如何正确使用 OpenSSL 的 BIO_write()

    我是 OpenSSL 新手 我知道 BIO write BIO b const void buf int len 需要在循环中调用 但我不完全确定我是否正确使用它 我写了一个这样的函数 int32 t SendPacket BIO cons
  • 如何以编程方式在 Google 文档中创建文档?

    The Google 文档列表 API 的文档 http code google com apis documents overview html 好像说可以创建本地文档并上传 有没有办法实际创建和编辑文档on通过 API 的 Google
  • `uwsgi_modifier1 30` 指令没有按照记录从 PATH_INFO 中删除 SCRIPT_NAME

    这是我的 nginx 虚拟主机配置 debian cat etc nginx sites enabled mybox server listen 8080 root www index index html index htm server
  • 如何集成 sympy 表达式和 Latex 格式

    您好 提前感谢您的帮助 我正在尝试将数学格式与 sympy 表达式混合在一起 我正在使用 jupyter 笔记本 我可以在单独的行上获得乳胶和漂亮的 sympy 打印 但似乎无法找到一种方法将它们组合成一行 下面的代码打印三行 第三行应该是
  • 当id包含点时,如何使用jquery通过ID选择html节点?

    如果我的 html 看起来像这样 td class controlCell td
  • Log4j 显示包名称

    现在对于我的 ConversionPattern 我有 log4j appender A1 layout ConversionPattern d yyyy MMM dd HH mm ss SSS 5p t F L m n 我想做的还包括包含
  • 如何防止Excel单元格更新?

    我有一个相当大的范围 10 000 行 10 列 我每天都会逐行填充它 我还有一个较小的范围 366 行 5 列 其中 对于每个单元格 我运行一个宏 该宏的作用与 DSUM 或 SUMIF 的作用几乎相同 但具有多个条件 问题是 在实现了这
  • 最大值和最小值的算法? (目标-C)

    这是我正在阅读的一本学习 Objective C 的书的一部分 下面定义了一个名为 MAX 的宏 它给出了两个的最大值 价值观 define MAX a b a gt b a b 然后书中有一些练习要求读者定义一个宏 MIN 找到两个值中的
  • 从 Google Chat POST 请求验证 JWT

    我有一个 NodeJS 机器人使用 HTTPs 端点连接到 Google Chat 我正在使用快递来接收请求 我需要验证所有请求是否都来自 Google 并且希望使用 Google 随请求发送的不记名令牌来执行此操作 我的问题是我正在努力寻
  • 找出最后获得焦点的控件

    我有一个带有几个文本框和一个按钮的 C Windows 窗体应用程序 我想找出具有焦点的文本框并用它做一些事情 我已经编写了以下代码 但它当然不起作用 因为按钮一旦按下就会获得焦点 private void button1 MouseDow
  • 在winforms设计器中,我可以关闭烦人的双击->事件处理程序创建背后的代码吗?

    大多数时候这很方便 但有时我想关闭它 有人知道怎么做吗 没有这个选择 解决方法很简单 只需键入 Ctrl Z 并单击 是 即可
  • 我可以使用什么 Linq 查询来按类别返回所有产品的计数?

    给定以下表结构 如何使用 Linq 查询返回类别名称列表以及该类别中的产品总数 Category ID Name Product ID IDCategory Name 我理想的回报是 Clothes 156 Electronics 2149
  • 处理多个切换

    我陷入了一个toggle 噩梦 终于寻求帮助 我想要的很简单 我有三个链接 a showcountries bronze a showcountries silver a showcountries gold 和三个盒子 countries
  • Android 底部菜单栏

    我想在我的应用程序底部实现一个菜单栏 就像我在 Facebook Google stumble 等许多主要应用程序中看到的那样 见下图 其关键方面是它覆盖在实际内容的顶部 当您向下滚动时 它会消失 但当您向上滚动时它会回来 由于很多应用程序
  • 优化 itoa 功能

    我正在考虑如何使用SSE指令实现整数 4字节 无符号 到字符串的转换 通常的例程是将数字相除并将其存储在局部变量中 然后反转字符串 本示例中缺少反转例程 char convert unsigned int num int base stat