想做一个通用的byte转换enum转换算法,检查enum是否合法 ,结果发现效率很低,只能这样写:
private T GetEnumFromByte<T>(byte b) {
if (Enum.IsDefined(typeof(T), b)) {
return (T)Enum.Parse(typeof(T), b.ToString());
}
else {
throw SgipException.CreateByErrorCode(5);
}
}
以下写法不能编译:
private T GetEnumFromByte<T>(byte b) where T:struct {
if (Enum.IsDefined(typeof(T), b)) {
return (T)b;
}
else {
throw SgipException.CreateByErrorCode(5);
}
}
不知道有没有更好的算法,因为这个算法太慢了。要把byte转成string,然后再用enum的parse方法转回来。我希望能直接转换,这样速度比较快。
private T GetEnumFromByte<T>(byte b) {
if (Enum.IsDefined(typeof(T), b)) {
return (T)Enum.Parse(typeof(T), b.ToString());
}
else {
throw SgipException.CreateByErrorCode(5);
}
}
以下写法不能编译:
private T GetEnumFromByte<T>(byte b) where T:struct {
if (Enum.IsDefined(typeof(T), b)) {
return (T)b;
}
else {
throw SgipException.CreateByErrorCode(5);
}
}
不知道有没有更好的算法,因为这个算法太慢了。要把byte转成string,然后再用enum的parse方法转回来。我希望能直接转换,这样速度比较快。
where 后面可以用class 和struct,但是不能用Enum和ValueType,int等类型
性能倒是其次,主要向探索一下值类型如何运用范型约束。而且所有的enum类型转换都有这个问题的。比如我可以把一个非法的值转换成一个enum,而系统运行时也不会报错,但是逻辑就不对了。
{
T value = (T)Convert.ChangeType(b, Enum.GetUnderlyingType(typeof(T)));
if (Enum.IsDefined(typeof(T), value))
{
return value;
}
else
{
throw SgipException.CreateByErrorCode(5);
}
}别看用到了ChangeType,其实这里速度满快的,比Parse快
但是你要保证T是一个Enum,若用C++/CLI可以让编译器帮你检查。
private EnumTypeName GetEnumFromByte<EnumTypeName, IntTypeName>(IntTypeName b)
where EnumTypeName : struct//enum一定要是一个Enum,此代码中不做检查
where IntTypeName : struct {//intType可以是各种int类型,要与EnumType的基础类型一致,此处不做检查
EnumTypeName value = (EnumTypeName)Convert.ChangeType(b, Enum.GetUnderlyingType(typeof(EnumTypeName)));
if (Enum.IsDefined(typeof(EnumTypeName), value)) {
return value;
}
else {
throw new InvalidCastException();
}
}
美中不足的是,Enum.IsDefined(typeof(EnumTypeName), value)这个地方还是有装箱操作,不如依次判断来得快。
因为c#语法的限制,约束时只能限制enum为值类型,不能限制为enum类型。为了提高效率,没有在代码中检查。用的时候按照说明就可以了。
比如
if(value!=(byte)Color.Red ||value!=(byte)Color.Blue ||value!=(byte)Color.Green){
...
}
不过,如果每个Enum都这样写的话,就难以写成通用的算法了。不知道还有没有更好的算法,能够避免装箱操作。
暂时不结贴。要是谁给出更好的算法,赠送500分。
Enum.EsDefined是用二分法查找的,性能是O(Enum元素个数),如果元素很多,那么这个方法比你逐个比较更有效!完全可以抵消掉装箱损失。
况且你用于网络应用,我认为每个包两次的装箱/拆箱不算什么,不影响到你的性能。
{
Type enumType = typeof(TEnum);
TEnum value = (TEnum)Convert.ChangeType(b, Enum.GetUnderlyingType(enumType));
TEnum[] enumValues = (TEnum[])Enum.GetValues(enumType); if (Array.BinarySearch(enumValues, value) > 0)
{
return value;
}
else
{
throw SgipException.CreateByErrorCode(5);
}
}PS.不用约束,因为即使约束,也不可能保证TEnum一定是枚举,而且性能也没有任何不同。
static Enumtype[] values;
static Type enumType = typeof(Enumtype);
static EnumConverter() {
values = (Enumtype[]) Enum.GetValues(typeof(Enumtype));
}
public static Enumtype GetFromByte(byte value) {
for (int i = 0; i < values.Length; i++) {
byte enumValue = (byte)((IConvertible)values[i]); ;
if (enumValue ==value) {
return values[i];
}
}
throw new InvalidCastException(string.Format("Can't convert from System.Byte to {0}", enumType.Name));
}
public static Enumtype GetFromInt(int value) {
for (int i = 0; i < values.Length; i++) {
int enumValue = (int)((IConvertible)values[i]);
if (enumValue == value) {
return values[i];
}
}
throw new InvalidCastException(string.Format("Can't convert from System.Int32 to {0}", enumType.Name));
}
对于Enum,一般都只有十来个。对于这种情况,逐个比较应该比创建hashTable再查找来的快。
此外,在静态构造方法中取出了Enum的值列表,避免了每次查找。这个算法比较满意了。
不过,可能还是有装箱。只不过藏在系统的方法里面。遗憾的地方是不能使用指针。否则还可以再挖出一点效率。
static Enumtype[] values;
static EnumBaseType[] intValues;
static string enumTypeName = typeof(Enumtype).FullName;
static string enumBaseTypeName = typeof(EnumBaseType).FullName;
static EnumConverter() {
values = (Enumtype[]) Enum.GetValues(typeof(Enumtype));
intValues = new EnumBaseType[values.Length];
for (int i = 0; i < values.Length;i++ ) {
EnumBaseType intValue = (EnumBaseType)(IConvertible)values[i];
intValues[i] = intValue;
}
}
public static Enumtype GetEnum(EnumBaseType value) {
for (int i = 0; i < intValues.Length; i++) {
if (((IComparable<EnumBaseType>)intValues[i]).CompareTo(value)==0) {
return values[i];
}
}
throw new InvalidCastException(string.Format("Can't convert from {0} to {1}", enumBaseTypeName,enumTypeName));
} }
不知道这样在转换的方法中会不会装箱。得看看il。
TEnum[] enumValues = (TEnum[])Enum.GetValues(enumType);如果按你写的那样,把这个缓存进static域里,就非常完美了。但是我觉得没必要了,快不了多少。PS.要积极推行大写字母T开头定义类型参数的命名规则
但是觉得你追求速度也用不到这样吧。。
实际上,我看了il,我的后一种方法,可能比前一种方法更慢。转型成IConvertible的时候,创建了一个instance. 而且代码变长了。而前一个方法只不过有一个box.
确实,这种优化没有必要。因为在网关上最慢的操作是写日志。我也不提倡做这样的优化。
不过,这个问题研究一下还是有必要的。因为这种int转换成Enum的操作很普遍。一次写好定型了,以后都会用得着。
这个代码还是可以继续优化的。比如在枚举数量较多的情况下采用二分法查找,较少的时候逐个比较。还有完全避免创建任何instance。
你的算法在枚举数量较多的情况下,肯定会比较快。但是一般情况下,枚举都不超过十个值。这时候逐个比较反而会快一些。
Convert.ChangeType里面调用的代码有几次创建实例的操作。不会很快的。我也可以用二分法查找的,但是我担心BinarySearch会在执行过程中创建实例,这样就会抵消算法的优势。
isDefine里面还不是二分法查找,而是hashtable,在数量较多的情况下,比二分法更快。但是创建实例的操作比较多。这个就抵消了算法上的优势。因为创建实例的速度要比数值计算慢一个数量级。(mSDN里面说慢20倍)。因为前者要执行一系列与分配内存有关的操作,尽管在il中只有以行代码,但是.net framework还要执行一系列分配内存的操作。而后者仅仅是几条机器指令。
确实,我想把算法尽量优化到最大程度。因为我想证明c#不比c++慢。但是,要做到这一点,就得在保持代码外部接口不受影响的情况下,在内部尽量避免创建实例。算法一般是差不多的。
c#的优势在于开发速度快,有精力在算法上做更好的优化。
但是现在有个瓶颈,就是数据库insert执行的速度太慢。我做了测试,sqlserver在默认配置下,创建一个表,有十个nvarchar字段,递增主键。然后向表中连续插入10000条每个字段100个英文字母的字符串,这样每条记录实际长度是2004字节。c#每秒只能插入1300-1500条左右的数据。用vc+Ado也是一样,甚至还要更慢一些。用查询分析器执行的结果也差不多。
把每个字段的长度减少到一个字节,插入速度并没有明显的提高。仅仅上升到2000条每秒左右。
用批更新是行不通的,因为每个包都需要记日志。我Google了一下,发现大家差不多都是这个数字。
这个问题,恐怕难以解决,不知道老兄有没有什么建议?此外,不知道tpcc上每分钟300万个交易的纪录(非集群),用的是什么技术?为什么相差这么大?