Bug #9060
Cache oftenly used values that are immutable
90%
Related issues
History
#1 Updated by Alexandru Lungu almost 2 years ago
- Subject changed from Cache oftenly used literals that are immutable to Cache oftenly used values that are immutable
- Assignee set to Ioana-Cristina Prioteasa
As part of the profiling work, the GC activity was detected as being high. This may be caused by the high number of allocations / deallocations of objects.
To reduce this, we can identify the immutable converted values and cache them. This was done already for logical, implementing logical.TRUE, logical.FALSE and logical.UNKNOWN (that can be retrieved using logical.of).
The point is to extend this technique to other types like: integer (cache first N integers in a static array), characters (cache EMPTY character and one-letter ASCII characters) and maybe others.
However, note that only the immutable occurrences can be cached: the result of MathOps, DynamicOps, etc. Variables themselves or other writable values can't be cached. Thus, we should reuse the principle from logical.logicalConstant to enforce immutability.
We don't have an exact measure of such improvement, so we should start with that: count how many such instances are created from immutable contexts.
#2 Updated by Ioana-Cristina Prioteasa over 1 year ago
- Status changed from New to WIP
In integer.java, I implemented a mechanism to count the number of instances created and saved the results to a CSV file. This was performed during server startup and one performance test to analyze the instance creations and identify the most frequently used integers.
| Value | Count |
|---|---|
| Unknown | 174014 |
| 0 | 410683 |
| 5 | 187010 |
| 1 | 128977 |
| 4 | 55694 |
| 2 | 38967 |
| 3 | 34061 |
| 20 | 29244 |
| 80 | 23673 |
| 170 | 23077 |
| 89 | 20577 |
| 10 | 20460 |
| 38 | 17940 |
| 8 | 15333 |
| 30 | 13681 |
| 236 | 12496 |
| 40 | 12481 |
| 275 | 12449 |
| 63 | 11136 |
| 21 | 10779 |
| -300 | 9793 |
| 7 | 9124 |
| 6 | 8530 |
| 9 | 8481 |
| 60 | 8166 |
| 50 | 7584 |
Notably, all integers from 0 to 10 appear in the top 25 most used values, suggesting that caching these integers would be an effective starting point for this optimization.
I also attempted to count the integers returned by MathOps and DynamicOps using the same test scenario, but this yielded no results. I am pretty unsure how I could determine what integers are coming from an immutable context.
#3 Updated by Eric Faulhaber over 1 year ago
- reviewer Constantin Asofiei added
#4 Updated by Constantin Asofiei over 1 year ago
Please check every usage from p2j.oo, of MathOps and DynamicOps, so there is nothing like, for example, integer i1 = MathOps.plus instead of i1.assign(MathOps.plus).
#5 Updated by Ioana-Cristina Prioteasa over 1 year ago
- reviewer deleted (
Constantin Asofiei)
- Added
integerConstantand cached the first 10 integers. - Added
int64Constantand cached the first 10 int64 values. - Provided support for the above.
- Utilized
integer.of,int64.of,logical.ofandcharacter.ofin immutable contexts for performance improvement.
I logged the source of the most frequently used integers for integer.of, and applied caching where possible. In a performance test on a big customer application, this resulted in the use of 580k cached integers at server startup and an additional 42k cached integers during the test.
My plan is to apply this method to identify the most commonly used int64, logical and character constants and cache them, increasing our performance gains. For characters, I'll need to be selective to avoid overloading the cache, focusing on scenarios with limited possibilities.
Currently, the integer cache size is set to 10. I'll measure performance across different sizes to determine the optimal value. Additionally, this setting could be made configurable in directory.xml.
I think the changes so far are ready for review to ensure I'm on the right track.
Constantin, please review 9060a, revisions 15659-15661.
#6 Updated by Ioana-Cristina Prioteasa over 1 year ago
- version_resolved set to cons
- reviewer Constantin Asofiei added
Constantin Asofiei wrote:
Please check every usage from
p2j.oo, ofMathOpsandDynamicOps, so there is nothing like, for example,integer i1 = MathOps.plusinstead ofi1.assign(MathOps.plus).
I will assess this next, thank you.
#7 Updated by Ioana-Cristina Prioteasa over 1 year ago
- version_resolved deleted (
cons)
#8 Updated by Ioana-Cristina Prioteasa over 1 year ago
I found multiple occurences of = MathOps. in our code.
In TotalAccumulator.accumulate:
public void accumulate(BaseDataType datum)
{
total = MathOps.plus(total, (NumberType) datum);
}
Similar in
AverageAccumulator.In
Operators there are a few uses similar to:
int64 mod = MathOps.modulo(new int64(a), new decimal(b));
return mod.isUnknown() ? null : mod.longValue();
In
ExpressionEvaluator.evaluate there are some cases similar to this:
if (op1 instanceof NumberType)
{
r = MathOps.plus((NumberType) op1, (NumberType) op2);
}
//more code
return r;
In
ToClause.incrementAndTest:
@// get the current loop control value
val = var.get();
// calculate the new loop control value
if (num)
{
NumberType number = (NumberType) val;
val = MathOps.plus(number, factor);
}
else if (val instanceof datetime) // datetime-tz also
{
datetime dd = (datetime) val;
val = datetime.plusMillis(dd, factor);
}
else if (val instanceof date)
{
date dd = (date) val;
val = date.plusDays(dd, factor);
}
// else some incorrect looping datatype, probably should throw an exception
// update the loop control var
var.set(val);
In total I found the above construct about 15 times, but = TextOps. appears closer to 100 times and = CompareOps. again about 15 times. I can go thru and change these to an assign construct, but for example here I don t think it is necessary as the integer is not used int the next steps.
int64 mod = MathOps.modulo(new int64(a), new decimal(b)); return mod.isUnknown() ? null : mod.longValue();
Should I only change to assign in the places where the value is returned or used in the next lines?
#9 Updated by Constantin Asofiei over 1 year ago
As long as the Java variable assigned to that reference of the i.e. integerConstant is using it in a read-only way, is OK to leave it as-is. But it must not be returned to the application runtime, it should stay in the FWD runtime.
#10 Updated by Ioana-Cristina Prioteasa over 1 year ago
Seems pretty difficult to determine that for sure in all use cases. Using assign is the better way to go right?
It may be that I will spend more time determining if the value is used only in read only than just using assign.
#11 Updated by Constantin Asofiei over 1 year ago
Focus on what is obvious. We can always come back to something which stands up in profiling.
#12 Updated by Ioana-Cristina Prioteasa over 1 year ago
Committed on 9060a rev 15662, 15663 multiple uses of logical and int64 constants.
Tested on a large customer application, where during server startup, approximately 250k logical and 25k int64 instances are retrieved from cache. During a performance test run, an additional 1,000k logical and 250k int64 instances are reused.
#13 Updated by Ioana-Cristina Prioteasa over 1 year ago
Committed on 9060a rev 15664 multiple uses of character constants. These are used where the value has limited options.
Tested on a large customer application and during server startup we reuse 160k character.UNKNOWN, 60k character.EMPTY_STRING and 90k values retrieved from the cache. At this step the cache contains 17 values.
Then, during a performance test run we reuse an extra 5k character.UNKNOWN and 200k values retrieved from the cache. After this step the cache contains 31 values.
I am unsure if it is ok to use character.of in LegacyLogManager.getLogFileName() - this contains customer information but in theory is has a limited pool of values and it is used a lot of times, almost 60k times.
#14 Updated by Constantin Asofiei over 1 year ago
Ioana-Cristina Prioteasa wrote:
I am unsure if it is ok to use
character.ofinLegacyLogManager.getLogFileName()- this contains customer information but in theory is has a limited pool of values and it is used a lot of times, almost 60k times.
Go ahead and cache these. I think we should add a hard-limit to the character.of() cache to i.e. 2048 or something like this, for runtime.
#15 Updated by Ioana-Cristina Prioteasa over 1 year ago
Constantin Asofiei wrote:
Go ahead and cache these. I think we should add a hard-limit to the
character.of()cache to i.e. 2048 or something like this, for runtime.
Got it, i will implement that next.
#16 Updated by Ioana-Cristina Prioteasa over 1 year ago
Committed to 9060a rev. 15665: limited character cache size, added directory.xml options to determine the character, integer and int64 cache sizes and some quick fixes.
What remains to be done here is checking where construct like integer i1 = MathOps.plus appear. I will move to that.
#17 Updated by Alexandru Lungu over 1 year ago
- static cache of configurable size.
- it is an array where Nth position stores the int value for N.
- this can be lazily initiated and the collection size is bound.
- dynamic cache of configurable size (lets say N).
- it is an array with a bound size.
- if an integer X is not in the static cache, then look into the dynamic cache. For this, compute X % N to gain a quick hash of X < N. PS: maybe use the absolute value to also cover negative values.
- if there is something at the
X % Nposition, then check if what is found is the right instance (e.g.dynamic_cache[x % n].value() == x % n).- if this holds, there you go ... cache hit
- if not, then create a new instance and override the one from
dynamic_cache[x % n].
- if there is nothing at the
X % Nposition, then ... cache miss. create a new instance and store it indynamic_cache[x % n]
The gain of the dynamic cache: we still cache recently used large integers.
The downside of the dynamic cache: there is a low chance (depending on the collection size and collision probability) to trash the dynamic_cache on a very specific position.
#18 Updated by Ioana-Cristina Prioteasa over 1 year ago
- File search_results.txt
added
Ioana-Cristina Prioteasa wrote:
What remains to be done here is checking where construct like
integer i1 = MathOps.plusappear. I will move to that.
I created a script that scanned our codebase for usages of constructs like = MathOps., for MathOps, CompareOps, DynamicOps, EnvironmentOps and TextOps. I have attached the output of that search. I manually reviewed all occurrences.
Initially, I committed a change related to this in rev. 15666, but later uncommitted it in rev. 15668, as I realized the = usage was incorrect in that case. After reviewing all instances, I confirmed that none require an explicit call to assign.
In revision 15667, I updated CacheManager to support the creation of an array cache and an IdentityHashMap cache. I also made CacheManager.getCacheSize public to allow retrieving the maximum configured size for the character cache.
My next step is to test a large customer GUI application and also evaluate performance to assess the impact of 9060. Once we have a clearer understanding, we can consider implementing the dynamic cache approach suggested by Alex in #9060-17.
#19 Updated by Ioana-Cristina Prioteasa over 1 year ago
Tested a big GUI customer application and a NPE showed up, caused by the use of CacheManager.character.of was called before CacheManger.initialize, causing the cache in character to be null.
I think CacheManager was initially designed to initialize the persistence caches, as it's initialize method is called in Persistence.initialize. Since then, it's scope seemed to extend beyond persistence. I think it's a good idea for it to be refactored, add a server hook to initialize the CacheManager and modify it's configuration path from directory.xml from server/default/persistence to server/default.
Does this sound like a good approach?
After a quick fix for the NPE, the tested application showed no regressions, this is good news!
#20 Updated by Eric Faulhaber over 1 year ago
Ioana-Cristina Prioteasa wrote:
...and modify it's configuration path from
directory.xmlfromserver/default/persistencetoserver/default.
Does this sound like a good approach?
It makes sense, since the CacheManager is no longer used only for persistence. However, there are applications in production and testing which this change will break (well, cache configuration will be lost, which can have unexpected performance and memory consequences, as defaults are used).
To avoid using the hard-coded cache defaults, I think we need to prefer these new directory paths, but honor the old ones if they're found (and not overridden), logging a warning to alert admins that these settings are deprecated. Any new caches (like the one for this task) will only be read from the new location.
The cache configuration documentation will need updates as well (including a note about the deprecation of the old location).
#21 Updated by Ioana-Cristina Prioteasa over 1 year ago
Eric Faulhaber wrote:
To avoid using the hard-coded cache defaults, I think we need to prefer these new directory paths, but honor the old ones if they're found (and not overridden), logging a warning to alert admins that these settings are deprecated. Any new caches (like the one for this task) will only be read from the new location.
The initialization of cache sizes is handled in CacheManager, which searches for all containers inside /server/default/persistent/cache-sizes/ in the directory and builds a map of cache sizes based on all configurations found. When an actual cache is initialized in a specific class, it looks up its configured size in this map — if not found, it falls back to a default value.
The challenge here is that CacheManager does not inherently differentiate between old and newly added caches. Given this, the best approach seems to be:
- First, initialize from
/server/default/persistence. - Log a warning for each cache size found in this deprecated location. - Then, read from
/server/default. - Any values found here will override those from the deprecated location.
At the end of this process, we will have cache sizes for all defined caches while also providing clear deprecation warnings for any values retrieved from /server/default/persistence.
#22 Updated by Alexandru Lungu over 1 year ago
The challenge here is that CacheManager does not inherently differentiate between old and newly added caches. Given this, the best approach seems to be:
I am not quite sure what you mean by old and new. Each time you demand a new cache from the CacheManager it will oblige, so I would say that these are always "new caches".
#23 Updated by Eric Faulhaber over 1 year ago
Alexandru Lungu wrote:
I am not quite sure what you mean by old and new.
"Old" (deprecated) path: /server/default/persistence/cache-sizes/
"New" path: /server/default/cache-sizes/
#24 Updated by Alexandru Lungu over 1 year ago
Got it - good approach in #9060-21 then.
#25 Updated by Dănuț Filimon over 1 year ago
I also agree with #9060-21, but make sure you update the wiki to reflect this change https://proj.goldencode.com/projects/p2j/wiki/Database_Configuration#Cache-Sizes, including the warnings when using the deprecated path.
#26 Updated by Constantin Asofiei over 1 year ago
Dănuț Filimon wrote:
I also agree with #9060-21, but make sure you update the wiki to reflect this change https://proj.goldencode.com/projects/p2j/wiki/Database_Configuration#Cache-Sizes, including the warnings when using the deprecated path.
We also need to migrate these paths in existing applications - maybe we can do this auto-magically, at FWD server startup?
#27 Updated by Ioana-Cristina Prioteasa over 1 year ago
integer- 1024 (integer with values 0-1021 cached)int64- 1024 (int64 with values 0-1021 cached)character- 2048 (the character cache cannot exceed 2048)
The overall execution time decreased by 2%, but certain steps in the performance test showed improvements of up to 13%. This is a significant result and the impact of these changes is clearly visible.
I will work next on the CacheManager and the dynamic cache Alex explained in #9060-17, maybe we can increase the impact of this even more.
#28 Updated by Ioana-Cristina Prioteasa over 1 year ago
- % Done changed from 0 to 90
- Status changed from WIP to Review
- rev. 15669: Refactored
CacheManager. - rev. 15670: Added dynamic cache to
integerandint64.
I am working right now on a post to help tracking the changes, but as for now 9060a is ready for review.
Moved to 90%, the default cache sizes can be fine tuned.
#29 Updated by Ioana-Cristina Prioteasa over 1 year ago
Main implementation and changes:
com.goldencode.p2j.util.int64- Addedint64Constant, static and dynamic cache using theCacheManager.com.goldencode.p2j.util.integer- AddedintegerConstant, static and dynamic cache usingCacheManagercom.goldencode.p2j.util.character- Used theCacheManagerto instantiate the cache and added a hard limit to the size of the cache.com.goldencode.p2j.persist.dialect.P2JSQLServer2008Dialect- Added support for integer and int64 constants.com.goldencode.p2j.persist.hql.DataTypeHelper- Added support for integer and int64 constants.com.goldencode.p2j.persist.orm.types.TypeManager- Added support for integer and int64 constants.com.goldencode.p2j.util.BaseDataTypeFactory- Added support for integer and int64 constants.com.goldencode.p2j.util.DataSetSDOHelper- Added support for integer and int64 constants.com.goldencode.p2j.util.jobject- Added support for integer and int64 constants.com.goldencode.p2j.persist.CacheManager- added APIs for an array cache and an identity hash map cache and refactored to read the configuration first from/persistence/cache-size, register the sizes found there, but log a deprecated location warning and then check the new/cache-sizelocation, add everything new found there and override the deprecated values if necessary.com.goldencode.p2j.main.StandardServer- added a server hook to initialize theCacheManager.com.goldencode.p2j.persist.Persistence- removed the initialization of theCacheManager.
logicalConstant:
com.goldencode.p2j.oo.lang.LegacyClassinisAmethod.com.goldencode.p2j.persist.BufferFieldImplinliteralQuestionmethod.com.goldencode.p2j.persist.ConnectionManagerinconnectedImplmethod.com.goldencode.p2j.persist.DataSourceinaddSourceBufferImplmethod.com.goldencode.p2j.persist.DynamicConversionHelperininjectVariablemethod.com.goldencode.p2j.persist.DynamicTablesHelperingenerateSchemaAstmethod.com.goldencode.p2j.persist.P2JQueryingetNextmethod.com.goldencode.p2j.persist.PresorterinisFirstOfGroupmethod.com.goldencode.p2j.persist.RecordBufferindeletemethod.com.goldencode.p2j.persist.StaticTempTableincanUndo,clearandaddFieldToIndexmethods.com.goldencode.p2j.persist.TableMapperinisLegacyFieldMandatoryandloadFieldsmethods.com.goldencode.p2j.persist.TempTableBuilderinaddNewField, @addNewIndex,addFieldToIndex,tempTablePrepareImplcreateTableLikeImpl,addIndexLikeandparseIndexStringmethods.com.goldencode.p2j.persist.meta.MetadataManagerinpopulateTenant,childData,indexData,fieldDataandbuildmethods.com.goldencode.p2j.util.ArrayAssignerinassignMultiandassignSinglemethods.com.goldencode.p2j.util.ErrorManagerinisErrormethod.com.goldencode.p2j.util.LegacyLogManagerImplinwriteMessagemethod.com.goldencode.p2j.util.NumberTypeinsetNumericFormatmethod.com.goldencode.p2j.util.ProcedureManagerinaddSuperProcedureImplandisPersistentmethods.com.goldencode.p2j.util.SessionUtilsinisErrorStackTracemethod.com.goldencode.p2j.util.XDocumentImplinloadmethod.com.goldencode.p2j.util.XEntityImplingetChild,removeChildandremoveAttributemethods.com.goldencode.p2j.util.XNodeRefImplinnodeValueToMemptrandnormalizemethods.
characterConstant:
com.goldencode.p2j.persist.AbstractTempTableinnamespacePrefixmethod.com.goldencode.p2j.persist.RecordBufferindeletemethod.com.goldencode.p2j.persist.meta.MetadataManagerinaddTableToFile,fieldDataandbuildmethods.com.goldencode.p2j.util.EnvironmentOpsingetCurrentLanguage,getOperatingSystemandgetRuntimeOperatingSystemmethods.com.goldencode.p2j.util.HandleChaininnamemethod.com.goldencode.p2j.util.HandleResourceinresourceTypemethod.com.goldencode.p2j.util.LegacyLogManagerImplingetLogFileNamemethod.com.goldencode.p2j.util.NumberTypeingetNumericFormatandsetNumericFormatmethods.com.goldencode.p2j.util.dateingetDateOrdermethod.
integerConstant:
com.goldencode.p2j.persist.BufferFieldImplinextentmethod.com.goldencode.p2j.persist.BufferImplinnumFieldsmethod.com.goldencode.p2j.persist.DataSetinnumBuffersandbufferHandlemethods.com.goldencode.p2j.persist.QueryComponentinresolveArgmethod.com.goldencode.p2j.persist.meta.MetadataManagerinaddTableToFile,childData,indexData,fieldDataandbuildmethods.com.goldencode.p2j.util.AccessorWrapperingetmethod.com.goldencode.p2j.util.ArrayAssignerinresizeandlengthOfmethod.com.goldencode.p2j.util.DynamicOpsinplus,minusandlengthmethods.com.goldencode.p2j.util.TextOpsinbyteLength,indexOf,lastIndexOf,length,lookupandnumEntriesmethods.com.goldencode.p2j.util.ToClausein 2 constructors.com.goldencode.p2j.util.dateingetTimeZonemethod.com.goldencode.p2j.util.XEntityImplingetNumChildrenmethod.com.goldencode.p2j.util.XNodeRefImplingetLocalNamemethod.
int64Constant:
com.goldencode.p2j.persist.DynamicConversionHelperininjectVariablemethod.com.goldencode.p2j.persist.QueryComponentinresolveArgmethod.com.goldencode.p2j.persist.sequence.SequenceHandleringetResultValue,safeGetCurrentValueandsafeGetNextValuemethods.com.goldencode.p2j.util.ArrayAssignerinassignMulti,assignSingle,assignSingleUnknownandsubscriptHelpermethods.com.goldencode.p2j.util.BinaryDatainlengthmethod.com.goldencode.p2j.util.CompareOpsin_isEqual,isEqual,_isNotEqual,isNotEqual,_isGraterThan,isGraterThan,_isGreaterThanOrEqual,isGreaterThanOrEqual,_isLessThan,isLessThan,_isLessThanOrEqualandisLessThanOrEqualmethods.com.goldencode.p2j.util.DynamicOpsinplusandminusmethods.com.goldencode.p2j.util.MathOpsinabs,random,negate,plus,minus,multiplyandmodulomethods.com.goldencode.p2j.util.ToClausein 2 constructors.com.goldencode.p2j.util.dateinsecondsSinceMidnightandelapsedmethods.com.goldencode.p2j.util.datetimein 2 constructors.com.goldencode.p2j.util.datetimetzin 4 constructors.
Also committed on 9060a rev. 15671 an empty row deleted by accident and some history entry revisions.
The revisions that need review on the branch: 15659-15671.
Constantin, please review! Thanks.
#30 Updated by Constantin Asofiei over 1 year ago
CallParameterneeds the integer/int64/characterConstant checks- CacheManager.java
- this message
Please move it from /server/standard/persistence/cache-size to /server/default/cache-size.- keep in mind that you can have/server/default/paths. I think the message should be like/server/{default|<serverID>}/persistent/cache-sizepath - same for the second,@/server/{default|<serverID>}/.
- this message
character.java,int64.java,integer.java- there are some unused imports, please remove themcharacter- this needs to be insidesynchronized:if (CHARACTER_CONSTANTS.size() < CHARACTER_CACHE_LIMIT)int64.ofandinteger.of- do we need synchronization forDYNAMIC_CACHE?- please run javadoc and fix any issues, but only in the files you've already changed
#31 Updated by Ioana-Cristina Prioteasa over 1 year ago
- File CheckAssignments.java
added
Used Spoon to scan the source code for occurrences of assignments like = MathOps.. Below is the generated report:
==== MathOps ==== (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:992) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:1024) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:1047) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:1058) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:1069) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:1084) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:1088) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/AverageAccumulator.java:404) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/ToClause.java:3028) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/ToClause.java:3169) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/ToClause.java:3174) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/TotalAccumulator.java:394) ==== TextOps ==== (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/persist/BufferFieldImpl.java:848) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/persist/BufferImpl.java:9863) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/persist/DataSet.java:4644) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/persist/DataSet.java:4813) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/persist/FieldReference.java:1697) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/persist/Persistence.java:1133) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/persist/QueryWrapper.java:6922) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/persist/RecordBuffer.java:2593) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/persist/orm/OrmUtils.java:138) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/persist/orm/Validation.java:1304) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:966) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:977) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:996) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/ui/BrowseRow.java:182) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/ui/BrowseRow.java:185) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/ui/BrowseRow.java:188) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/ui/BrowseRow.java:189) ==== DynamicOps ==== ==== CompareOps ==== (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:913) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:921) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:929) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:937) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:946) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/uast/ExpressionEvaluator.java:956) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/ToClause.java:170) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/ToClause.java:3009) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/ToClause.java:3010) ==== EnvironmentOps ==== (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/ui/ColorSpec.java:514) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/ui/ColorSpec.java:519) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/ui/ColorSpec.java:524) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/ui/ColorTable.java:2100) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/ui/Keyboard.java:2833) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/ui/chui/ThinClient.java:3954) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/FileSystemOps.java:1733) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/SourceNameMapper.java:369) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/SourceNameMapper.java:2212) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/SourceNameMapper.java:4425) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/SourceNameMapper.java:4444) (/home/icp/projects/branches/9060a/src/com/goldencode/p2j/util/memptr.java:210)
The good part is that none of the occurrences are in the
oo package. But these could cause issues further down the line if they are assigned.I attached the code I used to generate this.
#32 Updated by Alexandru Lungu over 1 year ago
- 187k
character.<init>(String)- 53k from
Text.assign - 36k from
CompareOps.isEqual/_isNotEqual/_isEqual - 12k from
TypeFactory.character
- 53k from
- 101k
character.<init>(character)- 96k from
character.duplicate(BDT.copy, RAQ.resolveArg, - 4k from
GenericFrame.parseValue
- 96k from
- 224k
character.<init>(String, boolean, boolean>- 101k from
TextOps.instantiate(toUpperCaseImpl, trimImpl, substringImpl, etc.) - 101k from
Record._getCharacter
- 101k from
- 88k
integer.<init>(Number)- 53k from
Record._getInteger - 29k from
UserTableStatUpdater.stat
- 53k from
- 81k
integer.<init>(int)- 20k from
TypeFactory.integer - 15k from
TextOps.numEntries - 0.5k from
FontTable.getTextWidthPixels - 0.2k from
TextOps.lookup - 0.1k from
FrameWidget.getDown
- 20k from
- 92k
int64.<init>()- mostly from integer c'tors
- 546 from
FontTable.getTextWidthPixels
- 26k
character.<init>()- mostly from run-time
- 3.8k from
StringFormat.fromVar4FieldValue - 0.5k from
StringFormat.fromVar
- 38k
int64.<init>()- 20k from
CompareOps.isEqual - 9.4k from
MathOps.minus - 3.9k from
CompareOps._isEqual - 1.9k from
CompareOps._isGreaterThanOrEqual
- 20k from
- 4k
decimal.<init>(long)- 4.2k from
MathOps.plus
- 4.2k from
- 4.2k
decimal.<init>(BDT)- 3.7k from
CompareOps.lessThanOrEqual - 0.4k from
CompareOps.equals
- 3.7k from
- 17.5k
logical.<init>(boolean)- 10k from
P2JQuery.getNext() - 5.6k from
TempTableBuilder.addNewField - 0.5k from
HandleOps.canQuery
- 10k from
- 11k
logical.<init>(Boolean)- 11k from
Record._isLogical
- 11k from
#33 Updated by Constantin Asofiei over 1 year ago
Ioana, from the list you posted, I think the only part of concern is:
(/home/icp/projects/branches/9060a/src/com/goldencode/p2j/persist/FieldReference.java:1697)
but the call result = TextOps.toUpperCase(TextOps.rightTrim(c, " ")); will always return a new instance, is not using character.of.
Last commit in 9060a is from Feb 14 - please commit latest changes so we can get this into testing.
As a side note: in the spoon code you posted, you can further filter this for methods which return a BaseDataType; no need to change for now, just an idea if the list is in the 1000's of lines, then always look to reduce them, if possible.
#34 Updated by Ioana-Cristina Prioteasa over 1 year ago
Constantin Asofiei wrote:
CallParameterneeds the integer/int64/characterConstant checks
CallParameter contains the characterConstant changes and for int and int64 there is this check if (int64.class.isAssignableFrom(type)). I think this is fine?
The rest of the review is addressed and committed in 9060a rev. 15672.
#35 Updated by Constantin Asofiei over 1 year ago
Thanks, looks good. Lets get this into runtime testing.
#36 Updated by Alexandru Lungu over 1 year ago
I updated my list in #9060-32 (now is complete).
#37 Updated by Constantin Asofiei over 1 year ago
Alexandru Lungu wrote:
I updated my list in #9060-32 (now is complete).
TypeFactory instances can't be immutable/constants. The Record methods like Record._isLogical will be worked in a second phase.
Ioana: can you double-check Alexandru's list, to what extent this is covered by 9060a?
#38 Updated by Ioana-Cristina Prioteasa over 1 year ago
Already covered by 9060a:
TextOps.numEntriesandTextOps.lookupCompareOpsandMathOpsP2JQuery.getNext()TempTableBuilder.addNewField()
UserTableStatUpdater.stat- used integer.ofFontTable- used int64.of in more functionsFrameWidget.getDown- used integer.ofHandleOps.canQuery- used logical constants
I will rebase the branch to facilitate testing, will update when that is done.
#39 Updated by Ioana-Cristina Prioteasa over 1 year ago
Please watch out for these error messages:
This is an immutable integer instance.This is an immutable int64 instance.This is an immutable character instance.This is an immutable logical instance.
If they appear in the server.log, post it here.
#40 Updated by Ioana-Cristina Prioteasa over 1 year ago
There was again a NPE because the cache was not initialized in time. Was caused by the use of character.of in EnvironmentOps.getOperatingSystem(), it seems like that can get executed before the server hook initializes the CacheManager. Added a null check in character, committed on 9060a rev.15729.
#41 Updated by Constantin Asofiei over 1 year ago
I'm reiterating something Ioana and I discussed about character.of() - at this point in time, the size of this cache is limited, as the strings are interned. So, the domain of the string values used by character.of must be small and also be only from inside FWD runtime, and not from outside, from the application. We may want to try something different (like a LRU) in phase two, but this needs to be kept in mind as we add character.of to more and more places (for example, adding it to Record._getCharacter may do more harm than good).
OTOH, in phase 2 we want to see how we can leverage logical.of and maybe also integer.of - but maybe we should consider if is possible to delay creating the BDT for each record field, and keep Java values as long as possible.
#42 Updated by Eric Faulhaber over 1 year ago
The current implementation uses a simple array for the cache, correct? Keep in mind that an LRU cache will have a lot more overhead, if the cache currently uses only simple indexed access to the array. If the performance margins are slim for this improvement, the speed of the cache could have a big impact.
#43 Updated by Constantin Asofiei over 1 year ago
Eric Faulhaber wrote:
The current implementation uses a simple array for the cache, correct? Keep in mind that an LRU cache will have a lot more overhead, if the cache currently uses only simple indexed access to the array. If the performance margins are slim for this improvement, the speed of the cache could have a big impact.
I'm talking about character.of() which uses an IdentityHashMap as the cache (with a limited max size), so that's not an array. My point with LRU is to do some experiments, what happens if we allow more strings (not interned).
#44 Updated by Alexandru Lungu over 1 year ago
My point with LRU is to do some experiments, what happens if we allow more strings (not interned).
What about the same approach as the dynamic cache in #9060-17? Static array with hashCode as key. The only overhead will be the hashCode computation for the String if not already computed.
#45 Updated by Constantin Asofiei over 1 year ago
Alexandru Lungu wrote:
What about the same approach as the dynamic cache in #9060-17? Static array with hashCode as key. The only overhead will be the hashCode computation for the
Stringif not already computed.
Yes, we can experiment in second phase; but on collision will require an equals.
#46 Updated by Alexandru Lungu over 1 year ago
Yes, we can experiment in second phase; but on collision will require an equals.
I checked H2 code right now and this technique is applied only for strings that do not exceed OBJECT_CACHE_MAX_PER_ELEMENT_SIZE in length (which defaults to 4096). Maybe we can do some experimenting and set a lower margin (?). If set to 16, I expect quite fast equals (also mind the == short-circuit).
#47 Updated by Eric Faulhaber over 1 year ago
Constantin, I believe Ioana said in the stand-up today that 9060a was ready for trunk. If you agree, please queue for merge.
#48 Updated by Constantin Asofiei over 1 year ago
- Status changed from Review to Merge Pending
9060a can be merged to trunk after 9544a
#49 Updated by Ioana-Cristina Prioteasa over 1 year ago
9060a is merged to trunk as rev.15726 and archived.
#50 Updated by Ovidiu Maxiniuc over 1 year ago
Because of the large update I peek at the changes in r15726.
I think we can squeeze a bit more. There are cases where values like integer.of(0) and int64.of(1) are used. Using the of method and the lookup(s) can be avoided if for these particular cases we define constants. Not many of them, probably 0, 1, 2 for each of the classes is enough. A similar constant I saw already implemented for character.EMPTY_STRING.
At any rate, the way these constants are created makes me a bit nervous. These static constants of a subclass type cannot be created until the current class is fully loaded. So this puts the base for a recursive initialization or some kind of a deadlock. With JDK 8 and 17, it works, but this is a strange construct and possible to fail for other JDKs.
#51 Updated by Artur Școlnic over 1 year ago
I added support for Constant BDTs for getters in Record. There was an issue with the type resolving in FuzzyMethodLookup, but it was fixed.
The code is in 9060b/15754.
#52 Updated by Dănuț Filimon over 1 year ago
- Status changed from Merge Pending to WIP
#53 Updated by Alexandru Lungu over 1 year ago
- Assignee changed from Ioana-Cristina Prioteasa to Artur Școlnic
On today meeting on #9060:
- Extend caching to more BDT when possible. Consider using dynamic caching for this. Refer to #9060-17.
- This may be a costly operation for
characteras hashingStringit is costly, but can fitdecimal,dateor other types that have very light hashing. Take example fromintegerand let that dynamic cache configurable.
- This may be a costly operation for
- For records: getters should return immutable types.
- Create a "constant" version for each BDT. For this, use
.unmodifiablewrapping static method to return a constant version of the BDT. - As an invariant, "of" is going to return an immutable instance after attempting to cache it (or look-up in a cache), while "unmodifiable" is simply returning a constant without any caching work.
- I think for
logical,integer,etc. we can useof, but more tricky types likecharactershould useunmodifiable.
- Create a "constant" version for each BDT. For this, use
- Save the BDT to
Recordlevel lazily using a separate array to store them. Consider invalidation when necessary (when alteringdatadue to setters). - Improve
charactercaching.- Currently it is used in a very specific place and the characters are interned. I don't have an immediate recommendation for this, so we should let the profiling generate some leads to drive any effort in this area.
- Conversion changes to replace new unknown() with unknown singleton where possible.
- Conversion changes to generate literals with
.ofmethod instead ofnewconstruct.
I think we can tackle these in this order. For conversion, feel free to open a separate task in conversion as the work on that shall come last and affect code in TRPL, rather than run-time.
#54 Updated by Artur Școlnic over 1 year ago
Should every constructor for a BDT have an equivalent immutable() overload?
#55 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
Should every constructor for a BDT have an equivalent
immutable()overload?
Add a static unmodifiable(<java native value>) to all BaseDataType sub-classes - get the list of all valid data type for a database or temp-table field (what the getter types in Record are).
#56 Updated by Artur Școlnic over 1 year ago
What I meant is that we only use one or two constructor overloads in Record for each BDT, so it makes sense to have an equivalent (the same parameters) immutable method only for those and not for all the constructors.
#57 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
What I meant is that we only use one or two constructor overloads in
Recordfor each BDT, so it makes sense to have an equivalent (the same parameters)immutablemethod only for those and not for all the constructors.
Ah, yes, that's good, add overloads as needed.
So we go with immutable or unmodifiable name for this new method?
#58 Updated by Artur Școlnic over 1 year ago
Immutable objects can't be changed at all, unmodifiable objects can't be change externally, but their state can be modified internally.
#59 Updated by Alexandru Lungu over 1 year ago
Artur Școlnic wrote:
Immutable objects can't be changed at all, unmodifiable objects can't be change externally, but their state can be modified internally.
- if we generate a "wrapper" over an existing BDT, then we should go with
unmodifiable, as it is a term used by Java collection wrappers (e.g.Collections.unmodifiableList). This intercepts calls toassignlike a proxy and rules out any modification. - if we generate objects of a new class that explicitly overrides the "assign" method, then
immutableis the right terminology. However, there is absolutely no occurence of an "immutable" method in Java docs.- Java 9 onward defines
ofstatic method as a way to provide immutable variants, mostly like we attempted with FWD. Thus, my personal preference is to stick withofand use caching whenever possible. If this is not possible (like for characters), lets have a flag for that (i.e.honorStaticCachethat will eventually do an intern and use the cache). The default for characters is honorStaticCache = false.
- Java 9 onward defines
#60 Updated by Constantin Asofiei over 1 year ago
Alexandru Lungu wrote:
- Java 9 onward defines
ofstatic method as a way to provide immutable variants, mostly like we attempted with FWD. Thus, my personal preference is to stick withofand use caching whenever possible. If this is not possible (like for characters), lets have a flag for that (i.e.honorStaticCachethat will eventually do an intern and use the cache). The default for characters is honorStaticCache = false.
I agree with this; lets go with .of() and an optional flag for i.e. character which would not cache the instance. This way, if we add future caches for types like date(time(tz)), then we don't need to change the Record getters.
#61 Updated by Andrei Plugaru over 1 year ago
Regarding the interference of this task to lazy hydration #6720.
IMO, at least for the current work on this task, we shouldn't have any conflicts with that. My only concern was if the data array from Record was directly accessed. Currently, it is not. However, Artur will probably need to access it in the future, and he will probably avoid using getDatum due to performance reasons. So, we should only take that into account when lazy hydration will get merged.
I will continue watching this task and if I see something that mixes with lazy hydration, I will notify about that.
#62 Updated by Artur Școlnic over 1 year ago
- Status changed from WIP to Review
I added the immutable types and used them in Record getters, so far it seems to work fine. The code is in 9060b/15755. I think a review after each phase would be an easier approach rather that analyzing all the changes at the end.
#63 Updated by Constantin Asofiei over 1 year ago
fuzzyMethodLookup-fromJavaalready hasfromConstantType, I thinktoLegacyBDTcall offromConstantTypewill do nothing.fromConstantType- instead of 'indexOf', use 'endsWith', and after that a substring using the"Constant".length()- why the access mode change for some methods in i.e.
datetimetz? handle- instead of duplicating code inhandleConstantforfromResourceId, get the common code into a method which returns aWrappedResourceand call.rawConstant.setUnknown()- this breaks the 'immutable' part - if we cache them, then there will be problems.recidis missing the overrides to make it immutable- look how
logicalConstantis checked througout the project - same must be done for the new constant types. - look into the implementations of
DataHandler- at leastpersist.orm.types.NumericType.convert(Object)I think needs to create constants, too. - why using
recid.ofRecid? - please place the public static 'of' method at the beginning of the file, after constructors
I have not checked if overrides are OK for all constant types.
#64 Updated by Artur Școlnic over 1 year ago
Constantin Asofiei wrote:
Review for 9060b/15755:
- why the access mode change for some methods in i.e.
datetimetz?
I needed the methods for constant types constructors.
rawConstant.setUnknown()- this breaks the 'immutable' part - if we cache them, then there will be problems.
This is extensively used in FWD, not allowing it, breaks the project. For now, I do not have a solution.
recidis missing the overrides to make it immutable
recid does not have any methods that alter it's state.
- why using
recid.ofRecid?
There is a conflict with the of from integer.
#65 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
rawConstant.setUnknown()- this breaks the 'immutable' part - if we cache them, then there will be problems.This is extensively used in FWD, not allowing it, breaks the project. For now, I do not have a solution.
Please provide some examples.
#66 Updated by Artur Școlnic over 1 year ago
- Status changed from Review to WIP
Constantin Asofiei wrote:
Please provide some examples.
What I meant is that it cannot be private or protected, as it is used a lot in FWD. The issue was that setUnknown is used in super constructors, but on a second look I managed to work around it, everything is working fine now with setUnknown being restricted. The next commit will have the fixes for the issues pointed out in the review and the caching of the BDTs in BaseRecord, I am pretty close with it.
#67 Updated by Artur Școlnic over 1 year ago
- Status changed from WIP to Review
9060b/15764 is ready for review.
#68 Updated by Artur Școlnic over 1 year ago
Clearly a LRU cache is not a good idea for caching the BDT constants, but what about a Most Frequently Used (MFU) cache?
The insertion, access and eviction operations can be done in constant time O(1), the cache size can be configured to be a relatively small one, as to not consume too much memory. This would not be as quick as the current static caching done in integer, but is a viable solution for the more complex types.
#69 Updated by Alexandru Lungu over 1 year ago
Artur Școlnic wrote:
Clearly a LRU cache is not a good idea for caching the BDT constants, but what about a Most Frequently Used (MFU) cache?
The insertion, access and eviction operations can be done in constant time O(1),
MFU is a cache policy which doesn't imply a certain implementation. Please describe how is this going to be implemented: the data structures and algorithm involved.
the cache size can be configured to be a relatively small one, as to not consume too much memory.
Memory is the last one to care about if the performance is improved. We can afford to spend extra 1MB per server for 1% time improvement (on a consistent manner and without synchronization overhead).
#70 Updated by Artur Școlnic over 1 year ago
This is the most efficient implementation I found:
Key Data Structures:- HashMap<K, V>: Stores the actual cache data.
- HashMap<K, Integer>: Tracks the frequency for each key.
- HashMap<Integer, LinkedHashSet<K>>: Groups keys by their access frequency. The frequency count is the key, and the values are sets of keys that have that frequency.
- minFrequency: Keeps track of the least frequently used frequency to easily find the entry to evict.
- get(K key):
Retrieves the value for the specified key.
Increments the access frequency of the key.
Moves the key to the appropriate frequency list. - put(K key, V value):
Adds a new key-value pair to the cache.
Initializes the frequency of the key to 1.
Adds the key to the frequency list for frequency 1.
Updates the minFrequency to 1 after insertion. - Eviction:
If the cache exceeds its capacity, it evicts the least frequently used entry (from the minFrequency list).
The evicted entry is removed from both the cache and the frequency map.
#71 Updated by Artur Școlnic over 1 year ago
The general idea is that the least used entries will always change, but the most used one will always be "in the top" of the cache, so if a lot of the same values are used, there won't be a lot of adding/eviction operations.
#72 Updated by Alexandru Lungu over 1 year ago
Artur Școlnic wrote:
This is the most efficient implementation I found:
I am not sure if this is more performant than a LRU cache, which uses a Map and a Linked List. The complexity of LRU operations is also O(1), but the practical constant is high (comparing to a simple array). We may agree that MFU can reach a similar constant to the LRU, but it has the same flaw as the LRU in the first place - which is high :)
For example, an arbitrary LRU operation is consuming 200 time units (which is O(1)) and the MFU operation is consuming 180 time units at best (which is also O(1)). In both cases, we may end up paying a time price higher than 20 time units that is memory allocation + GC stress. The #9060 goal is to check for a ... lets say ... 3 time units approach (which is again O(1)). Having this very narrow space forces us to use very trivial solutions. Very brute example: based on array look-up (2 time units) + one arithmetic operation (1 time unit). Simply invoking the get method, passing down parameters, hashing, returning from stack, etc. adds to the total of time units.
You can consider these "time units" as ms, opcodes executed, bytes accessed, or whatever time metric you see fit. All of the values in the example are simply illustrative and have no real experimentation behind - are there just to prove a point.
#73 Updated by Artur Școlnic over 1 year ago
I did a memory profiling on a large application FWDTest and saw that the most instantiated BDTs are unknown, and there are not any actual highly used values that could be statically cached for a performance improvement. Most likely, the conversion change we discussed and the transition to UNKNOWN static constant in the project where it make sens, will decrease greatly the number of instantiated BDTs.
#74 Updated by Artur Școlnic over 1 year ago
Memory profiling with the changes in 9060b results for BDT instances (large application FWDtest that uses a lot of CRUD operations):
object 1.4 million -> 300k (-78.57%) datetimetz 15k -> 10k (-33.33%) datetime 7.3k -> 5.8k (-20.55%) date 33k -> 25k (-24.24%) rowid 3.2k -> 2.1k (-34.38%) handle 64k -> 50k (-21.88%) decimal 280k -> 220k (-21.43%) raw 5.8k -> 3k (-48.28%)
Depending on the scenario, BDT instance creation could be significantly reduced, I suppose most of it is due to the usage of UNKNOWN constants.
I committed rev 15765, character caching in Record was added, still, new instances are used in the getter as trying to use constants, even the UNKNOWN, brings huge regressions.
#75 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
I committed rev 15765,
charactercaching inRecordwas added, still, new instances are used in the getter as trying to use constants, even theUNKNOWN, brings huge regressions.
Are you saying you are not using characterConstant for record's character getter?
#76 Updated by Artur Școlnic over 1 year ago
At this point, no. I am working on it.
#77 Updated by Artur Școlnic over 1 year ago
Currently I am trying to solve this issue:
new InputOutputExtentField(tempTable, "field") // field is of character type
The values for the parameters will be constants, because the
InputOutputExtentField constructor invokes the character getter.Later the parameter is passed to another method which assigns values to it, naturally, this breaks the immutability and the scenario crashes.
#78 Updated by Artur Școlnic over 1 year ago
Maybe we can cast the constant types to normal ones in FieldReference.getObject?
#79 Updated by Artur Școlnic over 1 year ago
Artur Școlnic wrote:
Maybe we can cast the constant types to normal ones in
FieldReference.getObject?
Or pass a parameter to it, to indicate whether we want an immutable instance or not.
#80 Updated by Constantin Asofiei over 1 year ago
I think the place is in InputOutputExtentField.
And also for InoutOutputField?
#81 Updated by Artur Școlnic over 1 year ago
Constantin Asofiei wrote:
And also for InoutOutputField?
It is possible, so far I have been solving issues as they arise.
So casting in such cases is ok?
#82 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
Constantin Asofiei wrote:
And also for InoutOutputField?
It is possible, so far I have been solving issues as they arise.
So casting in such cases is ok?
So there is no InputOutputField class. But, my point is: when you find a problem, look for the pattern: in this case, is INPUT-OUTPUT <field-ref>, which may be extent or not. Create standalone tests and check how it behaves.
For the InputOutputExtentField - yes, that is the place where to transform from constants to BDT. I don't see a simpler solution, as INPUT-OUTPUT means in 4GL that the argument is passed 'as reference' (be it a variable, field, property, etc).
#83 Updated by Artur Școlnic over 1 year ago
Alexandru suggested that at some point we should create new mutable instances and return them in the case of InputOutputExtentField.
#84 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
Alexandru suggested that at some point we should create new mutable instances and return them in the case of
InputOutputExtentField.
Yes, that's what I mean with 'transform from constants to BDT' - get an array with mutable instances instead of constants, in InputOutputExtentField.
#85 Updated by Artur Școlnic over 1 year ago
I encountered a few issued when using constant character, one of them leaves some tests in a perpetual wait state, a code example is illustrated below, it is inspired by the code in one of our customer's test:
define temp-table tt1 fields f1 as char. define temp-table tt2 fields f2 as char. create tt1. tt1.f1 = "100". create tt2. substring(tt2.f2,1,2) = right-trim(string(tt1.f1, "-999,999,999.99999")). message tt2.f2.
is converted to:
RecordBuffer.openScope(tt1, tt2);
tt1.create();
tt1.setF1(new character("100"));
tt2.create();
tt2.setF2(tt2.getF2().replace(rightTrim(valueOf(tt1.getF1(), "-999,999,999.99999")), 1, 2));
message((character) new FieldReference(tt2, "f2").getValue());
Although the result in FWD is the same as in 4GL, the tt2.setF2(tt2.getF2() code is troubling me. Now the replace is permitted in trunk, but I intend to restrict it in characterConstant, so in the future, code like that will fail. Most likely this will need to be changed at conversion.
As a side note, 130 attempts to use replace or replaceEntry with characterConstant are made by the FWDTests and some of them are failing due to this error.
Other than that, I managed to make it work and committed the code as rev 15766.
#86 Updated by Artur Școlnic over 1 year ago
After running 106 FWDTests, these are the results of the dynamic cache usage:
95% of decimals are retrieved from the cache. 17% of the characters are retrieved from the cache. 63% of the date are retrieved from the cache. 50% of the datetimetz are retrieved from the cache. Other types have a low occurance or extremely high miss rate.
From the tests I conduceted, I would say it is definetely worth it to enble the dynamic caching for
decimal, for character, I would avoid caching.For
date and datetimetz I conducted a series of experiments. I generated 1 mil values and recorded the total execution time, including garbage collection.Creating 1 mil
datetimetz instances takes, on average, 6594 milliseconds, using the dynamic cache, that has a hit rate of 50%-60%, takes 9140 milliseconds, this is a 71% total execution time increase, so I did not include the dynamic caching for these types.
I committed the code that enables dynamic caching for decimalConstants as rev 15767.
#87 Updated by Alexandru Lungu over 1 year ago
Creating 1 mil datetimetz instances takes, on average, 6594 milliseconds, using the dynamic cache, that has a hit rate of 50%-60%, takes 9140 milliseconds, this is a 71% total execution time increase, so I did not include the dynamic caching for these types.
This is odd. Can you share the caching code for datetimetz?
#88 Updated by Artur Școlnic over 1 year ago
if (DYNAMIC_CACHE == null)
{
return new datetimetzConstant(odt);
}
// Check dynamic cache
int index = Math.abs(odt.hashCode() % DEFAULT_DYNAMIC_CACHE_SIZE);
synchronized (DYNAMIC_CACHE)
{
datetimetzConstant cachedInstance = DYNAMIC_CACHE[index];
if (cachedInstance != null && odt.isEqual(cachedInstance.offsetDateTimeValue()))
{
return cachedInstance;
}
// Cache miss - create and replace the entry in dynamic cache
datetimetzConstant newInstance = new datetimetzConstant(odt);
DYNAMIC_CACHE[index] = newInstance;
return newInstance;
}
Of course, writing a performance test for such cases is not trivial, so there is a chance that the results look weird due to the testing methodology. I welcome any suggestions for testing the dynamic caching in isolation.
#89 Updated by Alexandru Lungu over 1 year ago
CompareOps.equalscan use a == short-circuit now, as there is a higher chance to have the same BDT as operands. For examplett.f1 = 1can be short-circuited as the static cache is used. The same applies tott.f1 = 1.1as the dynamic cache is used.System.identityHashCodecan be used forStringto retrieve a unique identifier for them (similar to a hash-code, but I guess it is faster). As for the equals check we can use == instead. This way, we can provide the samecharacterinstances for sameStringinstances. This can help for strings literals that are interned by default. I don't know the performance cost ofidentityHashCode... this should be investigated.
#90 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
Of course, writing a performance test for such cases is not trivial, so there is a chance that the results look weird due to the testing methodology. I welcome any suggestions for testing the dynamic caching in isolation.
Test the cache directly in Java - create the datetime-tz strings (what is odt in your code?), save them in a list, and after that check how .of() behaves when giving it this list.
#91 Updated by Artur Școlnic over 1 year ago
Constantin Asofiei wrote:
Test the cache directly in Java - create the datetime-tz strings (what is
odtin your code?), save them in a list, and after that check how.of()behaves when giving it this list.
This is what I have been doing. The 70% performance decrease is the result of tests like that.
#92 Updated by Artur Școlnic over 1 year ago
I am trying to replace all date instance creation in the project with the date.of. Naturally this breaks everything, should I leave the date instances that are used outside FWD as is?
For example:
date someDate = TypeFactory.date();// should return date or dateConstant?
#93 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
I am trying to replace all
dateinstance creation in the project with thedate.of. Naturally this breaks everything, should I leave thedateinstances that are used outside FWD as is?
For example:
[...]
TypeFactory and UndoableFactory must return mutable instances - this is how vars are defined in the converted 4GL code.
#94 Updated by Artur Școlnic over 1 year ago
I suppose BaseDataTypeFactory.instantiate and any BDT generation method should return a mutable instance.
#95 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
I suppose
BaseDataTypeFactory.instantiateand any BDT generation method should return a mutable instance.
Not necessarily. BaseDataTypeFactory is used only internally in FWD - are the usages requiring to have a mutable instance?
#96 Updated by Artur Școlnic over 1 year ago
It seems so, I am getting errors in the SharedVariableManager because dateConstants are used.
#97 Updated by Artur Școlnic over 1 year ago
The issue seem to originate from BaseDataType.sameType, it returns false for date and constant. Does it make sens to alter it so it returns true?
#98 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
The issue seem to originate from
BaseDataType.sameType, it returns false for date and constant. Does it make sens to alter it so it returns true?
Why would a constant end up in the SharedVariableManager registries? That's the root cause IMO.
#99 Updated by Artur Școlnic over 1 year ago
Object var = (extent != null ? Array.newInstance(expected, Math.max(0, extent))
: type != null ? new object(type)
: BaseDataType.generateDefault(expected));
generateDefault generates a date.UNKNOWN, that's why I asked about it.#100 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
[...]
generateDefaultgenerates adate.UNKNOWN, that's why I asked about it.
Because of BaseDataTypeFactory, right? If so, leave BaseDataTypeFactory unchanged for now.
#101 Updated by Artur Școlnic over 1 year ago
Replacing new date with date.of throughout the project means there are going to be a lot of overloads for the of, which means a lot of overloaded constructors for dateConstant. A lot of them use restricted methods (assign, setUnknown...), would it be acceptable to create private versions of these methods? This would mean duplication of code, but the alternative would be to duplicate it directly in the constructors, which also requires access to parent's attributes.
#102 Updated by Alexandru Lungu over 1 year ago
Artur, can you do a safe cut-out of the current changes you have to merge to trunk by EOW? Other matters can be deferred in the next iteration.
I really want the decimal dynamic cache (Urgent).
#103 Updated by Artur Școlnic over 1 year ago
- Status changed from Review to WIP
I will bring the branch to a stable state by solving the issue described in #9060-85 and any other that pop out during testing. Replacing new instance creation with constants, dynamic caches for date/datetimetz and the conversion change that generates constants instead of new instance creation will be deferred to 9060c.
#104 Updated by Artur Școlnic over 1 year ago
Here are my proposals for solving the replace issue:
original: tt2.setF2(tt2.getF2().replace(rightTrim(valueOf(tt1.getF1(), "-999,999,999.99999")), 1, 2)); option 1: tt2.setF2(new character(tt2.getF2()).replace(rightTrim(valueOf(tt1.getF1(), "-999,999,999.99999")), 1, 2)); // wrap the constant in a new char and use repalce on it. option 2: tt2.setF2(tt2.getF2().substring(rightTrim(valueOf(tt1.getF1(), "-999,999,999.99999")), 1, 2)); //add a new method, substring returns a new immutable instance option 3: tt2.setF2(tt2.getF2().replace(rightTrim(valueOf(tt1.getF1(), "-999,999,999.99999")), 1, 2)); // check in the method if it was generated at conversion time, if true, allow altering the state of the constant. At 'runtime', throw the error, if that can be done...
The same approach would need to be applied to the replaceEntry method.
Constantin, Alexandru, I would appreciate your input on this.
#105 Updated by Artur Școlnic over 1 year ago
Of course I will try to limit the changes to be applied only in the field access scenario.
#106 Updated by Artur Școlnic over 1 year ago
I had a talk with Constantin, the conclusion was that we need to wrap in mutable instances the constant types for every assign style statement.
#107 Updated by Artur Școlnic over 1 year ago
- Status changed from WIP to Review
I committed the conversion change to rev 15768, Constantin, please take a look. I converted hotel and there were no changes. I think all the changes currently in the branch need to be reviewed, after that I can proceed with an extensive testing plan.
#108 Updated by Constantin Asofiei over 1 year ago
- reviewer Alexandru Lungu added
- in
database_references.rules, I think you need to check for the index, too BufferImpl- are all characterConstant case-insensitive? FromRecord._getCharacter, this is not the case - so I think there we need to create a character mutable instance withcase-sensitive = false.CacheManagerrequires history entryRecord_getCharacter- please align the arguments incharacter.of_getReciddoes not usecachedBDTssetPropertyValues- look for direct write access ofBaseRecord.datafield
FuzzyMethodLookup-fromConstantType- instead ofcontainsuseendsWithcharacter$characterConstant.equalswas removed?- look how
logicalConstantorintegerConstantis used - you need to register all new constant types in the same places
Alexandru: please take a look, too.
#109 Updated by Alexandru Lungu over 1 year ago
Review of 9060b:
- Leave a space between the target and cast type (e.g.
datetimetz result = (datetimetz)cachedBDTs[offset];should bedatetimetz result = (datetimetz) cachedBDTs[offset];) _getCharacterdoesn't seem to cache unknown values.- There are spots in
BaseRecordthat still should invalidate the cache (e.g.copy(BaseRecord from),RecordMeta.applyInitialValues).- I think
*Type.javaclasses are all changing the record's data array. For example,BlobType.setFieldis setting a field in the data and so it should invalidate the cached BDT. - Please look more in depth on this, I barely skimmed some places that need invalidation. Omitting some of this may be fatal as updates won't be visible as the cached value will be old.
- I think
- move static unknown constants at the top of properties (before the instance props).
- put overloaded methods one near each other (the one will less args before the one with more props): see
character.of - Isn't
assign(value.val());fromclobConstantgoing to throw an error due to immutability? - All
.ofvariants may rather return UNKNOWN if the parameter is null. This better encapsulates the null-safety. - Some braces are placed on the same line as the method signature (e.g.
dateConstant(Date d) {) - Misalignments:
+ public static class datetimetzConstant + extends datetimetz + implements BaseDataTypeConstant
- Leftovers:
long startTime = System.currentTimeMillis(); // Start time
- I am quite nervous with comparing
doublevalues with==:cachedInstance.doubleValue() == value.doubleValue(). This may be not accurate due to limitations ofdoubledata type precision. Can we rather havecachedInstance.equals(value)? Please do the required tests to confirm we still have the same cache hit rate. - I wonder if the synchronization is mandatory. What can happen if we remove that? The concurrent access is highly improbable on that and we pay a cost of synchronization for each decimal created. If there is actual concurrent access, if these are on different spots, I suspect nothing wrong can happen. If they are on the same position, a shadow update will happen so only one value will prevail in the cache, but two threads may work with two different instances. This is a very highly improbable scenario that will cost us only 1 extra instance creation. The cost of synchronizing however is too big!
- Please do the required testing and if possible get rid of the
synchronizedblock (from integer as well). Redo tests for date and other types you seem fit. Maybe the overhead was from synchronization.
- Please do the required testing and if possible get rid of the
- In object:
res.assign(object.of(datum));seems like the constant is redundant as it is assigned straight-away to a mutable instance that ends up being cached. Should the res simply be assigned with = operator to theofreturn value?
#110 Updated by Artur Școlnic over 1 year ago
Constantin Asofiei wrote:
Review for 9060b rev 15768:
- in
database_references.rules, I think you need to check for the index, too
I am not sure what you mean, the index of the property? Check it for what?
character$characterConstant.equalswas removed?
Yes, it was causing the unsupported exception. Is there a reason that characterConstant can't use the parent's method?
- look how
logicalConstantorintegerConstantis used - you need to register all new constant types in the same places
Do you mean replace new instance creation with constants in the project? If so, that would be the next step, which I already partly implemented, but for now we need to wrap up the existing changes.
#111 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
Constantin Asofiei wrote:
Review for 9060b rev 15768:
- in
database_references.rules, I think you need to check for the index, tooI am not sure what you mean, the index of the property? Check it for what?
Check the position of the field property in the AST - it should be zero I think?
character$characterConstant.equalswas removed?Yes, it was causing the unsupported exception. Is there a reason that characterConstant can't use the parent's method?
You mean Can not compare character with characer constant!? Or that now a constant and a mutable character can co-exist in the FWD runtime, so i.e. compare needs to be OK?
- look how
logicalConstantorintegerConstantis used - you need to register all new constant types in the same placesDo you mean replace new instance creation with constants in the project?
No, I mean where the i.e. logicalConstant.class is checked beside logical.class.
If so, that would be the next step, which I already partly implemented, but for now we need to wrap up the existing changes.
Is mandatory to make these changes in the branch, it can't be merged without them.
#112 Updated by Artur Școlnic over 1 year ago
Constantin Asofiei wrote:
You mean
Can not compare character with characer constant!?
Yes.
#113 Updated by Alexandru Lungu over 1 year ago
Is mandatory to make these changes in the branch, it can't be merged without them.
I tend to agree with this statement. As long as we throw "constant" BDT into the wild, we need to ensure they are properly supported by the rest of the FWD API. So indeed, jobject.toJavaInstance is one of the many examples.
#114 Updated by Artur Școlnic over 1 year ago
Artur Școlnic wrote:
that would be the next step, which I already partly implemented, but for now we need to wrap up the existing changes.
I was talking about using the constant types in the project instead of creating new instances. Class checking of the BDTs though the project, indeed needs to be complemented with the new constant types checking, I will add this in the next revision.
#115 Updated by Artur Școlnic over 1 year ago
Alexandru Lungu wrote:
- I am quite nervous with comparing
doublevalues with==:cachedInstance.doubleValue() == value.doubleValue(). This may be not accurate due to limitations ofdoubledata type precision. Can we rather havecachedInstance.equals(value)? Please do the required tests to confirm we still have the same cache hit rate.- I wonder if the synchronization is mandatory.
Removing the synchronization did not affect the hit rate much, but using cachedInstance.equals(value) simply breaks the caching because it is always false.
#116 Updated by Alexandru Lungu over 1 year ago
but using cachedInstance.equals(value) simply breaks the caching because it is always false.
- why?
- can you attempt
compareTo() == 0
#117 Updated by Artur Școlnic over 1 year ago
- Status changed from Review to WIP
Alexandru Lungu wrote:
- why?
Isn't this expected? Comparing a decimalConstant with a BigDecimal using equals should not return true.
- can you attempt
compareTo() == 0
This seems to work fine, the hit rate is not affected.
#118 Updated by Alexandru Lungu over 1 year ago
Isn't this expected? Comparing a decimalConstant with a BigDecimal using equals should not return true.
Oh, sorry for the confusion. I meant cachedInstance.value.equals(value) or something like that: comparing the BigDecimal of the cached instance with the BigDecimal provided, without converting to double and use ==.
#119 Updated by Artur Școlnic over 1 year ago
Alexandru Lungu wrote:
Oh, sorry for the confusion. I meant
cachedInstance.value.equals(value)or something like that: comparing theBigDecimalof the cached instance with theBigDecimalprovided, without converting todoubleand use==.
Only compareTo seems to work as expected.
#120 Updated by Alexandru Lungu over 1 year ago
Please make sure that compareTo returns 0 only when the scale is the same. Otherwise, we may end up considering def var x as decimal scale 2 initial 1 and def var y as decimal scale 3 initial 1 equal, although these should be different decimal instances.
#121 Updated by Artur Școlnic over 1 year ago
decimal.compareTo uses BigDecimal.compareTo which checks the scale, so I think we are safe.
#122 Updated by Artur Școlnic over 1 year ago
I started a more extensive testing process, hundreds of unit tests are failing because of assign style methods being used with a BDT that was retrieved with a getter, so it is a constant. The most notable example is clob.write. Should I wrap the constant given as a parameter in a new mutable type and return it instead?
#123 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
I started a more extensive testing process, hundreds of unit tests are failing because of assign style methods being used with a BDT that was retrieved with a getter, so it is a constant. The most notable example is
clob.write. Should I wrap the constant given as a parameter in a new mutable type and return it instead?
Didn't we solved this via conversion? Did you reconvert with 9060b?
#124 Updated by Artur Școlnic over 1 year ago
This is different, for example:
at com.goldencode.p2j.util.clob$clobConstant.assign(clob.java:705) at com.goldencode.p2j.util.Text.assign(Text.java:2335) at com.goldencode.p2j.util.clob.write(clob.java:443) at com.goldencode.p2j.util.TargetLob.write(TargetLob.java:382) at com.goldencode.p2j.util.LobCopy.run(LobCopy.java:333)
LobCopy ends up writing data in the clob object, which could be constant, so in situations like this, I am inclined to wrap the constant in a new BDT so the write method does not use the same instance to assign the new data.The converted code looks like:
new LobCopy(new SourceLob(longchar), new TargetLob(new FieldReference(tt, "field"))).run();
This is just an example, the idea is to come up with a general approach for this kind of situations where the immutable types end up being used in FWD with state altering methods.
#125 Updated by Alexandru Lungu over 1 year ago
new LobCopy(new SourceLob(longchar), new TargetLob(new FieldReference(tt, "field"))).run();
I agree that public TargetLob(FieldReference field) should wrap the constant in a mutable instance as TargetLob.lob is an internal longchar used to store the destination value. On TargetLob.write, if a field exists, the field is set with this internal BDT! Thus, the lob property seems to be an "intermediate" place to store that.
Please do extra tests with memptr, blob and clob data types to ensure they are also properly handled by the LobCopy operation (after fixing the longchar case).
#126 Updated by Constantin Asofiei over 1 year ago
Artur, BTW, clobConstant needs to override all methods which write it, like the clob.write(clob.java:443).
#127 Updated by Constantin Asofiei over 1 year ago
Artur, please go through all Accessor.get() references - there only 64 I can find, and at least Stream.readField looks like it needs attention. And there, I don't understand this code:
if (ref.getType() == blob.class)
{
var.assign(PropertyMapper.preprocessBlob(new character(var)));
}
else if (ref.getType() == clob.class)
{
var.assign(PropertyMapper.preprocessClob(new character(var)));
}
else
{
ref.set(var);
}
ref is a field, but in case of blob or clob, the var gets assigned, insead of ref.set(var).
#128 Updated by Artur Școlnic over 1 year ago
- Status changed from WIP to Review
I committed 9060b/15771, it contains fixes for issues from the review, constant type registering throughout the project and general regression fixing as a result of running a large application unit test suite.
Regarding public void readField(FieldReference ref), maybe I am mistaken, but I don't think it is used anywhere, otherwise I would have seen errors in it during testing. It looks like it calls itself recursively and no other methods call it.
#129 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
I committed 9060b/15771, it contains fixes for issues from the review, constant type registering throughout the project and general regression fixing as a result of running a large application unit test suite.
Regarding
public void readField(FieldReference ref), maybe I am mistaken, but I don't think it is used anywhere, otherwise I would have seen errors in it during testing. It looks like it calls itself recursively and no other methods call it.
See this test:
def temp-table tt1 field f1 as int. create tt1. import tt1.f1.
readField(FieldReference) is emitted at conversion.
#130 Updated by Artur Școlnic over 1 year ago
The changes in rev 15771 should solve the immutable types problem
BaseDataType var = ref.get(); var = readField(var); // readField returns a muttable BDT
so the execution will proceed as it did before the changes.
The only change that could be needed is in the conditional you mentioned in #9060-127.
if (ref.getType() == blob.class)
{
var.assign(PropertyMapper.preprocessBlob(new character(var)));
}
else if (ref.getType() == clob.class)
{
var.assign(PropertyMapper.preprocessClob(new character(var)));
}
ref.set(var);
I believe clob/blob data should be preprocessed, assigned to var which then should be set to the field reference.
#131 Updated by Constantin Asofiei over 1 year ago
Please create some tests to check IMPORT with blob/clob.
#132 Updated by Artur Școlnic over 1 year ago
I did the tests, with 9060b, both export and import is broken, with trunk, export is ok, but the import is broken. I am working on a fix.
#133 Updated by Artur Școlnic over 1 year ago
So I think PropertyMapper.preprocessClob/Blob is necessary because it assigns to var the actual data of the lob, as opposed to ref.get() which assign the name/path of the lob file, so the preprocessing is vital, the problem is that it is broken, PropertyMapper.readLobData is failing because it relies on the ImportWorker to be initialized, but it isn't.
#134 Updated by Artur Școlnic over 1 year ago
I see that Eric and Ovidiu worked on ImportWorker.initialize, can someone please explain where should it be called, it is needed to properly locate the lob files, which at this moment is not happening.
#135 Updated by Dănuț Filimon over 1 year ago
Artur Școlnic wrote:
I see that Eric and Ovidiu worked on
ImportWorker.initialize, can someone please explain where should it be called, it is needed to properly locate the lob files, which at this moment is not happening.
It's used in the rules, check rules/schema/import.xml.
#136 Updated by Constantin Asofiei over 1 year ago
Dănuț Filimon wrote:
Artur Școlnic wrote:
I see that Eric and Ovidiu worked on
ImportWorker.initialize, can someone please explain where should it be called, it is needed to properly locate the lob files, which at this moment is not happening.It's used in the rules, check rules/schema/import.xml.
And ImportWorker must not be used by runtime
#137 Updated by Artur Școlnic over 1 year ago
Well, PropertyMapper uses it:
private static byte[] readLobData(String locator)
{
String path = ImportWorker.lobPath; // lobPath is null
if (!path.endsWith(File.separator))
At this point we have the lob file name, but not the path.
#138 Updated by Constantin Asofiei over 1 year ago
OK, then please create a task in the database project for this. The code may be used just for importing the .d data (and lobs), not for IMPORT statement at runtime.
#139 Updated by Artur Școlnic over 1 year ago
- Related to Bug #9872: Runtime import statement relies on the ImportWorker to read lob data. added
#140 Updated by Constantin Asofiei over 1 year ago
FieldReference- is missing history entry
- do we need to do the same for
blobConstant?
RecordMeta- is missing history entry
RecordLoader- is missing history entry
characterConstant- you added
javaSpacifyNull, but you always dovalue = javaSpacifyNull(value);if there is a value. This seems wrong.
- you added
clobConstant- the
if (BaseDataType.isProxy(value))is always false, because you doText value = new character(str); - why the
checkUndoable(value);call? This is an immutable instance. writeExternalshould be allowed I think.
- the
decimalDYNAMIC_CACHEis global per JVM. We need synchronization here, I don't think volatile is enough.
DynamicOps- is missing history entry
- what were the reasons for this change? Now you return the actual BDT instance (from the args) as the result, and not a copy; this is wrong.
ObjectOps- I assume this change is related to
delete object tt1.objectField; please doo.ref = null;only if the instance is anobjectConstant.if (deleted) { o.ref = null; }
- I assume this change is related to
recidConstant- shouldn't this override all methods as in
integerConstant?
- shouldn't this override all methods as in
TargetLob- is missing history entry
this.lob = new clob(field.get());line is wrong. A field can be memptr also.
Stream- what is the test for the
readField(BaseDataType[] var)change?
- what is the test for the
- please run
ant javadocand fix any javadoc problems in the files changed in this task. - data import needs to be tested
9060b rev 15772 contains some formatting changes.
#141 Updated by Constantin Asofiei over 1 year ago
Also, please rebase.
#142 Updated by Artur Școlnic over 1 year ago
Rebased 9060b on trunk rev 15849.
#143 Updated by Artur Școlnic over 1 year ago
In order to make the Stream.readField work with the constant types, I had to change the method implementation so that the result is no longer assigned to the argument variable, but a new BDT is created and returned, this solves some of the issued but creates other ones, for example:
UnnamedStreams.safeInput().readField(var);
After the changes, the
var will not hold the read value, in order to solve this issue, conversion changes need to be made so that code looks like:var = UnnamedStreams.safeInput().readField(var);
Alternatively, the changes I made to the
readField can be reverted and another solution implemented, though I am not sure if one exists, if a constant is passed to it, we cannot change it's value.#144 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
In order to make the
Stream.readFieldwork with the constant types, I had to change the method implementation so that the result is no longer assigned to the argument variable, but a new BDT is created and returned, this solves some of the issued but creates other ones, for example:
[...]
After the changes, thevarwill not hold the read value, in order to solve this issue, conversion changes need to be made so that code looks like:
[...]
This is not correct - you can't re-assign the BDT var created by the application, to another instance.
Alternatively, the changes I made to the
readFieldcan be reverted and another solution implemented, though I am not sure if one exists, if a constant is passed to it, we cannot change it's value.
Where can I find the changes to readField?
#145 Updated by Artur Școlnic over 1 year ago
The changes are in 9060b/15859.
#146 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
The changes are in 9060b/15859.
Change the code in readField(BaseDataType) so that a new instance is created only and only if the parameter is a BaseDataTypeConstant. This should work.
#147 Updated by Artur Școlnic over 1 year ago
Sounds good.
#148 Updated by Constantin Asofiei over 1 year ago
Question: please remind me when readField(BaseDataType[] var) is emitted.
#149 Updated by Artur Școlnic over 1 year ago
Import statements, but I also saw code like:
import ^ newpath ^.
which is emitted as:
UnnamedStreams.safeInput().skipField(); UnnamedStreams.safeInput().readField(var); UnnamedStreams.safeInput().skipField(); UnnamedStreams.safeInput().resetCurrentLine();
In this particular context, I am not sure what the
^^ are.#150 Updated by Constantin Asofiei over 1 year ago
Artur Școlnic wrote:
Import statements, but I also saw code like:
I meant the array overload of readField
#151 Updated by Artur Școlnic over 1 year ago
I can't see an obvious usage in the customer project i am working with.
#152 Updated by Artur Școlnic over 1 year ago
I addressed the review and fixed a few bugs, I no longer have failing tests in the unit test modules I am using (> 5k tests).
When it comes to the DYNAMIC_CACHE in decimal, I removed the synchronization due to it's overhead, the retrieved value is checked anyway so there is no risk of retrieving a wrong one. I don't remember if I did extensive testing for this, but I could do it if needed.
I committed the changes to 9060b/15860.
#153 Updated by Constantin Asofiei about 1 year ago
- in
Stream.readField,return res;should be used for non-constant case (to keep the existing behavior) - for
readField(BDT[])- please try some examples with extent fields. TargetLobissue was not addressed (this.lob = new clob(field.get()))
#154 Updated by Artur Școlnic about 1 year ago
Constantin Asofiei wrote:
- in
Stream.readField,return res;should be used for non-constant case (to keep the existing behavior)
The existing behavior of readField is to reassign the value, it does not return anything. There is no need to return var.duplicate() for a non constant var.
TargetLobissue was not addressed (this.lob = new clob(field.get()))
this.lob will have a mutable type constructed from a potentially immutable field reference.
Can I move on to more extensive testing?
#155 Updated by Constantin Asofiei about 1 year ago
Artur Școlnic wrote:
Constantin Asofiei wrote:
- in
Stream.readField,return res;should be used for non-constant case (to keep the existing behavior)The existing behavior of
readFieldis to reassign the value, it does not return anything. There is no need to returnvar.duplicate()for a non constant var.
In readField(FieldReference) you have var = readField(var);. You are right that original code was not returning anything, but lets keep consistent and return var; even if is the same instance as the argument.
TargetLobissue was not addressed (this.lob = new clob(field.get()))
this.lobwill have a mutable type constructed from a potentially immutable field reference.
I don't understand. Original was:
public TargetLob(FieldReference field)
{
this.field = field;
this.lob = (LargeObject) field.get();
}
Now is:
public TargetLob(FieldReference field)
{
this.field = field;
this.lob = new clob(field.get());
}
LargeObject instances which can be at a field are blob, clob, longchar and memptr. You are now limiting this constructor only for clob types.
Can I move on to more extensive testing?
Yes, after addressing above and rebase.
#156 Updated by Constantin Asofiei about 1 year ago
Does this.lob = (LargeObject) field.get().duplicate(); work, only and only if field.get() is a BaseDataTypeConstant?
#157 Updated by Artur Școlnic about 1 year ago
Constantin Asofiei wrote:
Does
this.lob = (LargeObject) field.get().duplicate();work, only and only iffield.get()is aBaseDataTypeConstant?
Should work, duplicate returns a mutable instance.
#158 Updated by Artur Școlnic about 1 year ago
- Status changed from Review to Internal Test
#159 Updated by Constantin Asofiei about 1 year ago
In TargetLob(FieldReference field), field.get() is called twice per call. Please use bdt for the second call.
#160 Updated by Constantin Asofiei about 1 year ago
Also, testing needs to include performance testing, where possible.
#161 Updated by Artur Școlnic about 1 year ago
In fuzzyMethodLookup, the line:
System.out.printf("\n CALLER %s\n PARAMETERS: %d\n", node.dumpTree(true), caller.length);
the
node argument is null and is never assigned, so it causes a NPE, the changes in 9060b opened new execution paths, previously this code was never reached.The method is called in
FuzzyMethodRt.lookup fuzzyMethodLookup(methodName, sig, accessMode, isStatic, internal, null);
Should I add a conditional to the print or delete it?
#162 Updated by Constantin Asofiei about 1 year ago
I don't understand, fuzzyMethodLookup is almost sure called. I see WARNING: more than one method def found using fuzzy looku in lots of projects.
Why do you get the NPE?
#163 Updated by Artur Școlnic about 1 year ago
The call is fuzzyMethodLookup(methodName, sig, accessMode, isStatic, internal, null);
null is passed for the node parameter, and it is not reassigned, it simply could have not worked.
Previously, in fuzzyMethodLookup the method was returning earlier and was not reaching this point.
#164 Updated by Constantin Asofiei about 1 year ago
Artur Școlnic wrote:
The call is
fuzzyMethodLookup(methodName, sig, accessMode, isStatic, internal, null);
Please post the stacktrace for this call.
#165 Updated by Artur Școlnic about 1 year ago
Caused by: java.lang.NullPointerException[0m │ │ │ │ │ [31m at com.goldencode.p2j.uast.FuzzyMethodLookup.fuzzyMethodLookup(FuzzyMethodLookup.java:638)[0m │ │ │ │ │ [31m at com.goldencode.p2j.util.FuzzyMethodRt.lookup(FuzzyMethodRt.java:139)[0m │ │ │ │ │ [31m at com.goldencode.p2j.util.ControlFlowOps.resolveLegacyEntry(ControlFlowOps.java:6190)[0m │ │ │ │ │ [31m at com.goldencode.p2j.util.ControlFlowOps.resolveLegacyEntry(ControlFlowOps.java:5542)[0m │ │ │ │ │ [31m at com.goldencode.p2j.util.ControlFlowOps.resolveLegacyEntry(ControlFlowOps.java:4475)[0m │ │ │ │ │ [31m at com.goldencode.p2j.util.ObjectOps.invokePolyWorker(ObjectOps.java:860)[0m │ │ │ │ │ [31m at com.goldencode.p2j.util.ObjectOps.invokeStandalonePoly(ObjectOps.java:699)[0m
#166 Updated by Constantin Asofiei about 1 year ago
Artur, on a second thought: this is a runtime stack - that location should never be reached. Something is wrong in 9060b.
#167 Updated by Artur Școlnic about 1 year ago
else if (list.isEmpty())
{
if (debug)
{
// if we still have ambiguous matches, display any stored error
if (error[0] != null)
{
System.out.println(error[0]);
}
System.out.println("\n\n----------------fuzzyMethodLookup NO MATCH SUCKA!----------------\n");
}
return null; // ---> trunk
}
System.out.println("WARNING: more than one method def found using fuzzy lookup: " + name +
" from class " + getClassName());
System.out.printf("\n CALLER %s\n PARAMETERS: %d\n", node.dumpTree(true), caller.length); // ---> 9060b
the
MatchMetrics list is no longer empty.#168 Updated by Artur Școlnic about 1 year ago
With a simple null check, the failing tests are passing.
#169 Updated by Constantin Asofiei about 1 year ago
Artur, see FuzzyMethodRt.resolveSignature - type = arg.getClass().getSimpleName(); - if arg is a BaseDataTypeConstant, resolve the super-class. We already do this in ControlFlowOps.calculateType. But please review resolveLegacyEntry if there is any other missing place where BaseDataTypeConstant is not honored.
#170 Updated by Artur Școlnic about 1 year ago
Constantin Asofiei wrote:
Artur, see
FuzzyMethodRt.resolveSignature-type = arg.getClass().getSimpleName();- if arg is aBaseDataTypeConstant, resolve the super-class. We already do this inControlFlowOps.calculateType. But please reviewresolveLegacyEntryif there is any other missing place whereBaseDataTypeConstantis not honored.
It did not solve the issue. It still looks wrong to me to pass a null value for node then calling dumpTree on it.
#171 Updated by Constantin Asofiei about 1 year ago
The point is: the type at that place most likely is a i.e. integerConstant - look further or otherwise debug and find why a constant type is being used in FuzzyMethodRt. This must not happen.
#172 Updated by Artur Școlnic about 1 year ago
ObjectOps.invokeStandalonePoly(Assert.class, "AreEqual", "III", new Object[]{actual.unwrapBuffer().bufferField(i).unwrapBufferField().value(), expect.unwrapBuffer().bufferField(fieldName).unwrapBufferField().value(), msg});
the arguments are integerConstant, but i don't think this is an issue, there are a lot of calls in FuzzyMethodRt with constant arguments that do not cause any issues. I reverted the changes made in FuzzyMethodLookup and the tests pass.
#173 Updated by Constantin Asofiei about 1 year ago
Artur, I understand that, but constant types should be resolved to their actual types. There is a bug somewhere in FuzzyMethodRt or resolveLegacyEntry. If we target can exactly on the type, a constant argument should not interfere with this.
Also, System.out.println("WARNING: more than one method .. you mention in FuzzyMethodRt is a debug output which until now was emitted only for conversion; for runtime, we need to include it in if (debug) processing (but conversion should still emit it, as its useful). In any case, please try first to find the 'root cause' why constant types are not resolved to their BDT super-class.
#174 Updated by Artur Școlnic about 1 year ago
I committed the fix for this and other issues to 9060b/15882. Constantin, maybe you can take a look while I continue testing.
#175 Updated by Constantin Asofiei about 1 year ago
ControlFlowOps and TargetLob are good. But, the RecordBuffer change is not OK:
- you change the
datuminsetCompareMode, but the argument at the caller remains unchanged (thus the case-sensitivity is unchanged) - also, I think is part of a larger problem: we have
DataHandler.getField(), for which the returned instance can be assumed mutable in lots of places. I think we need to changegetFieldto return a mutable instance, and not a constant, in all implementations.
#176 Updated by Constantin Asofiei about 1 year ago
Constantin Asofiei wrote:
- also, I think is part of a larger problem: we have
DataHandler.getField(), for which the returned instance can be assumed mutable in lots of places. I think we need to changegetFieldto return a mutable instance, and not a constant, in all implementations.
Or otherwise add a parameter to getField(boolean allowConstant), so that a mutable instance is created only in places where is needed; this can work only if these places can be identified.
#177 Updated by Artur Școlnic about 1 year ago
Returning a mutable instance for all implementations seems to work fine, the testing passed.
#178 Updated by Constantin Asofiei about 1 year ago
OK, lets leave it as this for now.
For 9060b:- please do
bzr diff -r 15867 --using meldand check the copyright year for all files - run javadoc and fix problems only in the files changed in this branch
Otherwise, lets continue testing - all apps need to be nested, performance included.
#179 Updated by Alexandru Lungu about 1 year ago
- Related to Bug #10004: NativeInvoker fails if an argument is of type integerConstant added
#180 Updated by Artur Școlnic about 1 year ago
Rebased 9060b on trunk rev 16051.
#181 Updated by Artur Școlnic 4 months ago
I extracted the more impactful and safe bdt caching code and tested the performance impact on a customer application. Lookup style requests were used for the experiment and I registered an average of ~5% performance regression with all apis affected in a negative way.