Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speed up BTCHexFromData by 5.5x (-85%) #114

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

wjmelements
Copy link

@wjmelements wjmelements commented Jul 14, 2018

I wrote an optimized version of BTCHexFromData. The speedup with -O3 is approximately 85% (or 5.5x) compared to the current implementation.

The speedup comes from specialized printing and reduced memory allocation.

Here is the program I used to time the change:

@import Foundation;
#import <mach/mach_time.h>

static char hexValues[16] = {
    '0',
    '1',
    '2',
    '3',
    '4',
    '5',
    '6',
    '7',
    '8',
    '9',
    'a',
    'b',
    'c',
    'd',
    'e',
    'f',
};

NSString *toHex(NSData *data) {
    size_t inLength = data.length;
    size_t length = inLength * 2;
    uint8_t *outString = malloc(length);
    const unsigned char *bytes = data.bytes;
    for (uint32_t i = 0; i < inLength; i++) {
        unsigned char byte = bytes[i];
        outString[i*2] = hexValues[(byte & 0xF0) >> 4];
        outString[i*2+1] = hexValues[(byte & 0x0F)];
    }
    return [[NSString alloc]
        initWithBytesNoCopy:outString
        length:length
        encoding:NSUTF8StringEncoding
        freeWhenDone:YES
    ];
}

NSString* BTCHexFromDataWithFormat(NSData* data, const char* format) {
    if (!data) return nil;
    
    NSUInteger length = data.length;
    if (length == 0) return @"";
    
    NSMutableData* resultdata = [NSMutableData dataWithLength:length * 2];
    char *dest = resultdata.mutableBytes;
    unsigned const char *src = data.bytes;
    for (int i = 0; i < length; ++i) {
        sprintf(dest + i*2, format, (unsigned int)(src[i]));
    }
    return [[NSString alloc] initWithData:resultdata encoding:NSASCIIStringEncoding];
}

NSString* BTCHexFromData(NSData* data) {
    return BTCHexFromDataWithFormat(data, "%02x");
}


NSMutableArray<NSData *> *testVector;
void initVector() {
    testVector = [NSMutableArray array];
    for (int i = 0; i < 400000; i++) {
        void *buf = malloc(32);
        arc4random_buf(buf, 32);
        [testVector
            addObject:[NSData
                dataWithBytesNoCopy:buf
                length:32
                freeWhenDone:YES
            ]
        ];
    }
}
NSMutableArray<NSString *> *outVector;
void timeMethod(NSString *(method)(NSData *)) {
    outVector = [NSMutableArray array];
    for (NSData *data in testVector) {
        [outVector addObject:method(data)];
    }
}

int main() {
    @autoreleasepool {
        uint64_t start = mach_absolute_time();
        initVector();
        uint64_t setup = mach_absolute_time();
        timeMethod(BTCHexFromData);
        uint64_t btcHex = mach_absolute_time();
        timeMethod(toHex);
        uint64_t hex = mach_absolute_time();
        NSLog(@"setup : %llu", setup - start);
        NSLog(@"btcHex: %llu", btcHex - setup);
        NSLog(@"Hex   : %llu", hex - btcHex);
        return 0;
    }
}

Here is the output:

[email protected]:~/projects/CoreBitcoin(master)*$ clang -fmodules timer.m 
[email protected]:~/projects/CoreBitcoin(master)*$ ./a.out 
2018-07-13 18:53:14.825 a.out[71380:1588521] setup : 174539326
2018-07-13 18:53:14.826 a.out[71380:1588521] btcHex: 1138551621
2018-07-13 18:53:14.826 a.out[71380:1588521] Hex   : 217758575
[email protected]:~/projects/CoreBitcoin(master)*$ clang -fmodules -O3 timer.m
[email protected]:~/projects/CoreBitcoin(master)*$ ./a.out 
2018-07-13 18:53:58.248 a.out[71425:1589886] setup : 172171645
2018-07-13 18:53:58.248 a.out[71425:1589886] btcHex: 1148045936
2018-07-13 18:53:58.248 a.out[71425:1589886] Hex   : 172938196

Reviewer @oleganza

@wjmelements
Copy link
Author

wjmelements commented Jul 14, 2018

The latest commits improve the non--O3 speed slightly more. New timer.m:

@import Foundation;
#import <mach/mach_time.h>

static const char hexValues[16] = {
    '0',
    '1',
    '2',
    '3',
    '4',
    '5',
    '6',
    '7',
    '8',
    '9',
    'a',
    'b',
    'c',
    'd',
    'e',
    'f',
};

NSString *toHex(NSData *data) {
    size_t inLength = data.length;
    size_t length = inLength * 2;
    uint8_t *outString = malloc(length);
    const unsigned char *bytes = data.bytes;
    for (uint32_t i = 0; i < inLength; i++) {
        unsigned char byte = bytes[i];
        outString[i*2] = hexValues[byte >> 4];
        outString[i*2+1] = hexValues[byte & 0x0F];
    }
    return [[NSString alloc]
        initWithBytesNoCopy:outString
        length:length
        encoding:NSASCIIStringEncoding
        freeWhenDone:YES
    ];
}

NSString* BTCHexFromDataWithFormat(NSData* data, const char* format) {
    if (!data) return nil;
    
    NSUInteger length = data.length;
    if (length == 0) return @"";
    
    NSMutableData* resultdata = [NSMutableData dataWithLength:length * 2];
    char *dest = resultdata.mutableBytes;
    unsigned const char *src = data.bytes;
    for (int i = 0; i < length; ++i) {
        sprintf(dest + i*2, format, (unsigned int)(src[i]));
    }
    return [[NSString alloc] initWithData:resultdata encoding:NSASCIIStringEncoding];
}

NSString* BTCHexFromData(NSData* data) {
    return BTCHexFromDataWithFormat(data, "%02x");
}


NSMutableArray<NSData *> *testVector;
void initVector() {
    testVector = [NSMutableArray array];
    for (int i = 0; i < 400000; i++) {
        void *buf = malloc(32);
        arc4random_buf(buf, 32);
        [testVector
            addObject:[NSData
                dataWithBytesNoCopy:buf
                length:32
                freeWhenDone:YES
            ]
        ];
    }
}
NSMutableArray<NSString *> *outVector;
void timeMethod(NSString *(method)(NSData *)) {
    outVector = [NSMutableArray array];
    for (NSData *data in testVector) {
        [outVector addObject:method(data)];
    }
}

int main() {
    @autoreleasepool {
        uint64_t start = mach_absolute_time();
        initVector();
        uint64_t setup = mach_absolute_time();
        timeMethod(BTCHexFromData);
        uint64_t btcHex = mach_absolute_time();
        timeMethod(toHex);
        uint64_t hex = mach_absolute_time();
        NSLog(@"setup : %llu", setup - start);
        NSLog(@"btcHex: %llu", btcHex - setup);
        NSLog(@"Hex   : %llu", hex - btcHex);
        return 0;
    }
}

output:

[email protected]:~/projects/CoreBitcoin(speedy-btcdata)*$ clang -fmodules timer.m
[email protected]:~/projects/CoreBitcoin(speedy-btcdata)*$ ./a.out 
2018-07-13 19:25:00.986 a.out[78612:1648181] setup : 170093611
2018-07-13 19:25:00.986 a.out[78612:1648181] btcHex: 1145701422
2018-07-13 19:25:00.986 a.out[78612:1648181] Hex   : 210011202
[email protected]:~/projects/CoreBitcoin(speedy-btcdata)*$ clang -fmodules -O3 timer.m
[email protected]:~/projects/CoreBitcoin(speedy-btcdata)*$ ./a.out 
2018-07-13 19:25:16.981 a.out[78656:1648634] setup : 171016593
2018-07-13 19:25:16.981 a.out[78656:1648634] btcHex: 1117013679
2018-07-13 19:25:16.981 a.out[78656:1648634] Hex   : 169127477

@wjmelements
Copy link
Author

@oleganza ptal

@wjmelements wjmelements changed the title Speed up BTCHexFromData by 85% Speed up BTCHexFromData by 5.5x (-85%) Jul 19, 2018
Copy link

@hasti0073 hasti0073 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bc1qvz0lsuunca2wlet8fy8kaezvy9yf624dmyr85w

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants