Improved efficiency of startup methods.

- ShpReader will copy the input stream into memory just once rather than for every header.
- ShpReader.CopyImageData switched to use Array.Copy since that uses some unsafe magic for speed.
- In ActorInfo, cache a GetType call and prevent needless materialization in PrerequisitesOf.
- In ObjectCreator, cache type and ctor lookups since these are expensive and often repeated.
- Implement IReadOnlyDictionary<T, U> on Cache<T, U> to provide some supplementary functions.
- In TechTree.GatherOwnedPrerequisites, rearrange a Boolean 'and' expression to evaluate expensive functions later in the chain, and use ContainsKey to speed up name check.
This commit is contained in:
RoosterDragon
2014-05-28 21:29:29 +01:00
parent 334a210231
commit e63f330717
8 changed files with 93 additions and 77 deletions

View File

@@ -12,58 +12,58 @@ namespace OpenRA.FileFormats
{ {
public static class Format40 public static class Format40
{ {
public static int DecodeInto( byte[] src, byte[] dest ) public static int DecodeInto(byte[] src, byte[] dest, int srcOffset)
{ {
var ctx = new FastByteReader(src); var ctx = new FastByteReader(src, srcOffset);
int destIndex = 0; int destIndex = 0;
while( true ) while (true)
{ {
byte i = ctx.ReadByte(); byte i = ctx.ReadByte();
if( ( i & 0x80 ) == 0 ) if ((i & 0x80) == 0)
{ {
int count = i & 0x7F; int count = i & 0x7F;
if( count == 0 ) if (count == 0)
{ {
// case 6 // case 6
count = ctx.ReadByte(); count = ctx.ReadByte();
byte value = ctx.ReadByte(); byte value = ctx.ReadByte();
for( int end = destIndex + count ; destIndex < end ; destIndex++ ) for (int end = destIndex + count; destIndex < end; destIndex++)
dest[ destIndex ] ^= value; dest[destIndex] ^= value;
} }
else else
{ {
// case 5 // case 5
for( int end = destIndex + count ; destIndex < end ; destIndex++ ) for (int end = destIndex + count; destIndex < end; destIndex++)
dest[destIndex] ^= ctx.ReadByte(); dest[destIndex] ^= ctx.ReadByte();
} }
} }
else else
{ {
int count = i & 0x7F; int count = i & 0x7F;
if( count == 0 ) if (count == 0)
{ {
count = ctx.ReadWord(); count = ctx.ReadWord();
if( count == 0 ) if (count == 0)
return destIndex; return destIndex;
if( ( count & 0x8000 ) == 0 ) if ((count & 0x8000) == 0)
{ {
// case 2 // case 2
destIndex += ( count & 0x7FFF ); destIndex += (count & 0x7FFF);
} }
else if( ( count & 0x4000 ) == 0 ) else if ((count & 0x4000) == 0)
{ {
// case 3 // case 3
for( int end = destIndex + ( count & 0x3FFF ) ; destIndex < end ; destIndex++ ) for (int end = destIndex + (count & 0x3FFF); destIndex < end; destIndex++)
dest[destIndex] ^= ctx.ReadByte(); dest[destIndex] ^= ctx.ReadByte();
} }
else else
{ {
// case 4 // case 4
byte value = ctx.ReadByte(); byte value = ctx.ReadByte();
for( int end = destIndex + ( count & 0x3FFF ) ; destIndex < end ; destIndex++ ) for (int end = destIndex + (count & 0x3FFF); destIndex < end; destIndex++)
dest[ destIndex ] ^= value; dest[destIndex] ^= value;
} }
} }
else else

View File

@@ -16,11 +16,12 @@ namespace OpenRA.FileFormats
class FastByteReader class FastByteReader
{ {
readonly byte[] src; readonly byte[] src;
int offset = 0; int offset;
public FastByteReader(byte[] src) public FastByteReader(byte[] src, int offset = 0)
{ {
this.src = src; this.src = src;
this.offset = offset;
} }
public bool Done() { return offset >= src.Length; } public bool Done() { return offset >= src.Length; }
@@ -59,9 +60,9 @@ namespace OpenRA.FileFormats
} }
} }
public static int DecodeInto(byte[] src, byte[] dest) public static int DecodeInto(byte[] src, byte[] dest, int srcOffset = 0)
{ {
var ctx = new FastByteReader(src); var ctx = new FastByteReader(src, srcOffset);
var destIndex = 0; var destIndex = 0;
while (true) while (true)

View File

@@ -67,6 +67,9 @@ namespace OpenRA.FileFormats
int recurseDepth = 0; int recurseDepth = 0;
readonly int imageCount; readonly int imageCount;
readonly long shpBytesFileOffset;
readonly byte[] shpBytes;
public ShpReader(Stream stream) public ShpReader(Stream stream)
{ {
imageCount = stream.ReadUInt16(); imageCount = stream.ReadUInt16();
@@ -93,22 +96,16 @@ namespace OpenRA.FileFormats
throw new InvalidDataException("Reference doesnt point to image data {0}->{1}".F(h.FileOffset, h.RefOffset)); throw new InvalidDataException("Reference doesnt point to image data {0}->{1}".F(h.FileOffset, h.RefOffset));
} }
shpBytesFileOffset = stream.Position;
shpBytes = stream.ReadBytes((int)(stream.Length - stream.Position));
foreach (var h in headers) foreach (var h in headers)
Decompress(stream, h); Decompress(h);
spriteFrames = Exts.Lazy(() => headers.Cast<ISpriteFrame>()); spriteFrames = Exts.Lazy(() => headers.Cast<ISpriteFrame>());
} }
static byte[] ReadCompressedData(Stream stream, ImageHeader h) void Decompress(ImageHeader h)
{
stream.Position = h.FileOffset;
// Actually, far too big. There's no length field with the correct length though :(
var compressedLength = (int)(stream.Length - stream.Position);
return stream.ReadBytes(compressedLength);
}
void Decompress(Stream stream, ImageHeader h)
{ {
// No extra work is required for empty frames // No extra work is required for empty frames
if (h.Size.Width == 0 || h.Size.Height == 0) if (h.Size.Width == 0 || h.Size.Height == 0)
@@ -125,19 +122,19 @@ namespace OpenRA.FileFormats
if (h.RefImage.Data == null) if (h.RefImage.Data == null)
{ {
++recurseDepth; ++recurseDepth;
Decompress(stream, h.RefImage); Decompress(h.RefImage);
--recurseDepth; --recurseDepth;
} }
h.Data = CopyImageData(h.RefImage.Data); h.Data = CopyImageData(h.RefImage.Data);
Format40.DecodeInto(ReadCompressedData(stream, h), h.Data); Format40.DecodeInto(shpBytes, h.Data, (int)(h.FileOffset - shpBytesFileOffset));
break; break;
} }
case Format.Format80: case Format.Format80:
{ {
var imageBytes = new byte[Size.Width * Size.Height]; var imageBytes = new byte[Size.Width * Size.Height];
Format80.DecodeInto(ReadCompressedData(stream, h), imageBytes); Format80.DecodeInto(shpBytes, imageBytes, (int)(h.FileOffset - shpBytesFileOffset));
h.Data = imageBytes; h.Data = imageBytes;
break; break;
} }
@@ -150,9 +147,7 @@ namespace OpenRA.FileFormats
byte[] CopyImageData(byte[] baseImage) byte[] CopyImageData(byte[] baseImage)
{ {
var imageData = new byte[Size.Width * Size.Height]; var imageData = new byte[Size.Width * Size.Height];
for (var i = 0; i < Size.Width * Size.Height; i++) Array.Copy(baseImage, imageData, imageData.Length);
imageData[i] = baseImage[i];
return imageData; return imageData;
} }

View File

@@ -94,7 +94,11 @@ namespace OpenRA
while (t.Count != 0) while (t.Count != 0)
{ {
var prereqs = PrerequisitesOf(t[index]); var prereqs = PrerequisitesOf(t[index]);
var unsatisfied = prereqs.Where(n => !ret.Any(x => x.GetType() == n || n.IsAssignableFrom(x.GetType()))); var unsatisfied = prereqs.Where(n => !ret.Any(x =>
{
var type = x.GetType();
return type == n || n.IsAssignableFrom(type);
}));
if (!unsatisfied.Any()) if (!unsatisfied.Any())
{ {
ret.Add(t[index]); ret.Add(t[index]);
@@ -111,14 +115,13 @@ namespace OpenRA
return ret; return ret;
} }
static List<Type> PrerequisitesOf(ITraitInfo info) static IEnumerable<Type> PrerequisitesOf(ITraitInfo info)
{ {
return info return info
.GetType() .GetType()
.GetInterfaces() .GetInterfaces()
.Where( t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof( Requires<> ) ) .Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Requires<>))
.Select( t => t.GetGenericArguments()[ 0 ] ) .Select(t => t.GetGenericArguments()[0]);
.ToList();
} }
public IEnumerable<Pair<string, Type>> GetInitKeys() public IEnumerable<Pair<string, Type>> GetInitKeys()

View File

@@ -19,10 +19,15 @@ namespace OpenRA
{ {
public class ObjectCreator public class ObjectCreator
{ {
Pair<Assembly, string>[] assemblies; readonly Cache<string, Type> typeCache;
readonly Cache<Type, ConstructorInfo> ctorCache;
readonly Pair<Assembly, string>[] assemblies;
public ObjectCreator(Manifest manifest) public ObjectCreator(Manifest manifest)
{ {
typeCache = new Cache<string, Type>(FindType);
ctorCache = new Cache<Type, ConstructorInfo>(GetCtor);
// All the core namespaces // All the core namespaces
var asms = typeof(Game).Assembly.GetNamespaces() // Game var asms = typeof(Game).Assembly.GetNamespaces() // Game
.Select(c => Pair.New(typeof(Game).Assembly, c)) .Select(c => Pair.New(typeof(Game).Assembly, c))
@@ -48,23 +53,18 @@ namespace OpenRA
public T CreateObject<T>(string className, Dictionary<string, object> args) public T CreateObject<T>(string className, Dictionary<string, object> args)
{ {
var type = FindType(className); var type = typeCache[className];
if (type == null) if (type == null)
{ {
MissingTypeAction(className); MissingTypeAction(className);
return default(T); return default(T);
} }
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; var ctor = ctorCache[type];
var ctors = type.GetConstructors(flags) if (ctor == null)
.Where(x => x.HasAttribute<UseCtorAttribute>()).ToList();
if (ctors.Count == 0)
return (T)CreateBasic(type); return (T)CreateBasic(type);
else if (ctors.Count == 1)
return (T)CreateUsingArgs(ctors[0], args);
else else
throw new InvalidOperationException("ObjectCreator: UseCtor on multiple constructors; invalid."); return (T)CreateUsingArgs(ctor, args);
} }
public Type FindType(string className) public Type FindType(string className)
@@ -74,6 +74,21 @@ namespace OpenRA
.FirstOrDefault(t => t != null); .FirstOrDefault(t => t != null);
} }
public ConstructorInfo GetCtor(Type type)
{
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance;
var ctors = type.GetConstructors(flags).Where(x => x.HasAttribute<UseCtorAttribute>());
using (var e = ctors.GetEnumerator())
{
if (!e.MoveNext())
return null;
var ctor = e.Current;
if (!e.MoveNext())
return ctor;
}
throw new InvalidOperationException("ObjectCreator: UseCtor on multiple constructors; invalid.");
}
public object CreateBasic(Type type) public object CreateBasic(Type type)
{ {
return type.GetConstructor(new Type[0]).Invoke(new object[0]); return type.GetConstructor(new Type[0]).Invoke(new object[0]);

View File

@@ -14,18 +14,17 @@ using System.Collections.Generic;
namespace OpenRA.Primitives namespace OpenRA.Primitives
{ {
public class Cache<T, U> : IEnumerable<KeyValuePair<T, U>> public class Cache<T, U> : IReadOnlyDictionary<T, U>
{ {
Dictionary<T, U> hax; readonly Dictionary<T, U> cache;
Func<T,U> loader; readonly Func<T, U> loader;
public Cache(Func<T,U> loader, IEqualityComparer<T> c) public Cache(Func<T, U> loader, IEqualityComparer<T> c)
{ {
hax = new Dictionary<T, U>(c);
if (loader == null) if (loader == null)
throw new ArgumentNullException("loader"); throw new ArgumentNullException("loader");
this.loader = loader; this.loader = loader;
cache = new Dictionary<T, U>(c);
} }
public Cache(Func<T, U> loader) public Cache(Func<T, U> loader)
@@ -36,18 +35,17 @@ namespace OpenRA.Primitives
get get
{ {
U result; U result;
if (!hax.TryGetValue(key, out result)) if (!cache.TryGetValue(key, out result))
hax.Add(key, result = loader(key)); cache.Add(key, result = loader(key));
return result; return result;
} }
} }
public bool ContainsKey(T key) { return cache.ContainsKey(key); }
public IEnumerator<KeyValuePair<T, U>> GetEnumerator() { return hax.GetEnumerator(); } public bool TryGetValue(T key, out U value) { return cache.TryGetValue(key, out value); }
public int Count { get { return cache.Count; } }
public ICollection<T> Keys { get { return cache.Keys; } }
public ICollection<U> Values { get { return cache.Values; } }
public IEnumerator<KeyValuePair<T, U>> GetEnumerator() { return cache.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public IEnumerable<T> Keys { get { return hax.Keys; } }
public IEnumerable<U> Values { get { return hax.Values; } }
} }
} }

View File

@@ -25,8 +25,8 @@ namespace OpenRA
{ {
int Count { get; } int Count { get; }
TValue this[TKey key] { get; } TValue this[TKey key] { get; }
IEnumerable<TKey> Keys { get; } ICollection<TKey> Keys { get; }
IEnumerable<TValue> Values { get; } ICollection<TValue> Values { get; }
bool ContainsKey(TKey key); bool ContainsKey(TKey key);
bool TryGetValue(TKey key, out TValue value); bool TryGetValue(TKey key, out TValue value);
@@ -68,9 +68,9 @@ namespace OpenRA
public TValue this[TKey key] { get { return dict[key]; } } public TValue this[TKey key] { get { return dict[key]; } }
public IEnumerable<TKey> Keys { get { return dict.Keys; } } public ICollection<TKey> Keys { get { return dict.Keys; } }
public IEnumerable<TValue> Values { get { return dict.Values; } } public ICollection<TValue> Values { get { return dict.Values; } }
#endregion #endregion
#region IEnumerable implementation #region IEnumerable implementation

View File

@@ -80,9 +80,13 @@ namespace OpenRA.Mods.RA
// Add buildables that have a build limit set and are not already in the list // Add buildables that have a build limit set and are not already in the list
player.World.ActorsWithTrait<Buildable>() player.World.ActorsWithTrait<Buildable>()
.Where(a => a.Actor.Info.Traits.Get<BuildableInfo>().BuildLimit > 0 && !a.Actor.IsDead() && a.Actor.IsInWorld && a.Actor.Owner == player && ret.Keys.All(k => k != a.Actor.Info.Name)) .Where(a =>
.ToList() a.Actor.Owner == player &&
.ForEach(b => ret[b.Actor.Info.Name].Add(b.Actor)); a.Actor.IsInWorld &&
!a.Actor.IsDead() &&
!ret.ContainsKey(a.Actor.Info.Name) &&
a.Actor.Info.Traits.Get<BuildableInfo>().BuildLimit > 0)
.Do(b => ret[b.Actor.Info.Name].Add(b.Actor));
return ret; return ret;
} }
@@ -111,17 +115,17 @@ namespace OpenRA.Mods.RA
bool HasPrerequisites(Cache<string, List<Actor>> ownedPrerequisites) bool HasPrerequisites(Cache<string, List<Actor>> ownedPrerequisites)
{ {
return prerequisites.All(p => !(p.Replace("~", "").StartsWith("!") ^ !ownedPrerequisites.Keys.Contains(p.Replace("!", "").Replace("~", "")))); return prerequisites.All(p => !(p.Replace("~", "").StartsWith("!") ^ !ownedPrerequisites.ContainsKey(p.Replace("!", "").Replace("~", ""))));
} }
bool IsHidden(Cache<string, List<Actor>> ownedPrerequisites) bool IsHidden(Cache<string, List<Actor>> ownedPrerequisites)
{ {
return prerequisites.Any(prereq => prereq.StartsWith("~") && (prereq.Replace("~", "").StartsWith("!") ^ !ownedPrerequisites.Keys.Contains(prereq.Replace("~", "").Replace("!", "")))); return prerequisites.Any(prereq => prereq.StartsWith("~") && (prereq.Replace("~", "").StartsWith("!") ^ !ownedPrerequisites.ContainsKey(prereq.Replace("~", "").Replace("!", ""))));
} }
public void Update(Cache<string, List<Actor>> ownedPrerequisites) public void Update(Cache<string, List<Actor>> ownedPrerequisites)
{ {
var hasReachedLimit = limit > 0 && ownedPrerequisites.Keys.Contains(Key) && ownedPrerequisites[Key].Count >= limit; var hasReachedLimit = limit > 0 && ownedPrerequisites.ContainsKey(Key) && ownedPrerequisites[Key].Count >= limit;
// The '!' annotation inverts prerequisites: "I'm buildable if this prerequisite *isn't* met" // The '!' annotation inverts prerequisites: "I'm buildable if this prerequisite *isn't* met"
var nowHasPrerequisites = HasPrerequisites(ownedPrerequisites) && !hasReachedLimit; var nowHasPrerequisites = HasPrerequisites(ownedPrerequisites) && !hasReachedLimit;
var nowHidden = IsHidden(ownedPrerequisites); var nowHidden = IsHidden(ownedPrerequisites);