|
阅读:192回复:3
合理使用逻辑短路
这里主要说说类C语言中的逻辑与。
为了防止踩到空指针,你可以这样写: if (ptr && *ptr ... ) { } 如果前面的ptr是空指针,就不会再去访问ptr指向的内存。 不过,这种便利可能是有代价的, 当你不需要使用短路特性时,你也可以这样写: if (A_True & B_True) { } 做完按位与,判断是否为0,直觉上很贴汇编。 LLM认为后者的性能比逻辑与更好, 这里还缺一个实机测试环节,未来有空给补上。 |
|
|
沙发#
发布于:2026-05-14 11:15
有空了,遂Vibe Coding一个测试用的玩具,
我不是真的专家,但我觉得设计还算合理, 源码如下: #include <stdio.h> #include <stdlib.h> #include <time.h> #include <windows.h> static inline long long get_time_ns() { LARGE_INTEGER freq, count; QueryPerformanceFrequency(&freq); QueryPerformanceCounter(&count); return count.QuadPart * 1000000000LL / freq.QuadPart; } #define ITERATIONS 100000000LL // short-circuit evaluation void test_logical_and(void) { volatile int dummy = 0; for (long long i = 0; i < ITERATIONS; ++i) { int a = rand(); int b = rand(); if ((a % 2) && (b % 2)) dummy++; } printf("Logical AND - dummy count: %d\n", dummy); } // non-short-circuit evaluation void test_bitwise_and(void) { volatile int dummy = 0; for (long long i = 0; i < ITERATIONS; ++i) { int a = rand(); int b = rand(); if ((a % 2) & (b % 2)) dummy++; } printf("Bitwise AND - dummy count: %d\n", dummy); } int main() { srand((unsigned int)time(NULL)); long long start, end; double time_ms; // Benchmark Logical AND start = get_time_ns(); test_logical_and(); end = get_time_ns(); time_ms = (end - start) / 1000000.0; printf("Logical && elapsed: %.2f ms\n\n", time_ms); // Benchmark Bitwise AND start = get_time_ns(); test_bitwise_and(); end = get_time_ns(); time_ms = (end - start) / 1000000.0; printf("Bitwise & elapsed: %.2f ms\n", time_ms); return 0; } |
|
|
2楼#
发布于:2026-05-14 11:18
测试用的机器,情况如下:
OS: Win11 25H2 RAM: 16+16 DDR4 CPU: AMD Ryzen5 4600G @ 3.70GHz 使用MinGW提供的GCC 12编译, 开启O2优化,std=C11 后台没有大负载, 在1e8次循环后, 典型的打印结果: Logical AND - dummy count: 24999945 Logical && elapsed: 2731.92 ms Bitwise AND - dummy count: 24999990 Bitwise & elapsed: 2531.15 ms 哦豁,看起来这次是LLM说对了...... |
|
|
3楼#
发布于:2026-05-14 11:22
对应的汇编也放一下吧,
万一有绅士喜欢看呢? (迎合绅士的喜好,没采用等宽字体) .file "CTest.c" .text .p2align 4 .def printf; .scl 3; .type 32; .endef .seh_proc printf printf: pushq %rsi .seh_pushreg %rsi pushq %rbx .seh_pushreg %rbx subq $56, %rsp .seh_stackalloc 56 .seh_endprologue leaq 88(%rsp), %rsi movq %rcx, %rbx movq %rdx, 88(%rsp) movl $1, %ecx movq %r8, 96(%rsp) movq %r9, 104(%rsp) movq %rsi, 40(%rsp) call *__imp___acrt_iob_func(%rip) movq %rsi, %r8 movq %rbx, %rdx movq %rax, %rcx call __mingw_vfprintf addq $56, %rsp popq %rbx popq %rsi ret .seh_endproc .p2align 4 .def get_time_ns; .scl 3; .type 32; .endef .seh_proc get_time_ns get_time_ns: subq $56, %rsp .seh_stackalloc 56 .seh_endprologue leaq 32(%rsp), %rcx call *__imp_QueryPerformanceFrequency(%rip) leaq 40(%rsp), %rcx call *__imp_QueryPerformanceCounter(%rip) imulq $1000000000, 40(%rsp), %rax cqto idivq 32(%rsp) addq $56, %rsp ret .seh_endproc .section .rdata,"dr" .align 8 .LC0: .ascii "Logical AND - dummy count: %d\12\0" .text .p2align 4 .globl test_logical_and .def test_logical_and; .scl 2; .type 32; .endef .seh_proc test_logical_and test_logical_and: pushq %rsi .seh_pushreg %rsi pushq %rbx .seh_pushreg %rbx subq $56, %rsp .seh_stackalloc 56 .seh_endprologue movl $100000000, %esi movl $0, 44(%rsp) .p2align 4,,10 .p2align 3 .L6: call rand movl %eax, %ebx call rand andl $1, %ebx je .L5 testb $1, %al je .L5 movl 44(%rsp), %eax addl $1, %eax movl %eax, 44(%rsp) .L5: subq $1, %rsi jne .L6 movl 44(%rsp), %edx leaq .LC0(%rip), %rcx addq $56, %rsp popq %rbx popq %rsi jmp printf .seh_endproc .section .rdata,"dr" .align 8 .LC1: .ascii "Bitwise AND - dummy count: %d\12\0" .text .p2align 4 .globl test_bitwise_and .def test_bitwise_and; .scl 2; .type 32; .endef .seh_proc test_bitwise_and test_bitwise_and: pushq %rsi .seh_pushreg %rsi pushq %rbx .seh_pushreg %rbx subq $56, %rsp .seh_stackalloc 56 .seh_endprologue movl $100000000, %esi movl $0, 44(%rsp) .p2align 4,,10 .p2align 3 .L16: call rand movl %eax, %ebx call rand movl %ebx, %edx shrl $31, %edx addl %edx, %ebx andl $1, %ebx subl %edx, %ebx movl %eax, %edx shrl $31, %edx addl %edx, %eax andl $1, %eax subl %edx, %eax testl %eax, %ebx je .L15 movl 44(%rsp), %eax addl $1, %eax movl %eax, 44(%rsp) .L15: subq $1, %rsi jne .L16 movl 44(%rsp), %edx leaq .LC1(%rip), %rcx addq $56, %rsp popq %rbx popq %rsi jmp printf .seh_endproc .def __main; .scl 2; .type 32; .endef .section .rdata,"dr" .LC3: .ascii "Logical && elapsed: %.2f ms\12\12\0" .LC4: .ascii "Bitwise & elapsed: %.2f ms\12\0" .section .text.startup,"x" .p2align 4 .globl main .def main; .scl 2; .type 32; .endef .seh_proc main main: pushq %rbx .seh_pushreg %rbx subq $48, %rsp .seh_stackalloc 48 movaps %xmm6, 32(%rsp) .seh_savexmm %xmm6, 32 .seh_endprologue call __main xorl %ecx, %ecx call *__imp__time64(%rip) movl %eax, %ecx call srand call get_time_ns movq %rax, %rbx call test_logical_and call get_time_ns pxor %xmm1, %xmm1 movsd .LC2(%rip), %xmm6 leaq .LC3(%rip), %rcx subq %rbx, %rax cvtsi2sdq %rax, %xmm1 divsd %xmm6, %xmm1 movq %xmm1, %rdx call printf call get_time_ns movq %rax, %rbx call test_bitwise_and call get_time_ns pxor %xmm1, %xmm1 leaq .LC4(%rip), %rcx subq %rbx, %rax cvtsi2sdq %rax, %xmm1 divsd %xmm6, %xmm1 movq %xmm1, %rdx call printf nop movaps 32(%rsp), %xmm6 xorl %eax, %eax addq $48, %rsp popq %rbx ret .seh_endproc .section .rdata,"dr" .align 8 .LC2: .long 0 .long 1093567616 .ident "GCC: (x86_64-win32-seh-rev1, Built by MinGW-W64 project) 12.2.0" .def __mingw_vfprintf; .scl 2; .type 32; .endef .def rand; .scl 2; .type 32; .endef .def srand; .scl 2; .type 32; .endef |
|