优化btwoc()

优化btwoc(),c,openid,C,Openid,根据第4.2节 任意精度整数必须编码为大端有符号2的补码二进制字符串。此后,“btwoc”是一个函数,它接受一个任意精度的整数,并返回其最短的大端二补表示。Diffie-Hellman密钥交换使用的所有整数都是正整数。这意味着两个补码表示的最左边的位必须为零。如果不是,则实现必须在字符串前面添加一个零字节 非规范性示例: 我已经尝试过用C编写自己的btwoc实现,这就是我所拥有的: typedef struct { uint8_t *data; uintmax_t length;

根据第4.2节

任意精度整数必须编码为大端有符号2的补码二进制字符串。此后,“btwoc”是一个函数,它接受一个任意精度的整数,并返回其最短的大端二补表示。Diffie-Hellman密钥交换使用的所有整数都是正整数。这意味着两个补码表示的最左边的位必须为零。如果不是,则实现必须在字符串前面添加一个零字节

非规范性示例:

我已经尝试过用C编写自己的
btwoc
实现,这就是我所拥有的:

typedef struct {
    uint8_t *data;
    uintmax_t length;
} oid_data;

oid_data *oid_btwoc_r(uintmax_t value, oid_data *ret) {
    unsigned    fnz = sizeof(uintmax_t) + 1,
                i = sizeof(uintmax_t) * 8;
    while (--fnz && (!(value >> (i -= 8) & 0xFF)));
    /*
        If `value' is non-zero, `fnz' now contains the index of the first
        non-zero byte in `value', where 1 refers to the least-significant byte.
        `fnz' will therefore be in the range [1 .. sizeof(uintmax_t)]. If
        `value' is zero, then `fnz' is zero.
    */
    if (!value) {
        /* The value is zero */
        ret->length = 1;
        ret->data[0] = 0;
    } else if (value >> ((fnz - 1) * 8 + 7)) {
        /* The most significant bit of the first non-zero byte is 1 */
        ret->length = fnz + 1;
        ret->data[0] = 0;
        for (i = 1; i <= fnz; i++)
            ret->data[1 + fnz - i] =
                value >> ((i - 1) * 8);
    } else {
        /* The most significant bit of the first non-zero byte is 0 */
        ret->length = fnz;
        for (i = 1; i <= fnz; i++)
            ret->data[fnz - i] =
                value >> ((i - 1) * 8);
    }
    return ret;
}
typedef结构{
uint8_t*数据;
u最大长度;
}oid_数据;
oid_数据*oid_btwoc_r(uintmax_t值,oid_数据*ret){
无符号fnz=sizeof(uintmax_t)+1,
i=尺寸f(uintmax_t)*8;
而(--fnz&(!(值>>(i-=8)和0xFF));
/*
如果'value'不是零,'fnz'现在包含第一个值的索引
“值”中的非零字节,其中1表示最低有效字节。
`因此,fnz'将在[1..sizeof(uintmax_t)]范围内。如果
`“值”为零,则“fnz”为零。
*/
如果(!值){
/*值为零*/
ret->length=1;
ret->data[0]=0;
}否则如果(值>>((fnz-1)*8+7)){
/*第一个非零字节的最高有效位为1*/
ret->length=fnz+1;
ret->data[0]=0;
对于(i=1;i数据[1+fnz-i]=
值>>((i-1)*8);
}否则{
/*第一个非零字节的最高有效位为0*/
ret->length=fnz;
对于(i=1;i数据[fnz-i]=
值>>((i-1)*8);
}
返回ret;
}
ret->data
应指向至少为
sizeof(uintmax_t)+1个字节的有效内存


它工作得很好,我还没有在实现中发现任何错误,但是它可以被优化吗?

如果你把零作为一个特例,那么你肯定应该在
循环之前处理它,而
循环(个人bête noire:我不喜欢
(!value)
对于
(value==0)


我还没有尝试对其计时,但这段代码产生了与您相同的答案(至少在我测试的值上,请参见下文),并且对我来说看起来更简单,这不仅仅是因为它没有将代码加倍以处理高位设置在最高有效字节的情况:

void oid_btwoc_r2(uintmax_t value, oid_data *ret)
{
    if (value == 0)
    {
        ret->data[0] = 0;
        ret->length  = 1;
        return;
    }

    uintmax_t v0 = value;
    uintmax_t v1 = v0;
    unsigned  n  = 0;

    while (v0 != 0)
    {
        n++;
        v1 = v0;
        v0 >>= 8;
    }
    //printf("Value: 0x%" PRIXMAX ", v1 = 0x%" PRIXMAX "\n", value, v1);

    assert(v1 < 0x100 && v1 != 0);
    if (v1 > 0x7F)
        n++;
    //printf("N = %u\n", n);

    for (unsigned i = n; i != 0; i--)
    {
        ret->data[i-1] = (value & 0xFF);
        value >>= 8;
    }
    ret->length = n;
}
因此,
oid_btwoc_r2()
函数似乎比原始函数快约20%——至少在测试的数据上是这样。较大的数字改变了有利于
oid_btwoc_r()
的平衡,“oid_btwoc_r1()则快约20%:

oid_btwoc_r1(): 3.671201
oid_btwoc_r2(): 4.605171
oid_btwoc_r1(): 3.669026
oid_btwoc_r2(): 4.575745
oid_btwoc_r1(): 3.673729
oid_btwoc_r2(): 4.659433
oid_btwoc_r1(): 3.684662
oid_btwoc_r2(): 4.671654
oid_btwoc_r1(): 3.730757
oid_btwoc_r2(): 4.645485
oid_btwoc_r1(): 3.764600
oid_btwoc_r2(): 4.673244
oid_btwoc_r1(): 3.669582
oid_btwoc_r2(): 4.610177
oid_btwoc_r1(): 3.664248
oid_btwoc_r2(): 4.813711
oid_btwoc_r1(): 3.675927
oid_btwoc_r2(): 4.630148
oid_btwoc_r1(): 3.681798
oid_btwoc_r2(): 4.614129
由于在这种情况下,大的数字可能比小的数字更可能出现,因此可以说是更好的选择

测试代码如下。未注释的
for
循环是“大数字”版本,显示
oid_btwoc_r1()
oid_btwoc_r2()
工作得更快;注释掉的
for
循环是“小数字”版本,显示
oid_btwoc_r2()
oid_btwoc_r1()工作得更快

静态无效测试转换器(常量字符*标记,无效(*函数)(uintmax,oid数据*)
{
时钟时钟;
clk_init(&clk);
clk_启动(&clk);
对于(uintmax_t i=0x100000000;i<0x10000000000000;i+=0x170000000)
//对于(uintmax_t i=0;i<0x100000000;i+=17)
{
uint8_t b1[sizeof(uintmax_t)+1];
oid_数据v1={b1,sizeof(b1)};
(*功能)(i和v1);
}
时钟停止(&clk);
字符缓冲区[32];
printf(“%s:%s\n”,标记,clk_过去的时间(&clk,buffer,sizeof(buffer));
}
内部主(空)
{
对于(int i=0;i<10;i++)
{
测试转换器(“oid_btwoc_r1()”,oid_btwoc_r1);
测试转换器(“oid_btwoc_r2()”,oid_btwoc_r2);
}
返回(0);
}

如果你把零作为一个特例,你肯定应该在
循环之前处理它


我还没有尝试对其计时,但这段代码产生了与您相同的答案(至少在我测试的值上,请参见下文),并且对我来说看起来更简单,这不仅仅是因为它没有将代码加倍以处理高位设置在最高有效字节的情况:

void oid_btwoc_r2(uintmax_t value, oid_data *ret)
{
    if (value == 0)
    {
        ret->data[0] = 0;
        ret->length  = 1;
        return;
    }

    uintmax_t v0 = value;
    uintmax_t v1 = v0;
    unsigned  n  = 0;

    while (v0 != 0)
    {
        n++;
        v1 = v0;
        v0 >>= 8;
    }
    //printf("Value: 0x%" PRIXMAX ", v1 = 0x%" PRIXMAX "\n", value, v1);

    assert(v1 < 0x100 && v1 != 0);
    if (v1 > 0x7F)
        n++;
    //printf("N = %u\n", n);

    for (unsigned i = n; i != 0; i--)
    {
        ret->data[i-1] = (value & 0xFF);
        value >>= 8;
    }
    ret->length = n;
}
因此,
oid_btwoc_r2()
函数似乎比原始函数快约20%——至少在测试的数据上是这样。较大的数字改变了有利于
oid_btwoc_r()
的平衡,“oid_btwoc_r1()则快约20%:

oid_btwoc_r1(): 3.671201
oid_btwoc_r2(): 4.605171
oid_btwoc_r1(): 3.669026
oid_btwoc_r2(): 4.575745
oid_btwoc_r1(): 3.673729
oid_btwoc_r2(): 4.659433
oid_btwoc_r1(): 3.684662
oid_btwoc_r2(): 4.671654
oid_btwoc_r1(): 3.730757
oid_btwoc_r2(): 4.645485
oid_btwoc_r1(): 3.764600
oid_btwoc_r2(): 4.673244
oid_btwoc_r1(): 3.669582
oid_btwoc_r2(): 4.610177
oid_btwoc_r1(): 3.664248
oid_btwoc_r2(): 4.813711
oid_btwoc_r1(): 3.675927
oid_btwoc_r2(): 4.630148
oid_btwoc_r1(): 3.681798
oid_btwoc_r2(): 4.614129
由于在这种情况下,大的数字可能比小的数字更可能出现,因此可以说是更好的选择

测试代码如下。未注释的
for
循环是“大数字”版本,显示
oid_btwoc_r1()
oid_btwoc_r2()
工作得更快;注释掉的
for
循环是“小数字”版本,显示
oid_btwoc_r2()
oid_btwoc_r1()工作得更快

静态无效测试转换器(常量字符*标记,无效(*函数)(uintmax,oid数据*)
{
时钟时钟;
clk_init(&clk);
clk_启动(&clk);
对于(uintmax_t i=0x100000000;i<0x10000000000000;i+=0x170000000)
//对于(uintmax_t i=0;i<0x100000000;i+=17)
{
uint8_t b1[sizeof(uintmax_t)+1];
oid_数据v1={b1,sizeof(b1)};
(*功能)(i和v1);
}
时钟停止(&clk);
字符缓冲区[32];
printf(“%s:%s\n”,标记,clk_过去的时间(&clk,buffer,sizeof(buffer));
}
内部主(空)
{
对于(int i=0;i<10;i++)
{
测试转换器(“oid_btwoc_r1()”,oid_btwoc_r1);
测试
oid_btwoc_r1(): 3.671201
oid_btwoc_r2(): 4.605171
oid_btwoc_r1(): 3.669026
oid_btwoc_r2(): 4.575745
oid_btwoc_r1(): 3.673729
oid_btwoc_r2(): 4.659433
oid_btwoc_r1(): 3.684662
oid_btwoc_r2(): 4.671654
oid_btwoc_r1(): 3.730757
oid_btwoc_r2(): 4.645485
oid_btwoc_r1(): 3.764600
oid_btwoc_r2(): 4.673244
oid_btwoc_r1(): 3.669582
oid_btwoc_r2(): 4.610177
oid_btwoc_r1(): 3.664248
oid_btwoc_r2(): 4.813711
oid_btwoc_r1(): 3.675927
oid_btwoc_r2(): 4.630148
oid_btwoc_r1(): 3.681798
oid_btwoc_r2(): 4.614129
static void test_converter(const char *tag, void (*function)(uintmax_t, oid_data *))
{
    Clock clk;
    clk_init(&clk);
    clk_start(&clk);
    for (uintmax_t i = 0x100000000; i < 0x1000000000000000; i += 0x170000000)
    //for (uintmax_t i = 0; i < 0x100000000; i += 17)
    {
        uint8_t b1[sizeof(uintmax_t) + 1];
        oid_data v1 = { b1, sizeof(b1) };
        (*function)(i, &v1);
    }
    clk_stop(&clk);
    char buffer[32];
    printf("%s: %s\n", tag, clk_elapsed_us(&clk, buffer, sizeof(buffer)));
}

int main(void)
{
    for (int i = 0; i < 10; i++)
    {
        test_converter("oid_btwoc_r1()", oid_btwoc_r1);
        test_converter("oid_btwoc_r2()", oid_btwoc_r2);
    }
    return(0);
}