Code is way neater so it is a good move forward but it still does a lot of buffer flushing and I think using a dedicated buffer or using the size heuristic + tuncation if needed would be faster but would need to measure it to be 100% sure.
Romain Manni-Bucau @rmannibucau <https://twitter.com/rmannibucau> | Blog <https://rmannibucau.metawerx.net/> | Old Blog <http://rmannibucau.wordpress.com> | Github <https://github.com/rmannibucau> | LinkedIn <https://www.linkedin.com/in/rmannibucau> | Book <https://www.packtpub.com/application-development/java-ee-8-high-performance> Le mar. 26 avr. 2022 à 09:36, David Blevins <[email protected]> a écrit : > Also had an idea as I was trying to go to bed. The thought occurred to me > that we only need to call flush before calling > snippetOutputStream.terminate(). So I reworked the code to do that. We > also had some calls to terminate() that we didn't need because they were > already getting called in the loops that proceeded them. > > I'll read what you wrote below, but can you take a look and let me know > what you think? > > > -David > > > > On Apr 26, 2022, at 12:02 AM, Romain Manni-Bucau <[email protected]> > wrote: > > > > Hey David, > > > > Just got an idea we can maybe refine to merge both points. > > I'll try to draft it high level (understand a Writer can be an > OutputStream > > or the opposite in your impl) so don't get too picky about the details > > please ;). > > > > High level the idea is to make JsonGeneratorFactoryImpl to be able to > > handle an outputstream or writer which would provide its buffer size. > > > > Here are the idea steps I can envision: > > > > 1. Create a new interface in johnzon-core `public interface > > MetadataProvider { int bufferSize(); }` > > 2. Create an OutputStream implementation of MetadataProvider - let's call > > it MetadataProviderOutputStream for this list - if it helps it can be a > > MetadataProviderSnippetOutputStream directly which would just be a > subclass > > of SnippetOutputStream, think it would make it simpler. > > 3. Create a Supplier<OutputStream> (factory) in MapperBuilder - where we > > create the Snippet instance - which will basically test the class name of > > the generator factory - no reflection there please to stay compatible > with > > all our downstream consumers without changes - and if it > > is org.apache.johnzon.core.JsonGeneratorFactoryImpl then it will create a > > MetadataProviderOutputStream else a plain OutputStream and the feature > will > > be disabled (for now at least) - note that it can need a small > indirection > > like a MetadataProviderOutputStreamSupplier to not trigger the load of > the > > MetadataProvider on MapperBuilder class init/verification. This will also > > use the snippet max size indeed. Here we want to ensure we can run > without > > johnzon-core as JSON-P impl. > > 4. Pass the supplier created at 3 to the Snippet class next to the > > generator factory > > 5. Change JsonGeneratorFactoryImpl#createGenerator(OutputStream) to test > if > > it is a MetadataProvider instance to handle it (small trick there is to > try > > to cache the snippet buffer to keep reusing the buffer caching - and only > > when needed): > > > > public class JsonGeneratorFactoryImpl extends AbstractJsonFactory > > implements JsonGeneratorFactory { > > public static final String GENERATOR_BUFFER_LENGTH = > > "org.apache.johnzon.default-char-buffer-generator"; > > public static final int DEFAULT_GENERATOR_BUFFER_LENGTH = > > Integer.getInteger(GENERATOR_BUFFER_LENGTH, 64 * 1024); //64k > > > > static final Collection<String> SUPPORTED_CONFIG_KEYS = asList( > > JsonGenerator.PRETTY_PRINTING, GENERATOR_BUFFER_LENGTH, > > BUFFER_STRATEGY > > ); > > //key caching currently disabled > > private final boolean pretty; > > private final Buffer buffer; > > private volatile Buffer firstCustomBuffer; > > > > public JsonGeneratorFactoryImpl(final Map<String, ?> config) { > > super(config, SUPPORTED_CONFIG_KEYS, null); > > this.pretty = getBool(JsonGenerator.PRETTY_PRINTING, false); > > > > final int bufferSize = getInt(GENERATOR_BUFFER_LENGTH, > > DEFAULT_GENERATOR_BUFFER_LENGTH); > > if (bufferSize <= 0) { > > throw new IllegalArgumentException("buffer length must be > > greater than zero"); > > } > > this.buffer = new > > Buffer(getBufferProvider().newCharProvider(bufferSize), bufferSize); > > } > > > > @Override > > public JsonGenerator createGenerator(final Writer writer) { > > return new JsonGeneratorImpl(writer, buffer.provider, pretty); > > } > > > > @Override > > public JsonGenerator createGenerator(final OutputStream out) { > > if (MetadataProvider.class.isInstance(out)) { // mainly for > Snippet > > handling since generators don't have properties > > final int bufferSize = > > MetadataProvider.class.cast(out).bufferSize(); > > if (buffer.size != bufferSize) { > > // most of the time it will be a single aside buffer so > > keep its factory to reuse the provider cache > > if (firstCustomBuffer == null) { > > synchronized (this) { > > if (firstCustomBuffer == null) { > > firstCustomBuffer = new > > Buffer(getBufferProvider().newCharProvider(bufferSize), bufferSize); > > } > > } > > } > > Buffer customBuffer = firstCustomBuffer; > > if (customBuffer.size != bufferSize) { // don't use the > > cache, shouldn't occur often > > return new JsonGeneratorImpl(out, > > getBufferProvider().newCharProvider(bufferSize), pretty); > > } > > return new JsonGeneratorImpl(out, customBuffer.provider, > > pretty); > > } // else just reuse the original buffer since it matches and > > leverages its cache > > } > > return new JsonGeneratorImpl(out, buffer.provider, pretty); > > } > > > > @Override > > public JsonGenerator createGenerator(final OutputStream out, final > > Charset charset) { > > return new JsonGeneratorImpl(out,charset, buffer.provider, > pretty); > > } > > > > @Override > > public Map<String, ?> getConfigInUse() { > > return Collections.unmodifiableMap(internalConfig); > > } > > > > private static final class Buffer { > > private final BufferStrategy.BufferProvider<char[]> provider; > > private final int size; > > > > private Buffer(final BufferStrategy.BufferProvider<char[]> > > bufferProvider, final int bufferSize) { > > this.provider = bufferProvider; > > this.size = bufferSize; > > } > > } > > } > > > > 6. you use the outputstream supplier in Snippet and done > > > > Wdyt? > > > > Romain Manni-Bucau > > @rmannibucau <https://twitter.com/rmannibucau> | Blog > > <https://rmannibucau.metawerx.net/> | Old Blog > > <http://rmannibucau.wordpress.com> | Github < > https://github.com/rmannibucau> | > > LinkedIn <https://www.linkedin.com/in/rmannibucau> | Book > > < > https://www.packtpub.com/application-development/java-ee-8-high-performance > > > > > > > > Le mar. 26 avr. 2022 à 07:05, Romain Manni-Bucau <[email protected]> > a > > écrit : > > > >> > >> > >> Le mar. 26 avr. 2022 à 00:44, David Blevins <[email protected]> a > >> écrit : > >> > >>> Since we're now getting the snippet max length via the config, could we > >>> construct the JsonGeneratorImpl directly and pass in a buffer of the > >>> correct size? > >>> > >> > >> From the JsonProvider we can - mapper must not depend on johnzon-core - > at > >> the condition to promote the other properties too AND not do it if user > >> provided a custom generator factory which would just be reused in this > >> case, so not sure it solves the issue. > >> > >> > >>> -David > >>> > >>> > >>>> On Apr 25, 2022, at 2:19 PM, Romain Manni-Bucau < > [email protected]> > >>> wrote: > >>>> > >>>> No worries, got it. > >>>> > >>>> One issue copying the generator is that it will still miss the flush > for > >>>> all small objects and for all other cases the heuristic + a simple > >>>> truncation if needed would work with almost no other downsides than > not > >>>> being exact but goal was to limit the overflow/cpu/mem so all good > IMHO. > >>>> > >>>> Le lun. 25 avr. 2022 à 22:57, David Blevins <[email protected]> > a > >>>> écrit : > >>>> > >>>>>> On Apr 25, 2022, at 10:47 AM, David Blevins < > [email protected]> > >>>>> wrote: > >>>>>> > >>>>>> The trick with that is there is yet another buffer being held > >>> internally > >>>>> by ObjectOutputStream and it recreates the issue. > >>>>> > >>>>> Just read this again -- this was supposed to be "OutputStreamWriter" > >>> not > >>>>> ObjectOutputStream. Too many years of EJB :) > >>>>> > >>>>> > >>>>> -David > >>>>> > >>>>> > >>> > >>> > >
