diff --git a/io_scene_fbx/__init__.py b/io_scene_fbx/__init__.py
index 96c5adf21f1bd2be7dfbcf1b08c11162acf41626..461189c118e7228a82d8ea9b2a461a7778c6fac6 100644
--- a/io_scene_fbx/__init__.py
+++ b/io_scene_fbx/__init__.py
@@ -21,7 +21,7 @@
 bl_info = {
     "name": "FBX format",
     "author": "Campbell Barton, Bastien Montagne, Jens Restemeier",
-    "version": (3, 2, 2),
+    "version": (3, 2, 3),
     "blender": (2, 74, 0),
     "location": "File > Import-Export",
     "description": "FBX IO meshes, UV's, vertex colors, materials, "
diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py
index 00d1f3c3b3326139041438d63a2d531196079b3a..95aebbd80265aeca360a89cf1b2d13c031ff25f1 100644
--- a/io_scene_fbx/export_fbx_bin.py
+++ b/io_scene_fbx/export_fbx_bin.py
@@ -61,7 +61,7 @@ from .fbx_utils import (
     FBX_LIGHT_TYPES, FBX_LIGHT_DECAY_TYPES,
     RIGHT_HAND_AXES, FBX_FRAMERATES,
     # Miscellaneous utils.
-    units_convertor, units_convertor_iter, matrix4_to_array, similar_values, similar_values_iter,
+    PerfMon, units_convertor, units_convertor_iter, matrix4_to_array, similar_values, similar_values_iter,
     # Mesh transform helpers.
     vcos_transformed_gen, nors_transformed_gen,
     # UUID from key.
@@ -2064,9 +2064,13 @@ def fbx_data_from_scene(scene, settings):
     Do some pre-processing over scene's data...
     """
     objtypes = settings.object_types
+    perfmon = PerfMon()
+    perfmon.level_up()
 
     # ##### Gathering data...
 
+    perfmon.step("FBX export prepare: Wrapping Objects...")
+
     # This is rather simple for now, maybe we could end generating templates with most-used values
     # instead of default ones?
     objects = OrderedDict()  # Because we do not have any ordered set...
@@ -2081,6 +2085,8 @@ def fbx_data_from_scene(scene, settings):
             objects[dp_obj] = None
         ob_obj.dupli_list_clear()
 
+    perfmon.step("FBX export prepare: Wrapping Data (lamps, cameras, empties)...")
+
     data_lamps = OrderedDict((ob_obj.bdata.data, get_blenderID_key(ob_obj.bdata.data))
                              for ob_obj in objects if ob_obj.type == 'LAMP')
     # Unfortunately, FBX camera data contains object-level data (like position, orientation, etc.)...
@@ -2090,6 +2096,8 @@ def fbx_data_from_scene(scene, settings):
     data_empties = OrderedDict((ob_obj, get_blender_empty_key(ob_obj.bdata))
                                for ob_obj in objects if ob_obj.type == 'EMPTY')
 
+    perfmon.step("FBX export prepare: Wrapping Meshes...")
+
     data_meshes = OrderedDict()
     for ob_obj in objects:
         if ob_obj.type not in BLENDER_OBJECT_TYPES_MESHLIKE:
@@ -2120,6 +2128,8 @@ def fbx_data_from_scene(scene, settings):
         if use_org_data:
             data_meshes[ob_obj] = (get_blenderID_key(ob.data), ob.data, False)
 
+    perfmon.step("FBX export prepare: Wrapping ShapeKeys...")
+
     # ShapeKeys.
     data_deformers_shape = OrderedDict()
     geom_mat_co = settings.global_matrix if settings.bake_space_transform else None
@@ -2152,6 +2162,8 @@ def fbx_data_from_scene(scene, settings):
             data = (channel_key, geom_key, shape_verts_co, shape_verts_idx)
             data_deformers_shape.setdefault(me, (me_key, shapes_key, OrderedDict()))[2][shape] = data
 
+    perfmon.step("FBX export prepare: Wrapping Armatures...")
+
     # Armatures!
     data_deformers_skin = OrderedDict()
     data_bones = OrderedDict()
@@ -2167,12 +2179,16 @@ def fbx_data_from_scene(scene, settings):
     if settings.add_leaf_bones:
         data_leaf_bones = fbx_generate_leaf_bones(settings, data_bones)
 
+    perfmon.step("FBX export prepare: Wrapping World...")
+
     # Some world settings are embedded in FBX materials...
     if scene.world:
         data_world = OrderedDict(((scene.world, get_blenderID_key(scene.world)),))
     else:
         data_world = OrderedDict()
 
+    perfmon.step("FBX export prepare: Wrapping Materials...")
+
     # TODO: Check all the mat stuff works even when mats are linked to Objects
     #       (we can then have the same mesh used with different materials...).
     #       *Should* work, as FBX always links its materials to Models (i.e. objects).
@@ -2194,6 +2210,8 @@ def fbx_data_from_scene(scene, settings):
             else:
                 data_materials[mat] = (get_blenderID_key(mat), [ob_obj])
 
+    perfmon.step("FBX export prepare: Wrapping Textures...")
+
     # Note FBX textures also hold their mapping info.
     # TODO: Support layers?
     data_textures = OrderedDict()
@@ -2231,6 +2249,8 @@ def fbx_data_from_scene(scene, settings):
             else:
                 data_videos[img] = (get_blenderID_key(img), [tex])
 
+    perfmon.step("FBX export prepare: Wrapping Animations...")
+
     # Animation...
     animations = ()
     frame_start = scene.frame_start
@@ -2249,6 +2269,8 @@ def fbx_data_from_scene(scene, settings):
 
     # ##### Creation of templates...
 
+    perfmon.step("FBX export prepare: Generating templates...")
+
     templates = OrderedDict()
     templates[b"GlobalSettings"] = fbx_template_def_globalsettings(scene, settings, nbr_users=1)
 
@@ -2326,6 +2348,8 @@ def fbx_data_from_scene(scene, settings):
 
     # ##### Creation of connections...
 
+    perfmon.step("FBX export prepare: Generating Connections...")
+
     connections = []
 
     # Objects (with classical parenting).
@@ -2448,6 +2472,8 @@ def fbx_data_from_scene(scene, settings):
                         # Animcurve -> Animcurvenode.
                         connections.append((b"OP", get_fbx_uuid_from_key(acurve_key), acurvenode_id, fbx_item.encode()))
 
+    perfmon.level_down()
+
     # ##### And pack all this!
 
     return FBXExportData(
@@ -2636,22 +2662,34 @@ def fbx_objects_elements(root, scene_data):
     """
     Data (objects, geometry, material, textures, armatures, etc.).
     """
+    perfmon = PerfMon()
+    perfmon.level_up()
     objects = elem_empty(root, b"Objects")
 
+    perfmon.step("FBX export fetch empties (%d)..." % len(scene_data.data_empties))
+
     for empty in scene_data.data_empties:
         fbx_data_empty_elements(objects, empty, scene_data)
 
+    perfmon.step("FBX export fetch lamps (%d)..." % len(scene_data.data_lamps))
+
     for lamp in scene_data.data_lamps:
         fbx_data_lamp_elements(objects, lamp, scene_data)
 
+    perfmon.step("FBX export fetch cameras (%d)..." % len(scene_data.data_cameras))
+
     for cam in scene_data.data_cameras:
         fbx_data_camera_elements(objects, cam, scene_data)
 
+    perfmon.step("FBX export fetch meshes (%d)..." % len(scene_data.data_meshes))
+
     done_meshes = set()
     for me_obj in scene_data.data_meshes:
         fbx_data_mesh_elements(objects, me_obj, scene_data, done_meshes)
     del done_meshes
 
+    perfmon.step("FBX export fetch objects (%d)..." % len(scene_data.objects))
+
     for ob_obj in scene_data.objects:
         if ob_obj.is_dupli:
             continue
@@ -2663,6 +2701,8 @@ def fbx_objects_elements(root, scene_data):
             fbx_data_object_elements(objects, dp_obj, scene_data)
         ob_obj.dupli_list_clear()
 
+    perfmon.step("FBX export fetch remaining...")
+
     for ob_obj in scene_data.objects:
         if not (ob_obj.is_object and ob_obj.type == 'ARMATURE'):
             continue
@@ -2680,8 +2720,13 @@ def fbx_objects_elements(root, scene_data):
     for vid in scene_data.data_videos:
         fbx_data_video_elements(objects, vid, scene_data)
 
+    perfmon.step("FBX export fetch animations...")
+    start_time = time.process_time()
+
     fbx_data_animation_elements(objects, scene_data)
 
+    perfmon.level_down()
+
 
 def fbx_connections_elements(root, scene_data):
     """
diff --git a/io_scene_fbx/fbx_utils.py b/io_scene_fbx/fbx_utils.py
index 07e4d824eaa5181946ce17b550c19e18df1e0c52..65ab24703404f2ed91d92c938d6683d695f53d5a 100644
--- a/io_scene_fbx/fbx_utils.py
+++ b/io_scene_fbx/fbx_utils.py
@@ -22,6 +22,7 @@
 
 
 import math
+import time
 
 from collections import namedtuple, OrderedDict
 from collections.abc import Iterable
@@ -147,6 +148,56 @@ FBX_FRAMERATES = (
 
 # ##### Misc utilities #####
 
+DO_PERFMON = True
+
+if DO_PERFMON:
+    class PerfMon():
+        def __init__(self):
+            self.level = -1
+            self.ref_time = []
+
+        def level_up(self, message=""):
+            self.level += 1
+            self.ref_time.append(None)
+            if message:
+                print("\t" * self.level, message, sep="")
+
+        def level_down(self, message=""):
+            if not self.ref_time:
+                if message:
+                    print(message)
+                return
+            ref_time = self.ref_time[self.level]
+            print("\t" * self.level,
+                  "\tDone (%f sec)\n" % ((time.process_time() - ref_time) if ref_time is not None else 0.0),
+                  sep="")
+            if message:
+                print("\t" * self.level, message, sep="")
+            del self.ref_time[self.level]
+            self.level -= 1
+
+        def step(self, message=""):
+            ref_time = self.ref_time[self.level]
+            curr_time = time.process_time()
+            if ref_time is not None:
+                print("\t" * self.level, "\tDone (%f sec)\n" % (curr_time - ref_time), sep="")
+            self.ref_time[self.level] = curr_time
+            print("\t" * self.level, message, sep="")
+else:
+    class PerfMon():
+        def __init__(self):
+            pass
+
+        def level_up(self, message=""):
+            pass
+
+        def level_down(self, message=""):
+            pass
+
+        def step(self, message=""):
+            pass
+
+
 # Note: this could be in a utility (math.units e.g.)...
 
 UNITS = {
diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py
index 10a17dd3096a7c338fc271c3c431df2470fc629a..33950f412d712bdc6029701ce3bc65752288d50c 100644
--- a/io_scene_fbx/import_fbx.py
+++ b/io_scene_fbx/import_fbx.py
@@ -41,6 +41,7 @@ from . import parse_fbx, fbx_utils
 
 from .parse_fbx import data_types, FBXElem
 from .fbx_utils import (
+    PerfMon,
     units_convertor_iter,
     array_to_matrix4,
     similar_values,
@@ -2091,6 +2092,11 @@ def load(operator, context, filepath="",
     start_time_proc = time.process_time()
     start_time_sys = time.time()
 
+    perfmon = PerfMon()
+    perfmon.level_up()
+    perfmon.step("FBX Import: start importing %s" % filepath)
+    perfmon.level_up()
+
     # detect ascii files
     if is_ascii(filepath, 24):
         operator.report({'ERROR'}, "ASCII FBX files are not supported %r" % filepath)
@@ -2135,6 +2141,8 @@ def load(operator, context, filepath="",
 
     # #### Get some info from GlobalSettings.
 
+    perfmon.step("FBX import: Prepare...")
+
     fbx_settings = elem_find_first(elem_root, b'GlobalSettings')
     fbx_settings_props = elem_find_first(fbx_settings, b'Properties70')
     if fbx_settings is None or fbx_settings_props is None:
@@ -2194,6 +2202,8 @@ def load(operator, context, filepath="",
 
     # #### And now, the "real" data.
 
+    perfmon.step("FBX import: Templates...")
+
     fbx_defs = elem_find_first(elem_root, b'Definitions')  # can be None
     fbx_nodes = elem_find_first(elem_root, b'Objects')
     fbx_connections = elem_find_first(elem_root, b'Connections')
@@ -2234,6 +2244,8 @@ def load(operator, context, filepath="",
             return fbx_templates.get(key, fbx_elem_nil)
         return ret
 
+    perfmon.step("FBX import: Nodes...")
+
     # ----
     # Build FBX node-table
     def _():
@@ -2249,6 +2261,8 @@ def load(operator, context, filepath="",
     # http://download.autodesk.com/us/fbx/20112/FBX_SDK_HELP/index.html?url=
     #        WS73099cc142f487551fea285e1221e4f9ff8-7fda.htm,topicNumber=d0e6388
 
+    perfmon.step("FBX import: Connections...")
+
     fbx_connection_map = {}
     fbx_connection_map_reverse = {}
 
@@ -2261,6 +2275,8 @@ def load(operator, context, filepath="",
                 fbx_connection_map_reverse.setdefault(c_dst, []).append((c_src, fbx_link))
     _(); del _
 
+    perfmon.step("FBX import: Meshes...")
+
     # ----
     # Load mesh data
     def _():
@@ -2275,6 +2291,8 @@ def load(operator, context, filepath="",
                 fbx_item[1] = blen_read_geom(fbx_tmpl, fbx_obj, settings)
     _(); del _
 
+    perfmon.step("FBX import: Materials & Textures...")
+
     # ----
     # Load material data
     def _():
@@ -2310,6 +2328,8 @@ def load(operator, context, filepath="",
             fbx_item[1] = blen_read_texture_image(fbx_tmpl_tex, fbx_obj, basedir, settings)
     _(); del _
 
+    perfmon.step("FBX import: Cameras & Lamps...")
+
     # ----
     # Load camera data
     def _():
@@ -2353,6 +2373,8 @@ def load(operator, context, filepath="",
     def connection_filter_reverse(fbx_uuid, fbx_id):
         return connection_filter_ex(fbx_uuid, fbx_id, fbx_connection_map_reverse)
 
+    perfmon.step("FBX import: Objects & Armatures...")
+
     # -- temporary helper hierarchy to build armatures and objects from
     # lookup from uuid to helper node. Used to build parent-child relations and later to look up animated nodes.
     fbx_helper_nodes = {}
@@ -2517,6 +2539,8 @@ def load(operator, context, filepath="",
         # root_helper.print_info(0)
     _(); del _
 
+    perfmon.step("FBX import: ShapeKeys...")
+
     # We can handle shapes.
     blend_shape_channels = {}  # We do not need Shapes themselves, but keyblocks, for anim.
 
@@ -2567,6 +2591,8 @@ def load(operator, context, filepath="",
                 blend_shape_channels[bc_uuid] = keyblocks
     _(); del _
 
+    perfmon.step("FBX import: Animations...")
+
     # Animation!
     def _():
         fbx_tmpl_astack = fbx_template_get((b'AnimationStack', b'FbxAnimStack'))
@@ -2658,6 +2684,8 @@ def load(operator, context, filepath="",
 
     _(); del _
 
+    perfmon.step("FBX import: Assign materials...")
+
     def _():
         # link Material's to Geometry (via Model's)
         for fbx_uuid, fbx_item in fbx_table_nodes.items():
@@ -2671,17 +2699,19 @@ def load(operator, context, filepath="",
             if mesh is None:
                 continue
 
-            for (fbx_lnk,
-                 fbx_lnk_item,
-                 fbx_lnk_type) in connection_filter_forward(fbx_uuid, b'Model'):
+            # In Blender, we link materials to data, typically (meshes), while in FBX they are linked to objects...
+            # So we have to be careful not to re-add endlessly the same material to a mesh!
+            # This can easily happen with 'baked' dupliobjects, see T44386.
+            # TODO: add an option to link materials to objects in Blender instead?
+            done_mats = set()
 
+            for (fbx_lnk, fbx_lnk_item, fbx_lnk_type) in connection_filter_forward(fbx_uuid, b'Model'):
                 # link materials
                 fbx_lnk_uuid = elem_uuid(fbx_lnk)
-                for (fbx_lnk_material,
-                     material,
-                     fbx_lnk_material_type) in connection_filter_reverse(fbx_lnk_uuid, b'Material'):
-
-                    mesh.materials.append(material)
+                for (fbx_lnk_material, material, fbx_lnk_material_type) in connection_filter_reverse(fbx_lnk_uuid, b'Material'):
+                    if material not in done_mats:
+                        mesh.materials.append(material)
+                        done_mats.add(material)
 
             # We have to validate mesh polygons' mat_idx, see T41015!
             # Some FBX seem to have an extra 'default' material which is not defined in FBX file.
@@ -2689,6 +2719,8 @@ def load(operator, context, filepath="",
                 print("WARNING: mesh '%s' had invalid material indices, those were reset to first material" % mesh.name)
     _(); del _
 
+    perfmon.step("FBX import: Assign textures...")
+
     def _():
         material_images = {}
 
@@ -2898,6 +2930,8 @@ def load(operator, context, filepath="",
 
     _(); del _
 
+    perfmon.step("FBX import: Cycles z-offset workaround...")
+
     def _():
         # Annoying workaround for cycles having no z-offset
         if material_decals and use_alpha_decals:
@@ -2925,6 +2959,7 @@ def load(operator, context, filepath="",
                                 material.use_raytrace = False
     _(); del _
 
-    print('Import finished in %.4f sec (process time: %.4f sec).' %
-          (time.time() - start_time_sys, time.process_time() - start_time_proc))
+    perfmon.level_down()
+
+    perfmon.level_down("Import finished.")
     return {'FINISHED'}