我有大量(> 5gb)二进制文件,其中包含每个文件中固定偏移量的二进制格式的长数据类型(以及其他数据)。我有一个很长的查找值,并希望实现二进制搜索算法,以查找文件中查找值匹配,大于或小于最近的长值的位置。自定义二进制搜索算法不是问题,
...但我正在寻找帮助来比较两个二进制数组,以确定等效的长值是等于,大于还是小于查找值。显然,我想摆脱不必将二进制数组反序列化为长,否则问题就没有实际意义了。
我可以比较二进制数组并确定代表性的长值是等于,小于还是更大而无需从二进制数组反序列化/转换为long?解决方案应该比类型转换更快,并且占用内存更小。
答案 0 :(得分:2)
我真正的答案是:
BitConverter.ToInt64
。这是第二种选择,这似乎相当快。您将获得从所有CompareTo方法获得的正常-1,0,+ 1类型的值,这意味着:
代码:
public static int Compare(byte[] a, byte[] b)
{
bool aIsNegative = (a[7] & 0x80) != 0;
bool bIsNegative = (b[7] & 0x80) != 0;
if (aIsNegative != bIsNegative)
{
if (aIsNegative)
return -1;
return +1;
}
var a7 = a[7] & 0x7f;
var b7 = b[7] & 0x7f;
if (a7 < b7) return -1;
if (a7 > b7) return +1;
var a6 = a[6]; var b6 = b[6];
if (a6 < b6) return -1;
if (a6 > b6) return +1;
var a5 = a[5]; var b5 = b[5];
if (a5 < b5) return -1;
if (a5 > b5) return +1;
var a4 = a[4]; var b4 = b[4];
if (a4 < b4) return -1;
if (a4 > b4) return +1;
var a3 = a[3]; var b3 = b[3];
if (a3 < b3) return -1;
if (a3 > b3) return +1;
var a2 = a[2]; var b2 = b[2];
if (a2 < b2) return -1;
if (a2 > b2) return +1;
var a1 = a[1]; var b1 = b[1];
if (a1 < b1) return -1;
if (a1 > b1) return +1;
var a0 = a[0]; var b0 = b[0];
if (a0 < b0) return -1;
if (a0 > b0) return +1;
return 0;
}
显然如果您想使用此代码,您应该编写一些非常好的单元测试。在你做之前不要相信这段代码。
请注意,只有在使用little-endian字节顺序对数据进行编码时,此方法才能正常工作。
如果你需要使用big-endian字节顺序,反转方法中的字节顺序,使7变为0,6变为1,5变为2,等等。如果你读取方法,它从7变为0 0,这是小端的。对于big-endian,它从0到7。
有符号的64位数字,如Int64,以第64位(位#63)表示符号的方式编码,1表示负数,0表示正数。这就是为什么在检查符号位后,处理的第一个字节需要被屏蔽到只有7位。
我测试了以下方法:
[StructLayout(LayoutKind.Explicit, Pack=1)]
的结构,并在8字节字段之上显式覆盖Int64
字段。出乎我的意料,这确实比所有替代方案都要糟糕。我猜测所有字节到结构中的混乱都会给它带来更多的开销。使用Benchmark.NET的整个代码示例可以在this gist中找到。
以下是10000个测试用例的基准测试结果,上面的要点还有更多,包括完整的基准测试日志:
Method | N | Mean | Error | StdDev |
-------------------- |------ |--------------:|--------------:|--------------:|
StructConversion | 10000 | 171,900.82 ns | 544.7975 ns | 482.9487 ns |
ByteForByte | 10000 | 140,844.09 ns | 1,263.6139 ns | 1,181.9851 ns |
ByteForByteUnrolled | 10000 | 125,728.87 ns | 377.4937 ns | 315.2243 ns |
UsingBitConverter | 10000 | 130,397.25 ns | 497.1728 ns | 465.0557 ns |
注意对于1000个元素,ByteForByte和ByteForByteUnrolled结果相反。我在运行时确实使用了我的计算机,因此这可能会对结果产生影响。买家要小心。
答案 1 :(得分:1)
如何使用结构?
public class Program
{
[StructLayout(LayoutKind.Explicit)]
struct ValueStruct
{
[FieldOffset(0)]
public byte byte1;
[FieldOffset(1)]
public byte byte2;
[FieldOffset(2)]
public byte byte3;
[FieldOffset(3)]
public byte byte4;
[FieldOffset(0)]
public uint uint1;
}
static void Main(string[] args)
{
var value1 = new ValueStruct() { byte1 = 0x88, byte2 = 0x99, byte3 = 0xAA, byte4 = 0xBB };
var value2 = new ValueStruct() { byte1 = 0x11, byte2 = 0x22, byte3 = 0x33, byte4 = 0x44 };
Console.WriteLine(value1.uint1);
Console.WriteLine(value2.uint1);
if (value1.uint1 > value2.uint1)
{
Console.WriteLine("value1 is greater than value2");
}
Console.ReadLine();
}
}