mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-29 15:11:53 +08:00
* GC reduction via IPooledEncodeable activator pooling
Add opt-in activator-level pooling for IEncodeable instances decoded
during V2 subscription publish dispatch. The design replaces the
earlier IAllocator-on-IServiceMessageContext approach (which had a
cross-contamination hazard for non-publish decodes) with a clean
factory-based model where pooling lives on the encodeable activator.
New public API:
- IPooledEncodeable : IEncodeable - opt-in marker with Reuse() method
- PooledEncodeableType<T> : EncodeableType<T> - activator base with
BoundedObjectPool<T> (lock-free, no new package dependency)
- ISubscriptionManager.PoolNotifications { get; set; }
- ManagedSessionOptions.PoolNotifications { get; init; }
- ManagedSessionBuilder.WithPoolNotifications(bool)
Source-gen:
- All concrete non-abstract structure types (including service
request/response types) are generated with PooledEncodeableType<T>
activators and IPooledEncodeable.Reuse() bodies by default
- Derived types use 'new' to hide inherited Reuse/ClearPooledSentinel
so each concrete type routes through its own activator's pool
- No changes to external design XML files (UA Core Services.xml etc.)
- poolable selection is hardcoded in the generator
Benchmarks (MonitoredItemNotification, 1000 ops):
- Allocations: 128 KB -> 408 B (~315x reduction)
- Gen0 collections: 29.75 -> 0.09 per 1000 ops
No interface changes to IEncodeable, IDecoder, IServiceMessageContext,
or ISubscriptionNotificationHandler. Handlers retain by copy when
pooling is enabled.
Call IPooledEncodeable.Reuse() on decoded IServiceRequest and
IServiceResponse after the server is done with them:
- TcpTransportListener.OnRequestReceivedAsync: reuse both request
and response in a finally block after ProcessRequestAsync +
SendResponse complete
- EndpointBase.InvokeServiceAsync (HTTPS path): same pattern after
ProcessRequestAsync + BinaryEncoder.EncodeMessage
Walk acknowledged, overflow-evicted, and disposed NotificationMessage
objects and call IPooledEncodeable.Reuse() on their notification
payloads (MonitoredItemNotification, DataChangeNotification,
EventFieldList, EventNotificationList).
Insertion points in Subscription.cs:
- Acknowledge(): after RemoveAt from m_sentMessages
- InnerPublish() overflow: before RemoveRange/Clear on m_sentMessages
- Dispose(): before m_sentMessages.Clear()
- InnerPublish() unsent-message overflow: before RemoveRange on
the messages list
Replace new T() with XxxActivator.Instance.CreateInstance() for all
server-side notification payload types so instances are rented from
the activator pool instead of heap-allocated:
New static helper ReuseNotificationPayloads() walks the
NotificationMessage.NotificationData, extracts DataChangeNotification
and EventNotificationList via TryGetValue, iterates their items, and
calls Reuse() on each pooled payload.
77 lines
3.0 KiB
C#
77 lines
3.0 KiB
C#
/* ========================================================================
|
|
* Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
|
|
*
|
|
* OPC Foundation MIT License 1.00
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use,
|
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following
|
|
* conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* The complete license agreement can be found here:
|
|
* http://opcfoundation.org/License/MIT/1.00/
|
|
* ======================================================================*/
|
|
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Opc.Ua.Client.Subscriptions
|
|
{
|
|
/// <summary>
|
|
/// Message acknoledgement queue
|
|
/// </summary>
|
|
internal interface IMessageAckQueue
|
|
{
|
|
/// <summary>
|
|
/// Subscriptions queue acknoledgements for completed
|
|
/// notifications as soon as they are dispatched / handled.
|
|
/// </summary>
|
|
/// <param name="ack"></param>
|
|
/// <param name="ct"></param>
|
|
/// <returns></returns>
|
|
ValueTask QueueAsync(SubscriptionAcknowledgement ack,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Complete subscription
|
|
/// </summary>
|
|
/// <param name="subscriptionId"></param>
|
|
/// <param name="ct"></param>
|
|
/// <returns></returns>
|
|
ValueTask CompleteAsync(uint subscriptionId,
|
|
CancellationToken ct = default);
|
|
|
|
/// <summary>
|
|
/// Notify the queue/manager that the subscription's state has
|
|
/// changed (created, modified, etc.) and the publish controller
|
|
/// should re-evaluate worker counts and resume publishing.
|
|
/// </summary>
|
|
void Update();
|
|
|
|
/// <summary>
|
|
/// Whether the V2 subscription dispatcher should call
|
|
/// <see cref="IPooledEncodeable.Reuse"/> on notification payload
|
|
/// instances after the handler returns. The value reflects the
|
|
/// current <see cref="ISubscriptionManager.PoolNotifications"/>
|
|
/// setting; toggling it on the manager takes effect on the next
|
|
/// dispatch.
|
|
/// </summary>
|
|
bool PoolNotifications { get; }
|
|
}
|
|
}
|