You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by do...@apache.org on 2009/11/29 22:32:36 UTC

svn commit: r885277 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Author: doogie
Date: Sun Nov 29 21:32:36 2009
New Revision: 885277

URL: http://svn.apache.org/viewvc?rev=885277&view=rev
Log:
Make testOperations manipulation non-blocking, using concurrent programming.

Modified:
    ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Modified: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java?rev=885277&r1=885276&r2=885277&view=diff
==============================================================================
--- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java (original)
+++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java Sun Nov 29 21:32:36 2009
@@ -29,6 +29,8 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeSet;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 
 import javax.xml.parsers.ParserConfigurationException;
 
@@ -116,7 +118,8 @@
 
     private boolean testMode = false;
     private boolean testRollbackInProgress = false;
-    private List<TestOperation> testOperations = null;
+    private static final AtomicReferenceFieldUpdater<GenericDelegator, LinkedBlockingDeque> testOperationsUpdater = AtomicReferenceFieldUpdater.newUpdater(GenericDelegator.class, LinkedBlockingDeque.class, "testOperations");
+    private volatile LinkedBlockingDeque<TestOperation> testOperations = null;
     private enum OperationType {INSERT, UPDATE, DELETE};
 
     private String originalDelegatorName = null;
@@ -3169,7 +3172,7 @@
         newDelegator.crypto = this.crypto;
         // In case this delegator is in testMode give it a reference to
         // the rollback list
-        newDelegator.testOperations = this.testOperations;
+        testOperationsUpdater.set(newDelegator, this.testOperations);
         // not setting the sequencer so that we have unique sequences.
 
         return newDelegator;
@@ -3195,7 +3198,7 @@
     private void setTestMode(boolean testMode) {
         this.testMode = testMode;
         if (testMode) {
-            this.testOperations = FastList.newInstance();
+            testOperationsUpdater.set(this, new LinkedBlockingDeque());
         } else {
             this.testOperations.clear();
         }
@@ -3217,25 +3220,23 @@
         }
         this.testMode = false;
         this.testRollbackInProgress = true;
-        synchronized (testOperations) {
-            Debug.logInfo("Rolling back " + testOperations.size() + " entity operations", module);
-            ListIterator<TestOperation> iterator = this.testOperations.listIterator(this.testOperations.size());
-            while (iterator.hasPrevious()) {
-                TestOperation testOperation = iterator.previous();
-                try {
-                    if (testOperation.getOperation().equals(OperationType.INSERT)) {
-                        this.removeValue(testOperation.getValue());
-                    } else if (testOperation.getOperation().equals(OperationType.UPDATE)) {
-                        this.store(testOperation.getValue());
-                    } else if (testOperation.getOperation().equals(OperationType.DELETE)) {
-                        this.create(testOperation.getValue());
-                    }
-                } catch (GenericEntityException e) {
-                    Debug.logWarning(e.toString(), module);
+        Debug.logInfo("Rolling back " + testOperations.size() + " entity operations", module);
+        while (!this.testOperations.isEmpty()) {
+            TestOperation testOperation = this.testOperations.pollLast();
+            if (testOperation == null) break;
+            try {
+                if (testOperation.getOperation().equals(OperationType.INSERT)) {
+                    this.removeValue(testOperation.getValue());
+                } else if (testOperation.getOperation().equals(OperationType.UPDATE)) {
+                    this.store(testOperation.getValue());
+                } else if (testOperation.getOperation().equals(OperationType.DELETE)) {
+                    this.create(testOperation.getValue());
                 }
+            } catch (GenericEntityException e) {
+                Debug.logWarning(e.toString(), module);
             }
-            this.testOperations.clear();
         }
+        this.testOperations.clear();
         this.testRollbackInProgress = false;
         this.testMode = true;
     }



Re: svn commit: r885277 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Posted by Adrian Crum <ad...@hlmksw.com>.
Adam Heath wrote:
> Adrian Crum wrote:
>> Adam Heath wrote:
>>> Adrian Crum wrote:
>>>> The java.util.concurrent package rocks! I used it a few weeks ago to
>>>> multi-thread the demo data loading code. I got it down from 3 minutes to
>>>> 1.5 minutes.
>>> What?  You made the ofbiz demo data loading code multi-threaded?
>>> Seriously?  If so, that rocks!
>> I used a thread pool to create tables and non-fk indexes. By fine tuning
>> the thread count, I was able to take the single-threaded CPU usage from
>> 12-20% up to 50-90%. I used a FIFO queue for loading data - the main
>> thread parses the XML files and places DOM Elements in the queue, and
>> another thread takes the elements from the queue and stores them in the
>> database.
>>
>> Some day I'll clean up the code and provide a patch. It only benefits
>> multi-CPU computers.
> 
> I would do this in multiple stages.
> 
> First stage would be a generic xml parsing service.  Each xml file is
> handed off to an ExecutorService.  The Callable.call() method would
> then parse the file, and the return would be a Document.
> 
> The second phase would then use the same ExecutorService, and convert
> each Document to a List<GenericValue>.  As an optimization, the first
> phase would auto-submit the document back to the same executor.
> 
> Third phase would then import files in parallel, but not the separate
> values.  You'd have to handle dependency issues, similiar to the
> looping that is currently done.  However, the correct fix for these
> kinds of problems would be to reorder the data in the files.

I'm sure all kinds of optimizations could be tried. Once you have the 
basic multi-threading working, tweaking it becomes addictive.

My stab at it served two purposes - I had just read the Sun tutorial on 
the concurrent package and I wanted to try it out, and I really needed 
to reduce the demo data load time because I was using the process to 
test the converter integration in the entity engine.

-Adrian


Re: svn commit: r885277 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Posted by Adam Heath <do...@brainfood.com>.
Adrian Crum wrote:
> Adam Heath wrote:
>> Adrian Crum wrote:
>>> The java.util.concurrent package rocks! I used it a few weeks ago to
>>> multi-thread the demo data loading code. I got it down from 3 minutes to
>>> 1.5 minutes.
>>
>> What?  You made the ofbiz demo data loading code multi-threaded?
>> Seriously?  If so, that rocks!
> 
> I used a thread pool to create tables and non-fk indexes. By fine tuning
> the thread count, I was able to take the single-threaded CPU usage from
> 12-20% up to 50-90%. I used a FIFO queue for loading data - the main
> thread parses the XML files and places DOM Elements in the queue, and
> another thread takes the elements from the queue and stores them in the
> database.
> 
> Some day I'll clean up the code and provide a patch. It only benefits
> multi-CPU computers.

I would do this in multiple stages.

First stage would be a generic xml parsing service.  Each xml file is
handed off to an ExecutorService.  The Callable.call() method would
then parse the file, and the return would be a Document.

The second phase would then use the same ExecutorService, and convert
each Document to a List<GenericValue>.  As an optimization, the first
phase would auto-submit the document back to the same executor.

Third phase would then import files in parallel, but not the separate
values.  You'd have to handle dependency issues, similiar to the
looping that is currently done.  However, the correct fix for these
kinds of problems would be to reorder the data in the files.



Re: svn commit: r885277 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Posted by Jacques Le Roux <ja...@les7arts.com>.
Thanks Adrian,

Now I see the issue

Jacques

From: "Adrian Crum" <ad...@hlmksw.com>
> Jacques Le Roux wrote:
>> From: "Adrian Crum" <ad...@hlmksw.com>
>>> Adam Heath wrote:
>>>> Adrian Crum wrote:
>>>>> Adam Heath wrote:
>>>>>> Adrian Crum wrote:
>>>>>>> The java.util.concurrent package rocks! I used it a few weeks ago to
>>>>>>> multi-thread the demo data loading code. I got it down from 3 
>>>>>>> minutes to
>>>>>>> 1.5 minutes.
>>>>>> What?  You made the ofbiz demo data loading code multi-threaded?
>>>>>> Seriously?  If so, that rocks!
>>>>> I used a thread pool to create tables and non-fk indexes. By fine 
>>>>> tuning
>>>>> the thread count, I was able to take the single-threaded CPU usage from
>>>>> 12-20% up to 50-90%. I used a FIFO queue for loading data - the main
>>>>> thread parses the XML files and places DOM Elements in the queue, and
>>>>> another thread takes the elements from the queue and stores them in the
>>>>> database.
>>>>>
>>>>> Some day I'll clean up the code and provide a patch. It only benefits
>>>>> multi-CPU computers.
>>>>
>>>> import java.lang.management.ManagementFactory;
>>>>
>>>> int workerCount =
>>>> ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
>>>
>>> The patch would be for educational purposes. If the community ever 
>>> decided to introduce multi-threading to the framework, it would have 
>>> to be controlled by configuration settings - because not all 
>>> installations would benefit from it.
>> 
>> But would it impair them ?
> 
> Sun warns that multi-threading in some situations actually slows things 
> down. I discovered that by using the patch on a single CPU system. The 
> settings that doubled throughput on my multi-CPU machine halved the 
> throughput on the singe CPU machine. Even after tweaking the settings, I 
> wasn't able to gain much on that platform.
> 
> -Adrian
>


Re: svn commit: r885277 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Posted by Adrian Crum <ad...@hlmksw.com>.
Jacques Le Roux wrote:
> From: "Adrian Crum" <ad...@hlmksw.com>
>> Adam Heath wrote:
>>> Adrian Crum wrote:
>>>> Adam Heath wrote:
>>>>> Adrian Crum wrote:
>>>>>> The java.util.concurrent package rocks! I used it a few weeks ago to
>>>>>> multi-thread the demo data loading code. I got it down from 3 
>>>>>> minutes to
>>>>>> 1.5 minutes.
>>>>> What?  You made the ofbiz demo data loading code multi-threaded?
>>>>> Seriously?  If so, that rocks!
>>>> I used a thread pool to create tables and non-fk indexes. By fine 
>>>> tuning
>>>> the thread count, I was able to take the single-threaded CPU usage from
>>>> 12-20% up to 50-90%. I used a FIFO queue for loading data - the main
>>>> thread parses the XML files and places DOM Elements in the queue, and
>>>> another thread takes the elements from the queue and stores them in the
>>>> database.
>>>>
>>>> Some day I'll clean up the code and provide a patch. It only benefits
>>>> multi-CPU computers.
>>>
>>> import java.lang.management.ManagementFactory;
>>>
>>> int workerCount =
>>> ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
>>
>> The patch would be for educational purposes. If the community ever 
>> decided to introduce multi-threading to the framework, it would have 
>> to be controlled by configuration settings - because not all 
>> installations would benefit from it.
> 
> But would it impair them ?

Sun warns that multi-threading in some situations actually slows things 
down. I discovered that by using the patch on a single CPU system. The 
settings that doubled throughput on my multi-CPU machine halved the 
throughput on the singe CPU machine. Even after tweaking the settings, I 
wasn't able to gain much on that platform.

-Adrian

Re: svn commit: r885277 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Posted by Jacques Le Roux <ja...@les7arts.com>.
From: "Adrian Crum" <ad...@hlmksw.com>
> Adam Heath wrote:
>> Adrian Crum wrote:
>>> Adam Heath wrote:
>>>> Adrian Crum wrote:
>>>>> The java.util.concurrent package rocks! I used it a few weeks ago to
>>>>> multi-thread the demo data loading code. I got it down from 3 minutes to
>>>>> 1.5 minutes.
>>>> What?  You made the ofbiz demo data loading code multi-threaded?
>>>> Seriously?  If so, that rocks!
>>> I used a thread pool to create tables and non-fk indexes. By fine tuning
>>> the thread count, I was able to take the single-threaded CPU usage from
>>> 12-20% up to 50-90%. I used a FIFO queue for loading data - the main
>>> thread parses the XML files and places DOM Elements in the queue, and
>>> another thread takes the elements from the queue and stores them in the
>>> database.
>>>
>>> Some day I'll clean up the code and provide a patch. It only benefits
>>> multi-CPU computers.
>> 
>> import java.lang.management.ManagementFactory;
>> 
>> int workerCount =
>> ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
> 
> The patch would be for educational purposes. If the community ever 
> decided to introduce multi-threading to the framework, it would have to 
> be controlled by configuration settings - because not all installations 
> would benefit from it.

But would it impair them ?

Jacques
 
> 
> -Adrian
>


Re: svn commit: r885277 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Posted by Adrian Crum <ad...@hlmksw.com>.
Adam Heath wrote:
> Adrian Crum wrote:
>> Adam Heath wrote:
>>> Adrian Crum wrote:
>>>> The java.util.concurrent package rocks! I used it a few weeks ago to
>>>> multi-thread the demo data loading code. I got it down from 3 minutes to
>>>> 1.5 minutes.
>>> What?  You made the ofbiz demo data loading code multi-threaded?
>>> Seriously?  If so, that rocks!
>> I used a thread pool to create tables and non-fk indexes. By fine tuning
>> the thread count, I was able to take the single-threaded CPU usage from
>> 12-20% up to 50-90%. I used a FIFO queue for loading data - the main
>> thread parses the XML files and places DOM Elements in the queue, and
>> another thread takes the elements from the queue and stores them in the
>> database.
>>
>> Some day I'll clean up the code and provide a patch. It only benefits
>> multi-CPU computers.
> 
> import java.lang.management.ManagementFactory;
> 
> int workerCount =
> ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();

The patch would be for educational purposes. If the community ever 
decided to introduce multi-threading to the framework, it would have to 
be controlled by configuration settings - because not all installations 
would benefit from it.


-Adrian

Re: svn commit: r885277 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Posted by Adam Heath <do...@brainfood.com>.
Adrian Crum wrote:
> Adam Heath wrote:
>> Adrian Crum wrote:
>>> The java.util.concurrent package rocks! I used it a few weeks ago to
>>> multi-thread the demo data loading code. I got it down from 3 minutes to
>>> 1.5 minutes.
>>
>> What?  You made the ofbiz demo data loading code multi-threaded?
>> Seriously?  If so, that rocks!
> 
> I used a thread pool to create tables and non-fk indexes. By fine tuning
> the thread count, I was able to take the single-threaded CPU usage from
> 12-20% up to 50-90%. I used a FIFO queue for loading data - the main
> thread parses the XML files and places DOM Elements in the queue, and
> another thread takes the elements from the queue and stores them in the
> database.
> 
> Some day I'll clean up the code and provide a patch. It only benefits
> multi-CPU computers.

import java.lang.management.ManagementFactory;

int workerCount =
ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();


Re: svn commit: r885277 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Posted by Jacques Le Roux <ja...@les7arts.com>.
From: "Adrian Crum" <ad...@hlmksw.com>
> Adam Heath wrote:
>> Adrian Crum wrote:
>>> The java.util.concurrent package rocks! I used it a few weeks ago to
>>> multi-thread the demo data loading code. I got it down from 3 minutes to
>>> 1.5 minutes.
>> 
>> What?  You made the ofbiz demo data loading code multi-threaded?
>> Seriously?  If so, that rocks!
> 
> I used a thread pool to create tables and non-fk indexes. By fine tuning 
> the thread count, I was able to take the single-threaded CPU usage from 
> 12-20% up to 50-90%. I used a FIFO queue for loading data - the main 
> thread parses the XML files and places DOM Elements in the queue, and 
> another thread takes the elements from the queue and stores them in the 
> database.
> 
> Some day I'll clean up the code and provide a patch. It only benefits 
> multi-CPU computers.

Interesting, there are more and more multi-CPU computers!

Jacques
 
> -Adrian
> 
>


Re: svn commit: r885277 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Posted by Adrian Crum <ad...@hlmksw.com>.
Adam Heath wrote:
> Adrian Crum wrote:
>> The java.util.concurrent package rocks! I used it a few weeks ago to
>> multi-thread the demo data loading code. I got it down from 3 minutes to
>> 1.5 minutes.
> 
> What?  You made the ofbiz demo data loading code multi-threaded?
> Seriously?  If so, that rocks!

I used a thread pool to create tables and non-fk indexes. By fine tuning 
the thread count, I was able to take the single-threaded CPU usage from 
12-20% up to 50-90%. I used a FIFO queue for loading data - the main 
thread parses the XML files and places DOM Elements in the queue, and 
another thread takes the elements from the queue and stores them in the 
database.

Some day I'll clean up the code and provide a patch. It only benefits 
multi-CPU computers.

-Adrian



Re: svn commit: r885277 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Posted by Adam Heath <do...@brainfood.com>.
Adrian Crum wrote:
> The java.util.concurrent package rocks! I used it a few weeks ago to
> multi-thread the demo data loading code. I got it down from 3 minutes to
> 1.5 minutes.

What?  You made the ofbiz demo data loading code multi-threaded?
Seriously?  If so, that rocks!

Re: svn commit: r885277 - /ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java

Posted by Adrian Crum <ad...@hlmksw.com>.
The java.util.concurrent package rocks! I used it a few weeks ago to 
multi-thread the demo data loading code. I got it down from 3 minutes to 
1.5 minutes.

-Adrian

doogie@apache.org wrote:
> Author: doogie
> Date: Sun Nov 29 21:32:36 2009
> New Revision: 885277
> 
> URL: http://svn.apache.org/viewvc?rev=885277&view=rev
> Log:
> Make testOperations manipulation non-blocking, using concurrent programming.
> 
> Modified:
>     ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java
> 
> Modified: ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java
> URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java?rev=885277&r1=885276&r2=885277&view=diff
> ==============================================================================
> --- ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java (original)
> +++ ofbiz/trunk/framework/entity/src/org/ofbiz/entity/GenericDelegator.java Sun Nov 29 21:32:36 2009
> @@ -29,6 +29,8 @@
>  import java.util.Map;
>  import java.util.Set;
>  import java.util.TreeSet;
> +import java.util.concurrent.LinkedBlockingDeque;
> +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
>  
>  import javax.xml.parsers.ParserConfigurationException;
>  
> @@ -116,7 +118,8 @@
>  
>      private boolean testMode = false;
>      private boolean testRollbackInProgress = false;
> -    private List<TestOperation> testOperations = null;
> +    private static final AtomicReferenceFieldUpdater<GenericDelegator, LinkedBlockingDeque> testOperationsUpdater = AtomicReferenceFieldUpdater.newUpdater(GenericDelegator.class, LinkedBlockingDeque.class, "testOperations");
> +    private volatile LinkedBlockingDeque<TestOperation> testOperations = null;
>      private enum OperationType {INSERT, UPDATE, DELETE};
>  
>      private String originalDelegatorName = null;
> @@ -3169,7 +3172,7 @@
>          newDelegator.crypto = this.crypto;
>          // In case this delegator is in testMode give it a reference to
>          // the rollback list
> -        newDelegator.testOperations = this.testOperations;
> +        testOperationsUpdater.set(newDelegator, this.testOperations);
>          // not setting the sequencer so that we have unique sequences.
>  
>          return newDelegator;
> @@ -3195,7 +3198,7 @@
>      private void setTestMode(boolean testMode) {
>          this.testMode = testMode;
>          if (testMode) {
> -            this.testOperations = FastList.newInstance();
> +            testOperationsUpdater.set(this, new LinkedBlockingDeque());
>          } else {
>              this.testOperations.clear();
>          }
> @@ -3217,25 +3220,23 @@
>          }
>          this.testMode = false;
>          this.testRollbackInProgress = true;
> -        synchronized (testOperations) {
> -            Debug.logInfo("Rolling back " + testOperations.size() + " entity operations", module);
> -            ListIterator<TestOperation> iterator = this.testOperations.listIterator(this.testOperations.size());
> -            while (iterator.hasPrevious()) {
> -                TestOperation testOperation = iterator.previous();
> -                try {
> -                    if (testOperation.getOperation().equals(OperationType.INSERT)) {
> -                        this.removeValue(testOperation.getValue());
> -                    } else if (testOperation.getOperation().equals(OperationType.UPDATE)) {
> -                        this.store(testOperation.getValue());
> -                    } else if (testOperation.getOperation().equals(OperationType.DELETE)) {
> -                        this.create(testOperation.getValue());
> -                    }
> -                } catch (GenericEntityException e) {
> -                    Debug.logWarning(e.toString(), module);
> +        Debug.logInfo("Rolling back " + testOperations.size() + " entity operations", module);
> +        while (!this.testOperations.isEmpty()) {
> +            TestOperation testOperation = this.testOperations.pollLast();
> +            if (testOperation == null) break;
> +            try {
> +                if (testOperation.getOperation().equals(OperationType.INSERT)) {
> +                    this.removeValue(testOperation.getValue());
> +                } else if (testOperation.getOperation().equals(OperationType.UPDATE)) {
> +                    this.store(testOperation.getValue());
> +                } else if (testOperation.getOperation().equals(OperationType.DELETE)) {
> +                    this.create(testOperation.getValue());
>                  }
> +            } catch (GenericEntityException e) {
> +                Debug.logWarning(e.toString(), module);
>              }
> -            this.testOperations.clear();
>          }
> +        this.testOperations.clear();
>          this.testRollbackInProgress = false;
>          this.testMode = true;
>      }
> 
> 
>