c#匹配内存中数据的固定属性
伙计们,这伤了我的脑筋 我在Linq中有一个慢的实现,我需要加快 我需要最快的方法来比较内存(200000)行中的大量只读数据 每行有一个名称和10个表示名称属性的整数c#匹配内存中数据的固定属性,c#,C#,伙计们,这伤了我的脑筋 我在Linq中有一个慢的实现,我需要加快 我需要最快的方法来比较内存(200000)行中的大量只读数据 每行有一个名称和10个表示名称属性的整数 Brake 8 7 3 2 1 0 4 3 2 Skull 8 7 3 2 1 0 4 3 2 Napkin 3 0 5 3 2 1 3 1 0 在任何一个属性中,每个项目都不会超过8个。因此,每个值都可以很好地放入一个double中 例: 现在我的问题是,我需要将这些值与每个字段的最大值进行比较 即: 500000
Brake 8 7 3 2 1 0 4 3 2
Skull 8 7 3 2 1 0 4 3 2
Napkin 3 0 5 3 2 1 3 1 0
在任何一个属性中,每个项目都不会超过8个。因此,每个值都可以很好地放入一个double中
例:
现在我的问题是,我需要将这些值与每个字段的最大值进行比较
即:
500000000将返回前2个和
000001000只会退回餐巾纸,因为其他两个餐巾纸的第4位至少没有1个餐巾纸
我愿意接受任何尽可能快的解决方案。会有很多比较,所以速度是我唯一担心的事情
我当前的实现是这样的:
Items.Where(c => c.A <= 5).Where(c => c.B <= 0).Where(c => c.C <= 0)
.Where(c => c.D <= 0).Where(c => c.E <= 0).Where(c => c.F <= 0)
.Where(c => c.H <= 0).Where(c => c.I <= 0).Where(c => c.J <= 0)
Items.Where(c=>c.A c.B c.c.D c.E c.F c.H c.I c.J c.A事实上,有一种比我原来的答案更好的方法,但它要求每个项目5位,而不是每个项目4位。我将用16位值中的三个项目来说明。您可以使用长整数的前50位将其扩展到10个项目
假设您将三个4位计数器保存在一个16位整数中。每个计数器都有一个用于计算的附加位(请参见下文)。因此,包含前三个计数器的制动项为:
3 7 8
Brake 00011 00111 01000
4 0 5
Search 00100 00000 00101
现在,您需要匹配“504”的内容,即第一列中至少有五个项目,第二列中有任意数字,第三列中至少有四个。该掩码为:
3 7 8
Brake 00011 00111 01000
4 0 5
Search 00100 00000 00101
从逻辑上讲,我们希望从制动器中减去搜索,并知道所有必填字段是否大于或等于零。这就是额外位的来源。如果我们在每个字段上设置额外位,我们有:
Brake 10011 10111 11000
现在,如果我们在这种情况下做减法,我们得到:
Brake - Search = 01111 10111 10011
请注意,结果最左侧字段中的高位为0。这表示该字段中的制动值小于我们要查找的值
现在,如何在代码中工作。在我的示例中,使用short
,我有:
short Brake = (short)((3 << 10) | (7 << 5) | 8);
short Search = (short)((4 << 10) | (0 << 5) | 5);
short CarryMask = 0x4210; // sets the high bit in each field.
// corresponds to 0100001000010000
// This would have to be done for each value that you want to compare.
short MaskedValue = (short)(Brake | CarryMask);
short diff = (short)(MaskedValue - Search);
short rslt = (short)(CarryMask & diff);
// rslt should equal CarryMask
if (rslt == CarryMask)
{
// All fields match
}
else
{
// At least one field doesn't match
}
我假设您有某种格式的数据,您可以方便地将每个项的字段值放入一个10字节的数组中。您需要添加一个保存组合值的CombinedValue
属性。或者,您可能有一个并行数据结构来保存组合值。无论如何,您必须在p执行此循环一次程序启动以创建用于比较的数据(或者如果可以更新单个字段,则可能更新值)
我也会假设,当需要搜索时,您可以将要搜索的值放入字节数组中,然后调用CreateValue
来获得要搜索的组合值。现在您所需要的只是进行比较的方法
// Carry mask has every 5th bit set.
// This is actually the mask for 12 values.
// That's okay, since nothing will affect those higher bits.
const long CarryMask = 0x842108421084210L;
bool ItemMatches(long itemValue, long searchFor)
{
long maskedValue = itemValue | CarryMask;
long diff = maskedValue - searchFor;
long rslt = diff & CarryMask;
return (rslt == CarryMask);
}
因此,搜索列表变得非常简单:
long searchFor = CreateValue(array_of_values);
foreach (var item in items)
{
if (ItemMatches(item.CombinedValue, searchFor)
{
// The item matches the criteria you specified
}
}
您的数据由以下结构表示:
public abstract class Widget
{
public string Name { get ; private set ; }
public byte[] Values { get ; private set ; }
}
无论是一个字节数组,还是从中提取的位字段,我怀疑与性能无关
一种方法是构建8个并行数组,其中包含对小部件的引用,每个这样的数组在不同的值列上排序
另一种方法是读取源数据一次,然后填充一个高度平衡的二进制搜索树数组,其键是所需列的值,其值是共享该特定列值的窗口小部件列表。每个列都需要一个这样的搜索树。显然,这种方法会消耗内存以获得快速查找在高度平衡二叉树中,是一种O(logn)操作。使用树意味着可以向集合中添加项或从集合中删除项,而不会产生大量开销
虽然您可以编写自己的树实现,但我不愿意。下面是一个实现,使用(您的工具箱中应该有的东西),因为我讨厌发明轮子:
using System;
using System.Collections.Generic;
using C5;
namespace ConsoleApplication13
{
class Program
{
static void Main( string[] args )
{
IEnumerable<Widget> sourceData = ReadWidgets();
WidgetSearcher lookup = new WidgetSearcher( sourceData );
// find all the Widgets where column 2 is >= 5 ;
Widget[] results1 = lookup.Search( 2 , 5 ).ToArray();
// find all the Widgets where column 0 is >= 3 ;
Widget[] results2 = lookup.Search( 0 , 3 ).ToArray();
return ;
}
private static IEnumerable<Widget> ReadWidgets()
{
//TODO: we need source data from somewhere. It gets provided here.
throw new NotImplementedException();
}
}
public class Widget
{
public const int ValueCount = 8;
public string Name { get; private set; }
public byte[] Values
{
get
{
return (byte[])_values.Clone();
}
}
private byte[] _values;
public Widget( string name , byte[] values )
{
if ( name==null )
throw new ArgumentNullException( "name" );
if ( name.Trim()=="" )
throw new ArgumentOutOfRangeException( "name" );
if ( values==null )
throw new ArgumentNullException( "values" );
if ( values.Length!=ValueCount )
throw new ArgumentOutOfRangeException( "values" );
this.Name=name;
this._values=values;
return;
}
/// <summary>
/// private constructor for search instances
/// </summary>
/// <param name="column"></param>
/// <param name="value"></param>
private Widget( int column , byte value )
{
this.Name=null;
this._values=new byte[Widget.ValueCount];
this._values.Initialize();
this._values[column]=value;
return;
}
public class Comparer : IComparer<Widget> , IEqualityComparer<Widget>
{
private int ColumnToCompare;
public Comparer( int colNum )
{
if ( colNum<0||colNum>=Widget.ValueCount )
throw new ArgumentOutOfRangeException( "colNum" );
this.ColumnToCompare=colNum;
}
#region IComparer<Widget> Members
public int Compare( Widget x , Widget y )
{
return (int)x._values[this.ColumnToCompare]-(int)y._values[this.ColumnToCompare];
}
#endregion
#region IEqualityComparer<Widget> Members
public bool Equals( Widget x , Widget y )
{
return ( x._values[this.ColumnToCompare]==x._values[this.ColumnToCompare] );
}
public int GetHashCode( Widget obj )
{
return obj._values[this.ColumnToCompare].GetHashCode();
}
#endregion
}
internal static Widget CreateSearchInstance( int column , byte value )
{
return new Widget( column , value );
}
}
public class WidgetSearcher
{
private C5.TreeBag<Widget>[] lookups;
public WidgetSearcher( IEnumerable<Widget> sourceData )
{
this.lookups=InstantiateLookups();
PopulateLookups( sourceData );
}
private TreeBag<Widget>[] InstantiateLookups()
{
C5.TreeBag<Widget>[] instance =new C5.TreeBag<Widget>[Widget.ValueCount];
for ( int i = 0 ; i<instance.Length ; ++i )
{
Widget.Comparer widgetComparer = new Widget.Comparer( i );
instance[i]=new TreeBag<Widget>( widgetComparer , widgetComparer );
}
return instance;
}
private void PopulateLookups( IEnumerable<Widget> sourceData )
{
foreach ( Widget datum in sourceData )
{
for ( int i = 0 ; i<Widget.ValueCount ; ++i )
{
lookups[i].Add( datum );
}
}
return;
}
public IDirectedCollectionValue<Widget> Search( int column , byte value )
{
Widget limit = Widget.CreateSearchInstance( column , value );
return lookups[column].RangeFrom( limit );
}
}
}
使用系统;
使用System.Collections.Generic;
使用C5;
命名空间控制台应用程序13
{
班级计划
{
静态void Main(字符串[]参数)
{
IEnumerable sourceData=ReadWidgets();
WidgetSearcher查找=新建WidgetSearcher(sourceData);
//查找第2列>=5的所有小部件;
Widget[]results1=lookup.Search(2,5.ToArray();
//查找列0大于等于3的所有小部件;
Widget[]results2=lookup.Search(0,3.ToArray();
返回;
}
私有静态IEnumerable ReadWidgets()
{
//TODO:我们需要某个地方的源数据。它在这里提供。
抛出新的NotImplementedException();
}
}
公共类小部件
{
public const int ValueCount=8;
公共字符串名称{get;private set;}
公共字节[]值
{
得到
{
返回(字节[])_值。克隆();
}
}
专用字节[]_值;
公共小部件(字符串名称,字节[]值)
{
if(name==null)
抛出新的ArgumentNullException(“名称”);
如果(name.Trim()==“”)
抛出新ArgumentOutOfRangeException(“名称”);
如果(值==null)
抛出新的ArgumentNullException(“值”);
if(values.Length!=ValueCount)
抛出新ArgumentOutOfRangeException(“值”);
this.Name=Name;
这是。_值=值;
返回;
}
///
///搜索实例的私有构造函数
///
///
///
私有小部件(int)
public abstract class Widget
{
public string Name { get ; private set ; }
public byte[] Values { get ; private set ; }
}
using System;
using System.Collections.Generic;
using C5;
namespace ConsoleApplication13
{
class Program
{
static void Main( string[] args )
{
IEnumerable<Widget> sourceData = ReadWidgets();
WidgetSearcher lookup = new WidgetSearcher( sourceData );
// find all the Widgets where column 2 is >= 5 ;
Widget[] results1 = lookup.Search( 2 , 5 ).ToArray();
// find all the Widgets where column 0 is >= 3 ;
Widget[] results2 = lookup.Search( 0 , 3 ).ToArray();
return ;
}
private static IEnumerable<Widget> ReadWidgets()
{
//TODO: we need source data from somewhere. It gets provided here.
throw new NotImplementedException();
}
}
public class Widget
{
public const int ValueCount = 8;
public string Name { get; private set; }
public byte[] Values
{
get
{
return (byte[])_values.Clone();
}
}
private byte[] _values;
public Widget( string name , byte[] values )
{
if ( name==null )
throw new ArgumentNullException( "name" );
if ( name.Trim()=="" )
throw new ArgumentOutOfRangeException( "name" );
if ( values==null )
throw new ArgumentNullException( "values" );
if ( values.Length!=ValueCount )
throw new ArgumentOutOfRangeException( "values" );
this.Name=name;
this._values=values;
return;
}
/// <summary>
/// private constructor for search instances
/// </summary>
/// <param name="column"></param>
/// <param name="value"></param>
private Widget( int column , byte value )
{
this.Name=null;
this._values=new byte[Widget.ValueCount];
this._values.Initialize();
this._values[column]=value;
return;
}
public class Comparer : IComparer<Widget> , IEqualityComparer<Widget>
{
private int ColumnToCompare;
public Comparer( int colNum )
{
if ( colNum<0||colNum>=Widget.ValueCount )
throw new ArgumentOutOfRangeException( "colNum" );
this.ColumnToCompare=colNum;
}
#region IComparer<Widget> Members
public int Compare( Widget x , Widget y )
{
return (int)x._values[this.ColumnToCompare]-(int)y._values[this.ColumnToCompare];
}
#endregion
#region IEqualityComparer<Widget> Members
public bool Equals( Widget x , Widget y )
{
return ( x._values[this.ColumnToCompare]==x._values[this.ColumnToCompare] );
}
public int GetHashCode( Widget obj )
{
return obj._values[this.ColumnToCompare].GetHashCode();
}
#endregion
}
internal static Widget CreateSearchInstance( int column , byte value )
{
return new Widget( column , value );
}
}
public class WidgetSearcher
{
private C5.TreeBag<Widget>[] lookups;
public WidgetSearcher( IEnumerable<Widget> sourceData )
{
this.lookups=InstantiateLookups();
PopulateLookups( sourceData );
}
private TreeBag<Widget>[] InstantiateLookups()
{
C5.TreeBag<Widget>[] instance =new C5.TreeBag<Widget>[Widget.ValueCount];
for ( int i = 0 ; i<instance.Length ; ++i )
{
Widget.Comparer widgetComparer = new Widget.Comparer( i );
instance[i]=new TreeBag<Widget>( widgetComparer , widgetComparer );
}
return instance;
}
private void PopulateLookups( IEnumerable<Widget> sourceData )
{
foreach ( Widget datum in sourceData )
{
for ( int i = 0 ; i<Widget.ValueCount ; ++i )
{
lookups[i].Add( datum );
}
}
return;
}
public IDirectedCollectionValue<Widget> Search( int column , byte value )
{
Widget limit = Widget.CreateSearchInstance( column , value );
return lookups[column].RangeFrom( limit );
}
}
}