紧密循环 - 100%磁盘,四核CPU @ 25%使用率,磁盘写入速度仅15MB

时间:2014-11-20 17:19:41

标签: multithreading performance get-event-store

我有一个紧凑的循环,它贯穿一大堆购物车,它们本身包含大约10个事件事件对象,并通过中间存储库(jOliver通用域重新连接GetEventStore.com)将它们写入磁盘:

// create ~200,000 carts, each with ~5 events
List<Cart> testData = TestData.GenerateFrom(products);
foreach (var cart in testData)
{
    count = count + (cart as IAggregate).GetUncommittedEvents().Count;
    repository.Save(cart);
}

我看到磁盘显示为100%,但整个过程为“低”(15MB /秒,每秒约5,000个事件)为什么会这样,我能想到的是:

  1. 由于这是单线程,25%的CPU使用率实际上意味着我所使用的1核心的100%(任何方式显示我的应用程序在Visual Studio中运行的特定核心)?

  2. 我是受I / O还是CPU限制的?如果我为每个CPU创建一个自己的线程池,我可以期待更好的性能吗?

  3. 为什么我能以~120MB /秒的速度复制文件,但我的应用程序只能获得15MB /秒的吞吐量?这是由于大量较小数据包的写入大小?

  4. 我错过了什么?

    throughput

    我使用的代码来自geteventstore docs / blog:

    public class GetEventStoreRepository : IRepository
    {
        private const string EventClrTypeHeader = "EventClrTypeName";
        private const string AggregateClrTypeHeader = "AggregateClrTypeName";
        private const string CommitIdHeader = "CommitId";
        private const int WritePageSize = 500;
        private const int ReadPageSize = 500;
    
        IStreamNamingConvention streamNamingConvention;
    
        private readonly IEventStoreConnection connection;
        private static readonly JsonSerializerSettings serializerSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.None };
    
    
        public GetEventStoreRepository(IEventStoreConnection eventStoreConnection, IStreamNamingConvention namingConvention)
        {
            this.connection = eventStoreConnection;
            this.streamNamingConvention = namingConvention;
        }
    
        public void Save(IAggregate aggregate)
        {
            this.Save(aggregate, Guid.NewGuid(), d => { });
    
        }
    
        public void Save(IAggregate aggregate, Guid commitId, Action<IDictionary<string, object>> updateHeaders)
        {
            var commitHeaders = new Dictionary<string, object>
                    {
                        {CommitIdHeader, commitId},
                        {AggregateClrTypeHeader, aggregate.GetType().AssemblyQualifiedName}
                    };
            updateHeaders(commitHeaders);
    
            var streamName = this.streamNamingConvention.GetStreamName(aggregate.GetType(), aggregate.Identity);
            var newEvents = aggregate.GetUncommittedEvents().Cast<object>().ToList();
            var originalVersion = aggregate.Version - newEvents.Count;
            var expectedVersion = originalVersion == 0 ? ExpectedVersion.NoStream : originalVersion - 1;
            var eventsToSave = newEvents.Select(e => ToEventData(Guid.NewGuid(), e, commitHeaders)).ToList();
    
            if (eventsToSave.Count < WritePageSize)
            {
                this.connection.AppendToStreamAsync(streamName, expectedVersion, eventsToSave).Wait();
            }
            else
            {
                var startTransactionTask = this.connection.StartTransactionAsync(streamName, expectedVersion);
                startTransactionTask.Wait();
                var transaction = startTransactionTask.Result;
    
                var position = 0;
                while (position < eventsToSave.Count)
                {
                    var pageEvents = eventsToSave.Skip(position).Take(WritePageSize);
                    var writeTask = transaction.WriteAsync(pageEvents);
                    writeTask.Wait();
                    position += WritePageSize;
                }
    
                var commitTask = transaction.CommitAsync();
                commitTask.Wait();
            }
    
            aggregate.ClearUncommittedEvents();
        }
    
        private static EventData ToEventData(Guid eventId, object evnt, IDictionary<string, object> headers)
        {
            var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(evnt, serializerSettings));
    
            var eventHeaders = new Dictionary<string, object>(headers)
                    {
                        {
                            EventClrTypeHeader, evnt.GetType().AssemblyQualifiedName
                        }
                    };
            var metadata = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(eventHeaders, serializerSettings));
            var typeName = evnt.GetType().Name;
    
            return new EventData(eventId, typeName, true, data, metadata);
        }
    }
    

1 个答案:

答案 0 :(得分:2)

在评论中已经部分提到了,但为了加强这一点,因为你在所提到的代码中完全使用单线程(虽然你使用异步,你只是在等待它们,所以有效地工作同步)你正在遭受痛苦来自上下文切换的延迟和开销以及来回的EventStore协议。要么真的去异步路由,要么避免等待异步线程而是并行化它(EventStore喜欢并行化,因为它可以批量多次写入)或者自己进行批处理,例如一次发送20个事件。