Skip to content

new intrinsic: @isComptime(value: var) bool #2128

Description

@shawnl

I need this to optimize fmt.charToDigit:

pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u4) {
    // These are manual optimizations for the common cases.
    // They only require a single branch instruction.
    if (radix == 10) { <== I need to test @isComptime(radix)
        if (std.ascii.isDigit(c)) {
            return c - '0';
        } else {
            return error.InvalidCharacter;
        }
    } else (radix == 16) {
        const NOT: u8 = 0xff;
        const swtch = []u8{
        //    0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15
            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
              0,   1,   2,   3,   4,   5,   6,   7,   8,   9, NOT, NOT, NOT, NOT, NOT, NOT,

            NOT,  10,  11,  12,  13,  14,  15, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
            NOT,  10,  11,  12,  13,  14,  15, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,

            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,

            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
            NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        };
        const digit = swtch[c];
        if (digit == NOT) {
            return error.InvalidCharacter;
        }
        return u4(digit);
    }

    const value = switch (c) {
        '0'...'9' => c - '0',
        'A'...'Z' => c - 'A' + 10,
        'a'...'z' => c - 'a' + 10,
        else => return error.InvalidCharacter,
    };

    if (value >= radix) return error.InvalidCharacter;

    return value;
}

Activity

  1. added this to the 0.5.0 milestone on Mar 29, 2019
  2. added
    proposalThis issue suggests language modifications. If it also has the "accepted" label then it is planned.
    on Mar 29, 2019
  3. andrewrk commented on Mar 29, 2019

    @andrewrk
    Member

    what code did you use to test the performance of charToDigit?

  4. shawnl commented on Mar 29, 2019

    @shawnl
    ContributorAuthor

    https://gist.github.com/shawnl/41ecc272cf77e6e7490589bee630063f

    $ zig run ~/projects/shared/bench_charToDigit.zig  --release-fast
    optimized: 279.4MiB/sec, checksum: 962597485
    simple: 160.3MiB/sec, checksum: 962597485
    optimized: 222.8MiB/sec, checksum: 921599919
    simple: 149.8MiB/sec, checksum: 921599919
    

    nice, however some safety check is making the optimized hex code very slow. Is there a way to unconditionally optimize a block of code?:

    $ zig run ~/projects/shared/bench_charToDigit.zig 
    optimized: 39.4MiB/sec, checksum: 962597485
    simple: 36.0MiB/sec, checksum: 962597485
    optimized: 1.7MiB/sec, checksum: 921599919
    simple: 35.0MiB/sec, checksum: 921599919
    
  5. tiehuis commented on Mar 29, 2019

    @tiehuis
    Member

    This particular case you can actually fix by pulling the switch table out and making it global.

    For example:

    const std = @import("std");
    const Utf8ByteSequenceLengthError = error{Utf8InvalidStartByte};
    
    const NOT = 0xff;
    const swtch = []u8{
        //    0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,  15
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, NOT, NOT, NOT, NOT, NOT, NOT,
    
        NOT, 10, 11, 12, 13, 14, 15, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        NOT, 10, 11, 12, 13, 14, 15, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
    
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
    
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
        NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT, NOT,
    };
    
    pub fn charToDigitOptimized(c: u8, radix: u8) (error{InvalidCharacter}!u6) {
        // These are manual optimizations for the common cases.
        // They only require a single branch instruction.
    
    // ... rest of code
    $ zig run original.zig 
    optimized: 53.7MiB/sec, checksum: 962597485
    simple: 48.7MiB/sec, checksum: 962597485
    optimized: 2.0MiB/sec, checksum: 921599919
    simple: 46.8MiB/sec, checksum: 921599919
    
    $ zig run original.zig --release-fast
    optimized: 311.6MiB/sec, checksum: 962597485
    simple: 177.1MiB/sec, checksum: 962597485
    optimized: 275.6MiB/sec, checksum: 921599919
    simple: 168.7MiB/sec, checksum: 921599919
    
    $ zig run new.zig 
    optimized: 54.3MiB/sec, checksum: 962597485
    simple: 49.6MiB/sec, checksum: 962597485
    optimized: 61.3MiB/sec, checksum: 921599919
    simple: 47.7MiB/sec, checksum: 921599919
    
    $ zig run new.zig --release-fast
    optimized: 315.9MiB/sec, checksum: 962597485
    simple: 177.5MiB/sec, checksum: 962597485
    optimized: 280.3MiB/sec, checksum: 921599919
    simple: 166.7MiB/sec, checksum: 921599919
    

    It appeared that Zig wasn't eliding a memcpy otherwise in debug mode. Alternatively you can initialize it in a comptime block locally and it should work fine. Might be clearer if you did that and set the digit ranges selectively.

    As for whether explicitly specifying if the argument radix was comptime, that didn't help here. I did try initially a variation with the radix explicitly comptime, but saw no difference as the main issue was the hidden memcpy.

    Also, #868 is related a little to this issue.

  6. shawnl commented on Mar 29, 2019

    @shawnl
    ContributorAuthor
  7. shawnl commented on Mar 29, 2019

    @shawnl
    ContributorAuthor

    I think the optimization is find without checking for comprime, because having it not comptime is so unusual, however it would make it more correct.

  8. modified the milestones: 0.5.0, 0.6.0 on Apr 15, 2019
  9. added a commit that references this issue on Apr 17, 2019
  10. andrewrk commented on Oct 17, 2019

    @andrewrk
    Member

    Related: #868

    I don't think this is a good use case for this feature. It would be better to have the same code path for comptime and runtime.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    proposalThis issue suggests language modifications. If it also has the "accepted" label then it is planned.

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions