分析 Cortex-M7 (stm32f7) 上的 memcpy 性能

2024-04-29

简洁版本:从 GNU ARM 工具链中提取的 memcpy 的性能指标在 ARM Cortex-M7 上对于不同的副本大小似乎差异很大,即使复制数据的代码始终保持不变。这可能是什么原因造成的?

长版:

我是使用 GNU Arm 工具链 11.2 开发 stm32f765 微控制器的团队的一员,链接newlib-nano将 stdlib 实现到我们的代码中。

最近,memcpy性能成为我们项目中的瓶颈,我们发现从 newlib-nano 拉入代码中的 memcpy 实现是一个简单的字节复制,事后看来,考虑到 newlib-nano 库,这应该不足为奇代码大小优化(编译为-Os).

查看源代码cygwin-newlib,我成功地找到了与 ARMv7m 的 Nano 库一起编译和打包的确切 memcpy 实现:

    void *
__inhibit_loop_to_libcall
memcpy (void *__restrict dst0,
    const void *__restrict src0,
    size_t len0)
{
#if defined(PREFER_SIZE_OVER_SPEED) || defined(__OPTIMIZE_SIZE__)
  char *dst = (char *) dst0;
  char *src = (char *) src0;

  void *save = dst0;

  while (len0--)
    {
      *dst++ = *src++;
    }

  return save;
#else
(...)
#endif

我们决定用我们自己的 memcpy 实现替换代码中的 newlib-nano memcpy 实现,同时出于其他原因坚持使用 newlib-nano。在此过程中,我们决定获取一些性能指标来将新实现与旧实现进行比较。

然而,理解所获得的指标对我来说是一个挑战。

Measurement results: Perfornace metrics obtained from profiling different memcpy implementations on ARM Cortex-M7

表中所有结果均为循环计数,通过读取获得DWT-CYCCNT值(有关实际测量设置的更多信息将在下面给出)。

表中比较了 3 种不同的 memcpy 实现。第一个是从 newlib-nano 库链接的默认库,如标签所示memcpy_nano。第二个和第三个是C语言中最幼稚、最愚蠢的数据复制实现,一种复制数据的实现每字节一个字节,以及另一个执行此操作的每个字:

memcpy_naive_bytewise(void *restrict dest, void *restrict src, size_t size)
{
    uint8_t *restrict u8_src = src,
            *restrict u8_dest = dest;

    for (size_t idx = 0; idx < size; idx++) {
        *u8_dest++ = *u8_src++;
    }

    return dest;
}
void *
memcpy_naive_wordwise(void *restrict dest, void *restrict src, size_t size)
{
    uintptr_t upt_dest = (uintptr_t)dest;

    uint8_t *restrict u8_dest = dest,
            *restrict u8_src  = src;

    while (upt_dest++ & !ALIGN_MASK) {
        *u8_dest++ = *u8_src++;
        size--;
    }

    word *restrict word_dest = (void *)u8_dest,
             *restrict word_src  = (void *)u8_src;

    while (size >= sizeof *word_dest) {
        *word_dest++ = *word_src++;
        size -= sizeof *word_dest;
    }

    u8_dest = (void *)word_dest;
    u8_src  = (void *)word_src;

    while (size--) {
        *u8_dest++ = *u8_src++;
    }

    return dest;
}

我一生都无法弄清楚为什么memcpy_nano最初类似于朴素的逐字复制实现(直到 256 字节大小的副本),只是从 256 字节大小的副本及以上开始类似于朴素的逐字节复制实现的性能。

我已经进行了三次检查,确实,对于测量的每个副本大小,预期的 memcpy 实现都与我的代码相关联。例如,这是为测量 16 字节大小的 memcpy 与 256 字节大小的副本(首先出现差异)的性能而获得的 memcpy 反汇编代码:

  • memcpy 定义链接为16字节大小 copy (newlib-nano memcpy):
08007a74 <memcpy>:
 8007a74:   440a        add r2, r1
 8007a76:   4291        cmp r1, r2
 8007a78:   f100 33ff   add.w   r3, r0, #4294967295
 8007a7c:   d100        bne.n   8007a80 <memcpy+0xc>
 8007a7e:   4770        bx  lr
 8007a80:   b510        push    {r4, lr}
 8007a82:   f811 4b01   ldrb.w  r4, [r1], #1
 8007a86:   f803 4f01   strb.w  r4, [r3, #1]!
 8007a8a:   4291        cmp r1, r2
 8007a8c:   d1f9        bne.n   8007a82 <memcpy+0xe>
 8007a8e:   bd10        pop {r4, pc}
  • memcpy 定义链接为256 字节大小 copy (newlib-nano memcpy):
08007a88 <memcpy>:
 8007a88:   440a        add r2, r1
 8007a8a:   4291        cmp r1, r2
 8007a8c:   f100 33ff   add.w   r3, r0, #4294967295
 8007a90:   d100        bne.n   8007a94 <memcpy+0xc>
 8007a92:   4770        bx  lr
 8007a94:   b510        push    {r4, lr}
 8007a96:   f811 4b01   ldrb.w  r4, [r1], #1
 8007a9a:   f803 4f01   strb.w  r4, [r3, #1]!
 8007a9e:   4291        cmp r1, r2
 8007aa0:   d1f9        bne.n   8007a96 <memcpy+0xe>
 8007aa2:   bd10        pop {r4, pc}

可以看到,除了函数的相对地址在哪里不同之外,实际的复制逻辑并没有变化。

测量设置:

  • 确保内存和指令缓存已禁用、IRQ 已禁用、DWT 已启用:
SCB->CSSELR = (0UL << 1) | 0UL;         // Level 1 data cache
    __DSB();

    SCB->CCR &= ~(uint32_t)SCB_CCR_DC_Msk;  // disable D-Cache
    __DSB();
    __ISB();

    SCB_DisableICache();

    if(DWT->CTRL & DWT_CTRL_NOCYCCNT_Msk)
    {
        //panic
        while(1);
    }

    /* Enable DWT unit */
    CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
    __DSB();

    /* Unlock DWT registers */
    DWT->LAR = 0xC5ACCE55;
    __DSB();

    /* Reset CYCCNT */
    DWT->CYCCNT = 0;

    /* Enable CYCCNT */
    DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;

    __disable_irq();

    __DSB();
    __ISB();
  • 将测试中的一个 memcpy 版本链接到代码,以及一个字节大小的步骤。编译代码-O0。然后测量执行时间(注意:au8_dst 和 au8_src 的地址是always对齐):
uint8_t volatile au8_dst[MAX_BYTE_SIZE];
uint8_t volatile au8_src[MAX_BYTE_SIZE];

    __DSB();
    __ISB();

    u32_cyccntStart = DWT->CYCCNT;

    __DSB();
    __ISB();

    memcpy(au8_dst, au8_src, u32_size);

    __DSB();
    __ISB();

    u32_cyccntEnd = DWT->CYCCNT;

    __DSB();
    __ISB();

    *u32_cyccnt = u32_cyccntEnd - u32_cyccntStart;
  • 对字节大小和 memcpy 版本的每个组合重复此过程

主要问题newlib-nano memcpy 的执行时间如何能够遵循原始逐字复制实现的执行时间,最大字节大小为 256 字节,之后它的执行类似于逐字节复制的原始实现?请记住,提取到代码中的 newlib-nano memcpy 的定义对于每个字节大小测量都是相同的,如上面提供的反汇编所示。我的测量设置是否存在一些我未能识别的明显缺陷?

对此的任何想法都将受到高度赞赏!


正如评论中提到的,您可能需要在性能测试中考虑您的一致性。可能出现的情况是,一种 memcpy 解决方案与另一种解决方案可能会命中我所说的这些提取线。

stm32 cortex-m7 部件。

测试中的代码:

/* r0 count */
/* r1 timer address */
.thumb_func
.globl TEST
TEST:
    push {r4,r5}
    ldr r4,[r1]

loop:
    sub r0,#1
    bne loop

    ldr r5,[r1]
    sub r0,r4,r5
    pop {r4,r5}
    bx lr

原始对齐方式

08000100 <TEST>:
 8000100:   b430        push    {r4, r5}
 8000102:   680c        ldr r4, [r1, #0]

08000104 <loop>:
 8000104:   3801        subs    r0, #1
 8000106:   d1fd        bne.n   8000104 <loop>
 8000108:   680d        ldr r5, [r1, #0]
 800010a:   1b60        subs    r0, r4, r5
 800010c:   bc30        pop {r4, r5}
 800010e:   4770        bx  lr

使用了 systick 计时器,没有理由使用调试计时器,它不会增加任何价值。

ra=TEST(0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=TEST(0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=TEST(0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=TEST(0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);

第一次运行

00001029 
00001006 
00001006 
00001006 

这是一个 stm32,因此有一个无法禁用的闪存缓存,因此您可以在第一次运行时看到上面的内容。

循环是这样对齐的

 8000104:   3801        subs    r0, #1
 8000106:   d1fd        bne.n   8000104 <loop>

添加 nop 将循环移动半个字

08000100 <TEST>:
 8000100:   46c0        nop         ; (mov r8, r8)
 8000102:   b430        push    {r4, r5}
 8000104:   680c        ldr r4, [r1, #0]

08000106 <loop>:
 8000106:   3801        subs    r0, #1
 8000108:   d1fd        bne.n   8000106 <loop>
 800010a:   680d        ldr r5, [r1, #0]
 800010c:   1b60        subs    r0, r4, r5
 800010e:   bc30        pop {r4, r5}
 8000110:   4770        bx  lr

整个测试从定时器读取到定时器读取都是相同的机器代码。

但性能却截然不同

00002013 
00002003 
00002003 
00002003 

执行时间是原来的两倍。

如果如文档所述,提取是 64 位,则每次提取 4 条指令。

如果我在每次测试中添加一个 nop

00001028 
00001006 
00001006 
00001006 

00001027 
00001006 
00001006 
00001006 

00001026 
00001006 
00001006 
00001006 

我又得到了三个返回 0x1000 的结果,然后......

08000100 <TEST>:
 8000100:   46c0        nop         ; (mov r8, r8)
 8000102:   46c0        nop         ; (mov r8, r8)
 8000104:   46c0        nop         ; (mov r8, r8)
 8000106:   46c0        nop         ; (mov r8, r8)
 8000108:   46c0        nop         ; (mov r8, r8)
 800010a:   b430        push    {r4, r5}
 800010c:   680c        ldr r4, [r1, #0]

0800010e <loop>:
 800010e:   3801        subs    r0, #1
 8000110:   d1fd        bne.n   800010e <loop>
 8000112:   680d        ldr r5, [r1, #0]
 8000114:   1b60        subs    r0, r4, r5
 8000116:   bc30        pop {r4, r5}
 8000118:   4770        bx  lr
 
00002010 
00002001 
00002001 
00002001 

您可以在 sram 中运行它以避免缓存,并执行其他操作,但我希望您在达到为循环添加额外获取的边界时会看到相同的效果。显然,这是最好的情况,整个循环一次获取,有时两次。使循环更长,它变成 N,然后 N+1 以不太严重的比率进行取指。

我还假设这里的 systick 是手臂时钟除以二,这对于这种性能测试来说非常适合。

因此,很可能由于两个不同函数的对齐,一个函数可能会受到性能影响,而另一个函数则不会受到额外的获取影响。

我倾向于将测试中的代码转换为 asm,将其放在二进制文件前面附近的引导程序中,这样我添加或删除的任何其他代码都不会影响对齐。我还可以将计时器包裹在它周围并以非常受控的方式循环。在定时区域之外添加 nop,以移动循环的对齐方式。如果被测代码中有多个循环,则可以在被测代码中间添加 nops 来控制每个循环的对齐方式。

您还需要调整数据的对齐方式,我不记得 cortex-ms 如何处理未对齐的访问,如果它们支持它,我认为它们会带来性能损失。

我针对 MCU 演示了与上述类似的内容,这也会影响到您。由于 sram(普通 sram 内存或就此而言的高速缓存)不是以字节形式组织的,因此它至少有 32 位宽(如果是 ecc/奇偶校验则更宽)。因此,单字节写入需要读取-修改-写入,半字相同,但对齐字写入不需要读取。通常,这被隐藏在噪音中,因为您没有进行足够的连续写入来从 sram 控制逻辑获得反压。但至少有一个 MCU 确实提到您可以/将会看到这种性能,并且我在某个时候在 SO 上发布了这一点。您还应该在未对齐的字写入中看到这一点,现在您需要两次读取-修改-写入。

显然,四条存储指令比一条字指令花费更多的时间。

我会这么做为什么不呢

/* r0 address */
/* r1 count */
/* r2 timer address */
.thumb_func
.globl swtest
swtest:
    push {r4,r5}
    ldr r4,[r2]
    
swloop:
    str r3,[r0]
    str r3,[r0]
    str r3,[r0]
    str r3,[r0]

    str r3,[r0]
    str r3,[r0]
    str r3,[r0]
    str r3,[r0]

    str r3,[r0]
    str r3,[r0]
    str r3,[r0]
    str r3,[r0]

    str r3,[r0]
    str r3,[r0]
    str r3,[r0]
    str r3,[r0]
    sub r1,#1
    bne swloop
    
    ldr r5,[r2]
    sub r0,r4,r5
    pop {r4,r5}
    bx lr


ra=swtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=swtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=swtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=swtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);

ra=swtest(0x20002002,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=swtest(0x20002002,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=swtest(0x20002002,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=swtest(0x20002002,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);

00012012 
0001200A 
0001200A 
0001200A 
0002FFFD 
0002FFFD 
0002FFFD 
0002FFFD 

未对齐的执行时间要长两倍多。

不幸的是,您无法控制通用 memcpy 的地址,因此地址可能是 0x1000 和 0x2001,而且速度会很慢。但是,如果这里的练习是因为您有需要经常复制的代码(并且芯片中没有 DMA 机制可以使速度更快,请记住 DMA 不是免费的,有时它只是一种懒惰的方法,使用较少的代码但运行速度较慢,了解架构)但如果您可以控制它是字对齐地址和整个字数至少要复制的数据量,那么请制作您自己的副本,而不是不将其称为memcpy。然后用手调一下。


编辑,从 SRAM 运行

for(rd=0;rd<8;rd++)
{
    rb=0x20002000;
    for(rc=0;rc<rd;rc++)
    {
        PUT32(rb,0xb430); rb+=2; //46c0         nop         ; (mov r8, r8)
    }

    PUT32(rb,0xb430); rb+=2; // 800010a:    b430        push    {r4, r5}
    PUT32(rb,0x680c); rb+=2; // 800010c:    680c        ldr r4, [r1, #0]
                             //0800010e <loop>:
    PUT32(rb,0x3801); rb+=2; // 800010e:    3801        subs    r0, #1
    PUT32(rb,0xd1fd); rb+=2; // 8000110:    d1fd        bne.n   800010e <loop>
    PUT32(rb,0x680d); rb+=2; // 8000112:    680d        ldr r5, [r1, #0]
    PUT32(rb,0x1b60); rb+=2; // 8000114:    1b60        subs    r0, r4, r5
    PUT32(rb,0xbc30); rb+=2; // 8000116:    bc30        pop {r4, r5}
    PUT32(rb,0x4770); rb+=2; // 8000118:    4770        bx  lr
    PUT32(rb,0x46c0); rb+=2;
    PUT32(rb,0x46c0); rb+=2;
    PUT32(rb,0x46c0); rb+=2;
    PUT32(rb,0x46c0); rb+=2;
    PUT32(rb,0x46c0); rb+=2;
    PUT32(rb,0x46c0); rb+=2;

    ra=HOP(0x1000,STK_CVR,0x20002001);  hexstrings(rd); hexstring(ra%0x00FFFFFF);
    ra=HOP(0x1000,STK_CVR,0x20002001);  hexstrings(rd); hexstring(ra%0x00FFFFFF);
    ra=HOP(0x1000,STK_CVR,0x20002001);  hexstrings(rd); hexstring(ra%0x00FFFFFF);
    ra=HOP(0x1000,STK_CVR,0x20002001);  hexstrings(rd); hexstring(ra%0x00FFFFFF);

}


00000000 00001011 
00000000 00001006 
00000000 00001006 
00000000 00001006 
00000001 00002010 
00000001 00002003 
00000001 00002003 
00000001 00002003 
00000002 00001014 
00000002 00001006 
00000002 00001006 
00000002 00001006 
00000003 00001014 
00000003 00001006 
00000003 00001006 
00000003 00001006 
00000004 00001014 
00000004 00001006 
00000004 00001006 
00000004 00001006 
00000005 00002010 
00000005 00002001 
00000005 00002002 
00000005 00002002 
00000006 00001012 
00000006 00001006 
00000006 00001006 
00000006 00001006 
00000007 00001014 
00000007 00001006 
00000007 00001006 
00000007 00001006 

现在仍然看到类似缓存的效果。我确实看到我的 CCR 是 0x00040200 并且我无法禁用它,我相信 m7 说你不能。

好的,正在使用 BTAC,但在 ACTLR 中设置位 13 将其更改为静态分支预测。所以现在时间实际上更有意义了,来自 sram:

00000000 00004003 
00000000 00004003 
00000000 00004003 
00000000 00004003 
00000001 00005002 
00000001 00005002 
00000001 00005002 
00000001 00005002 
00000002 00004003 
00000002 00004003 
00000002 00004003 
00000002 00004003 
00000003 00004003 
00000003 00004003 
00000003 00004003 
00000003 00004003 
00000004 00004003 
00000004 00004003 
00000004 00004003 
00000004 00004003 
00000005 00005002 
00000005 00005002 
00000005 00005002 
00000005 00005002 
00000006 00004003 
00000006 00004003 
00000006 00004003 
00000006 00004003 
00000007 00004003 
00000007 00004003 
00000007 00004003 
00000007 00004003 

我们确实看到了额外的获取行,但每次运行都与 sram 一致。

即使我知道 st 有缓存功能,Flash 也没有显示出从一个测试到另一个测试的任何变化。

00010FFC 
00010FFC 
00010FFC 
00010FFC 

相对于从 sram 运行而言,闪存的这种性能也感觉不错,闪存速度很慢,您对此无能为力,因此上面的数字确实看起来很奇怪。这说明了在性能测试中您可能会陷入多少陷阱,以及为什么所有基准测试都是b......t。

由于我对这个答案非常感兴趣,还要注意,假设 sram 是 32 位宽,则预计未对齐读取也会对未对齐读取造成性能影响,与一个周期相比,读取未对齐需要两个 sram 总线周期对于对齐,如果你击打得足够用力,那应该会产生反压。

禁用 BTAC 时

ra=swtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=swtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=swtest(0x20002002,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=swtest(0x20002002,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);

ra=lwtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=lwtest(0x20002000,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=lwtest(0x20002002,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);
ra=lwtest(0x20002002,0x1000,STK_CVR);  hexstring(ra%0x00FFFFFF);

store word aligned
00019FFE 
00019FFE 
store word unaligned
00030007 
00030007 
load word aligned
00020001 
00020001 
load word unaligned
0002A00C 
0002A00C 

因此,如果您的 memcpy 是从 0x1000 到 0x2002 或从 0x1001 到 0x2002,即使您预先对齐然后进行基于字的复制,性能仍然会受到影响。这就是为什么我提到你需要尝试不同的对齐方式。

关于你的一个问题,我记得几年前的全尺寸arm memcpy,我认为在newlib中他们有一些性能步骤,例如,如果要复制的数量小于x,他们只会执行一个字节循环,完成。否则,如果其中一个从 0x1001 开始,那么他们至少会尝试对齐其中一个,然后他们会执行一个字节、一个半字,然后是一堆字或多个字,然后根据长度在末尾添加一个额外的半字或字节来完成。但这只有在两个指针以相同方式对齐或未对齐的情况下才有效。

从你的桌子上看,我觉得你没有考虑到所有这些因素。你落入基准测试是b......t,一个基准测试代表一个源代码,即使该核心/芯片/系统可以在不同数量的时钟中运行该代码,有时严格地由于C编译器和连接器,没有其他因素。

然后再次

beg=get_timer();
for(i = 0;i<1000;i++)
{
  memcpy(a,b);
}
end=get_timer();

放大您的测量误差。单独调用 memcpy 的 for 循环也受到获取和分支预测的影响。我希望你不要这样测试。

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

分析 Cortex-M7 (stm32f7) 上的 memcpy 性能 的相关文章

随机推荐

  • 将多个前缀行过滤器设置为扫描仪 hbase java

    我想创建一台扫描仪 它可以为我提供带有 2 个前缀过滤器的结果例如 我想要其键以字符串 x 开头或以字符串 y 开头的所有行 目前我知道只能使用一个前缀 方法如下 scan setRowPrefixFilter prefixFiltet 在
  • 通过 URL 中的 ID 进行 RestKit 关系映射

    假设我有一个 APIusers 1 items返回一个列表items为了userID 为 1 假设API响应如下 items id 1 description Some item 请注意 响应不包含user id用于关系映射 RestKit
  • 提高我的脚本性能 Google Sheets 脚本

    我创建了一个函数 每当我运行 AppendRow 脚本时 AY 列中没有点 的每一行 包含我想要的每个信息 列的数组从该表将被转移到我的主表 其中有13k rows atm 通常 每天大约有 20 40 行被粘贴到第一个工作表中 此脚本会自
  • mcdropdown 的替代方案

    我正在构建一个自动完成下拉列表 用户可以从多个级别的类别中进行选择 在该网站的先前版本中 我们使用http www givainc com labs mcdropdown jquery plugin htm http www givainc
  • 如何减少 Ionic Cordova 项目启动持续时间?

    我做了一个离子科尔多瓦项目 但发布到Android手机后 根据手机类型 我们的程序持续时间约为 10 20 秒 当我搜索这个问题时 人们说这是因为 启动画面持续时间 离子启动画面未加载 https stackoverflow com que
  • 如何使用p:ajax按顺序更新多个组件

    我正在尝试呈现以下 JSF 页面
  • HoloEverywhere 的正确使用方法是什么

    我正在尝试使用开发分支HoloEverywhere 库添加到我的应用程序中 我知道这仍处于开发阶段 但演示似乎工作正常 所以我尝试一下 I put android theme style Holo Theme Sherlock Light
  • Java Thread.sleep 最短时间[重复]

    这个问题在这里已经有答案了 The TimeUnit sleep 长超时 http docs oracle com javase 8 docs api java util concurrent TimeUnit html sleep lon
  • 简化 Django 中的表单提交

    我在 Django 中有一个表单 用户可以在其中以单个表单提交文件 图像 文本 如下所示
  • git Cherry-pick 和 git show 有什么区别?补丁-p1?

    我遇到了一种情况git cherry pick X会产生一些冲突 但也会创建额外的插入 当使用git diff 然后我重新跑了git show X gt my patch 然后做了patch p1 lt my patch在我的树上 我得到了
  • 如何使用 QWebElement 设置 input(type="file") 的值?

    我正在尝试将照片上传到vk com https vk com using QtWebKit https qt project org doc qt 4 8 qtwebkit html模块 我面临的问题是无法正确填写input type fi
  • 在 SQL Server 中以编程方式创建数据库

    如何以编程方式创建数据库以及执行此操作所需的最少信息是什么 Please没有 SQL Server 管理对象 API 建议 您可以使用SQL Server 管理对象 API http msdn microsoft com en us lib
  • jQuery UI:DatePicker,仅选择今天到过去的日期

    我在 jQuery UI 核心中使用 datePicker 我需要一个只能选择从过去一直到今天的日期的日期选择器 是否有捷径可寻 请注意 我使用的是 UI 核心 而不是 DatePicker 插件 我的 jQuery 调用 function
  • Java线程的等待和通知方法

    我正在学习 OCJP 现在我在 线程 章节 我有一些关于等待和通知方法的问题 我想我明白这里发生了什么 但我只是想确保我走在正确的道路上 我编写了这段代码作为示例 package threads public class Main stat
  • SQL Server 2008:在没有任何锁的情况下出现死锁

    我目前正在 SQL Server 2008 数据库上进行一些实验 更具体地说 我有一个 JDBC 应用程序 它使用数百个并发线程来执行数千个任务 每个任务都在数据库上运行以下查询 UPDATE from Table A where rowI
  • MVC3 BeginForm 不渲染
    标签

    我的视图存在问题 未呈现开始和结束 FORM 标签 下面是我的控制器的代码 HttpGet Authorize public ActionResult Edit long id Position position positionRepos
  • C++中while(x--)是什么意思

    我刚刚开始竞争性编程 并一直使用如下循环来定义大多数练习问题中的测试用例数量 for int i 1 i lt t i 然而 我见过人们使用 while 循环 它只有条件 t 运行起来也完全没问题 有人可以向我解释这种情况实际上是如何运作的
  • 无法解析 Android 资源字符串

    我正在学习 Android 我遇到了一个我认为很奇怪的问题 在 res values strings xml 我有
  • mariadb: jdbc: setTimestamp 截断毫秒

    在我看来 如果我使用准备好的语句将它们插入到我的 mariadb 中 毫秒就会被截断 谷歌搜索并不成功 我发现了很多类似的问题 这些问题要么已解决 要么不适用 但很难相信我是唯一一个遇到这个问题的人 所以我想在向 mariadb 提交错误之
  • 分析 Cortex-M7 (stm32f7) 上的 memcpy 性能

    简洁版本 从 GNU ARM 工具链中提取的 memcpy 的性能指标在 ARM Cortex M7 上对于不同的副本大小似乎差异很大 即使复制数据的代码始终保持不变 这可能是什么原因造成的 长版 我是使用 GNU Arm 工具链 11 2