确定存储用户输入差异所需的最小字段大小的有效方法
Posted
技术标签:
【中文标题】确定存储用户输入差异所需的最小字段大小的有效方法【英文标题】:Efficient way of determining minimum field size required to store variances in user input 【发布时间】:2010-11-10 05:17:29 【问题描述】:对不起这个笨拙的标题;我找不到一点表达我想做的事情的方式。
我从多个 32 位整数的用户那里得到一个输入。例如,用户可以输入以下值(为了便于说明,以十六进制显示):
0x00001234
0x00005678
0x0000abcd
在这种特殊情况下,每个输入的前 2 个字节是常量,而后 2 个字节是可变的。为了提高效率,我可以将0x0000
存储为单个常量,并创建一个包含uint16_t
值的向量来存储输入的变量部分(0x1234
、0x5678
、0xabcd
)。
现在假设用户输入以下内容:
0x00000234
0x56780000
0x00001000
在这种情况下,我需要一个包含uint32_t
值的向量来存储输入的可变部分,因为每个值都会影响不同的字节。
我目前的想法是做以下事情:
uint32_t myVal = 0;
myVal |= input1;
myVal |= input2;
// ...
然后在最后找到myVal
中第一个和最后一个“切换”(即1
)位之间的距离。该距离将为我提供所有输入的可变部分所需的字段大小。
但是,这听起来不太适合大量用户输入。关于确定这一点的优雅而有效的方法有什么建议吗?
更新:
我在上面的解释中简化了问题。
需要说明的是,我这样做并不是为了节省内存(我有比尝试节省几个字节更好的事情要做,这不是为了优化目的)。
总之,组件 A 在我的系统中为组件 B 提供了值。有时这些值是 128 位的,但组件 B 只支持 32 位的值。
如果128位值的可变部分可以用32位值表示,我可以接受。否则我将需要以错误方式拒绝它。
我无法修改组件 B 以允许 128 位值,或修改组件 A 以阻止其使用 128 位值(这里也有硬件限制)。
【问题讨论】:
你为什么觉得有必要这样做? @GMan - 我在解释中简化了问题。总之,组件 A 为我系统中的组件 B 提供了值。有时这些值为 128 位,但组件 B 仅支持 32 位值。如果 128 位值的可变部分可以用 32 位值表示,我可以接受。否则我将需要以错误方式拒绝它。我无法修改组件 B 以允许 128 位值,或修改组件 A 以阻止其使用 128 位值(这里也有硬件限制)。 @Leopard:所以你应该把它作为你的问题发布,因为这实际上是你面临的问题。提出理论问题只会得到一般的理论答案。 Ask real questions for real answers. @GMan - 很抱歉,我试图将问题分解为我要解决的基本要素。我不认为这会影响人们给出的答案,但不认为人们会质疑我的动机。 组件 B 如何找到每个 128 位值的开始和结束?是否以文本形式传递? 【参考方案1】:虽然我看不出这一切的原因...为什么不将输入与std::numeric_limits<uint16_t>::max()
进行比较?如果输入的值较大,则需要使用uint32_t
。
回答您的编辑:
我想为了获得更好的性能,您应该使用特定于硬件的低级指令。您可以迭代输入 128 位值的 32 位部分,然后将每个部分添加到某个变量并检查下一个值与当前总和之间的差异。如果差不等于总和,那么您应该跳过这个 128 位值,否则您最终会得到必要的结果。示例如下:
uint32_t get_value( uint32_t v1, uint32_t v2, uint32_t v3, uint32_t v4)
uint32_t temp = v1;
if ( temp - v2 != temp ) throw exception;
temp += v2; if ( temp - v3 != temp ) throw exception;
temp += v3; if ( temp - v4 != temp ) throw exception;
temp = v4;
return temp;
在这个 C++ 示例中,它可能看起来很傻,但我相信汇编代码应该可以有效地处理输入流。
【讨论】:
可能是最优雅的方式,但由于 OP 担心对每个输入进行按位运算的性能,他可能会担心它会扩展甚至“更糟”(相对而言)。我显然假设他处于一个真正对性能至关重要的环境中,而不仅仅是微优化。 @Rafael - 我这样做不是为了表现 - 请参阅我对问题的修订。 但是如果用户输入 0x12340000, 0x56780000, ... 那么这些值中的每一个都是 > 最大 uint16_t 值,但是如果我们仍然可以将变量部分(高 16 位)存储在16 位字段。【参考方案2】:存储你遇到的第一个完整的 128 位数字,然后将它的低 32 位推送到一个向量上,设置为bool reject_all = false
。对于每个剩余的数字,如果高位 (128-32=96) 位与第一个数字不同,则设置reject_all = true
,否则将它们的低位推入向量。在循环结束时,使用reject_all 来决定是否使用值向量。
【讨论】:
【参考方案3】:在[0, (2^32)-1]
范围内存储一系列无符号整数的最有效方法是使用uint32_t
。跳过箍从用户输入中节省 2 个字节不值得你花时间——用户在他的一生中不可能输入足够的整数,以至于你的代码必须开始压缩它们。他或她早在任何现代系统上的内存限制变得明显之前就死于老年。
【讨论】:
查看我对该问题的更新。我这样做不是为了节省内存。 如果我正确阅读了您的更新,您想拒绝超出范围的值吗? 正确。但它需要考虑所有用户输入值,而不是一次一个值。如果有5个值,其中4个的可变部分被限制在32位,但其中1个的可变部分超过32位,我需要全部拒绝。 你能不能把它们全部输入一个集合(例如std::vector<uint32_t>
),一旦输入完成,遍历集合并检查?我不明白这里的位操作因素如何,并且用户仍然无法提供足够的输入,以至于任何合理的方法都会带来性能损失。【参考方案4】:
看起来您必须想出一个累积位掩码 - 然后您可以查看它以查看您是否有尾随或前导常量位。需要对每个输入进行操作的算法(使其成为 O(n) 算法,其中 n 是要检查的值的数量)。
该算法类似于您已经完成的算法:
unsigned long long bitmask = 0uL;
std::size_t count = val.size();
for (std::size_t i = 0; i < count; ++i)
bitmask |= val[i];
然后您可以检查可以使前导/尾随多少位/字节保持不变,以及您是否要使用完整的 32 位。如果您有权访问 SSE 指令,则可以使用 OpenMP 对其进行矢量化。
还有一个可能的优化方法是通过短路来查看第一个1
位和最后一个1
位之间的距离是否已经大于32,在这种情况下您可以停止。
要使该算法更好地扩展,您必须并行执行。您的朋友可能是矢量处理(可能使用 CUDA 用于 Nvidia GPU,或者如果您在 Mac 上或在已经支持 OpenCL 的平台上使用 OpenCL,或者仅使用 OpenMP 注释)。
【讨论】:
【参考方案5】:使用
uint32_t ORVal = 0;
uint32_t ANDVal = 0xFFFFFFFF;
ORVal |= input1;
ANDVal &= input1;
ORVal |= input2;
ANDVal &= input2;
ORVal |= input3;
ANDVal &= input3; // etc.
// At end of input...
mask = ORVal ^ ANDVal;
// bit positions set to 0 were constant, bit positions set to 1 changed
如果至少一个输入在该位置有1
,则ORVal
中的位位置将是1
,如果所有输入在该位置有0
,则0
。 ANDVal
中的位位置将是0
,如果至少一个输入在该位位置有0
,如果所有输入在该位置有1
,则1
。
如果输入中的位位置始终为1
,则ORVal
和ANDVal
都将设置为1
。
如果输入中的位位置始终为0
,则ORVal
和ANDVal
都将设置为0
。
如果0
和1
在位位置混合,则ORVal
将设置为1
和ANDVal
设置为0
,因此最后的XOR
给出了掩码位位置发生变化。
【讨论】:
以上是关于确定存储用户输入差异所需的最小字段大小的有效方法的主要内容,如果未能解决你的问题,请参考以下文章
ODP.NET 是存储过程所需的 OracleParameter 字段吗?